firebase completely disconnected, working at restoring all functionality

This commit is contained in:
lukew3 2021-05-14 13:56:08 -04:00
parent 2b869a9478
commit 3978b7a106
30 changed files with 4192 additions and 492 deletions

View file

@ -1,5 +0,0 @@
{
"projects": {
"default": "your-firebase-project-id"
}
}

View file

@ -1,24 +0,0 @@
{
"hosting": {
"public": "dist",
"ignore": ["firebase.json", "**/.*", "**/node_modules/**"],
"rewrites": [
{
"source": "/privacy-policy",
"destination": "/privacy-policy.html"
},
{
"source": "**",
"destination": "/index.html"
}
],
"cleanUrls": true,
"trailingSlash": false
}
// },
// "functions": {
// "predeploy": [
// "npm --prefix \"$RESOURCE_DIR\" run lint"
// ]
// }
}

View file

@ -1,5 +1,7 @@
const { task, src, dest, series, watch } = require("gulp");
const browserify = require("browserify");
const axios = require("axios");
const Cookies = require("js-cookie");
const babelify = require("babelify");
const concat = require("gulp-concat");
const del = require("del");
@ -15,7 +17,8 @@ let eslintConfig = {
globals: [
"jQuery",
"$",
"firebase",
"axios",
"Cookies",
"moment",
"html2canvas",
"ClipboardItem",

65
model.js Normal file
View file

@ -0,0 +1,65 @@
/*
//This is the mongo model for a user
{
"uid": 'uid',
"presets": [
//unknown whats inside here
//can be added once one is added, and not created as blank
],
"name",
"discordId",
"pairingCode",
"discordPairingCode",
"config",
"favouriteThemes",
"refactored",
"globalStats": {
"timeTests",
"startedTests",
"completedTests"
},
"banned",
"verified",
"emailVerified",
"lbMemory": {
"time15",
"time60"
}
snap.name = data.name;
snap.discordId = data.discordId;
snap.pairingCode =
data.discordPairingCode == null ? undefined : data.discordPairingCode;
snap.config = data.config;
snap.favouriteThemes =
data.favouriteThemes === undefined ? [] : data.favouriteThemes;
snap.refactored = data.refactored === true ? true : false;
snap.globalStats = {
time: data.timeTyping,
started: data.startedTests,
completed: data.completedTests,
};
snap.banned = data.banned;
snap.verified = data.verified;
snap.emailVerified = user.emailVerified;
try {
if (data.lbMemory.time15 !== undefined) {
snap.lbMemory.time15 = data.lbMemory.time15;
}
if (data.lbMemory.time60 !== undefined) {
snap.lbMemory.time60 = data.lbMemory.time60;
}
} catch {}
}
//The user object returned by firebase.auth().currentUser
// should this be retrieved from the server everytime or is it stored in the browser
// probably browser
{
"uid",
"name",
"emailVerified",
"displayName",
"refreshToken",
//guessing there is an access token in there somewhere, but I really don't know how this works
}
*/

17
models.js Normal file
View file

@ -0,0 +1,17 @@
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const userSchema = new Schema(
{
name: String,
email: String,
password: String,
emailVerified: Boolean,
},
{ timestamps: true }
);
const User = mongoose.model("User", userSchema);
module.exports = { User };
//export User;

3665
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -6,6 +6,7 @@
"scripts": {
"postinstall": "cd functions && npm install",
"build": "npx gulp build",
"start:dev2": "npm run build && concurrently --kill-others \"npx gulp watch\" \"nodemon ./server.js\"",
"start:dev": "npm run build && concurrently --kill-others \"npx gulp watch\" \"firebase serve\"",
"deploy:live:hosting": "npm run build && firebase deploy -P monkey-type --only hosting",
"deploy:live:functions": "npm run build && firebase deploy -P monkey-type --only functions",
@ -30,6 +31,7 @@
"gulp-eslint": "^6.0.0",
"gulp-sass": "^4.1.0",
"husky": "^4.3.0",
"nodemon": "^2.0.7",
"prettier": "2.1.2",
"pretty-quick": "^3.1.0",
"vinyl-buffer": "^1.0.1",
@ -43,9 +45,17 @@
},
"dependencies": {
"@babel/runtime": "^7.12.5",
"axios": "^0.21.1",
"bcrypt": "^5.0.1",
"chart.js": "^2.9.4",
"chartjs-plugin-annotation": "^0.5.7",
"chartjs-plugin-trendline": "^0.2.2",
"dotenv": "^9.0.2",
"express": "^4.17.1",
"js-cookie": "^2.2.1",
"jsonwebtoken": "^8.5.1",
"mongodb": "^3.6.6",
"mongoose": "^5.12.8",
"tinycolor2": "^1.4.2"
}
}

242
server.js Normal file
View file

@ -0,0 +1,242 @@
require("dotenv").config();
const express = require("express");
const bodyParser = require("body-parser");
const mongoose = require("mongoose");
const jwt = require("jsonwebtoken");
const bcrypt = require("bcrypt");
const saltRounds = 10;
const { User } = require("./models");
const app = express();
const { Schema } = mongoose;
const port = process.env.PORT || "5000";
//let dbConn = mongodb.MongoClient.connect('mongodb://localhost:27017/monkeytype');
mongoose.connect("mongodb://localhost:27017/monkeytype", {
useNewUrlParser: true,
useUnifiedTopology: true,
});
app.use(express.static(__dirname + "/dist"));
app.use(bodyParser.json());
function authenticateToken(req, res, next) {
const authHeader = req.headers["authorization"];
const token = authHeader && authHeader.split(" ")[1];
if (token == null) return res.sendStatus(401);
jwt.verify(token, process.env.ACCESS_TOKEN_SECRET, (err, identity) => {
if (err) return res.sendStatus(403);
req.name = identity.name;
next();
});
}
// API
app.get("/api/currentUser", authenticateToken, (req, res) => {
User.findOne({ name: req.name }, (err, user) => {
//user must have uid, displayName, email properties
const retUser = {
uid: user._id,
displayName: user.name,
email: user.email,
};
res.json({ user: retUser });
});
});
app.post("/api/updateName", (req, res) => {
//this might be a put/patch request
//update the name of user with given uid
const uid = req.body.uid;
const name = req.body.name;
});
app.post("/api/sendEmailVerification", (req, res) => {
const uid = req.body.uid;
//Add send Email verification code here
//should be a seperate sendEmailVerification function that can be called from sign up as well
res.sendStatus(200);
});
app.post("/api/signIn", (req, res) => {
/* Takes email and password */
//Login and send tokens
User.findOne({ email: req.body.email }, (err, user) => {
if (err) res.status(500).send({ error: err });
if (user == null) {
res.status(500).send({ error: "No user found with that email" });
}
bcrypt.compare(req.body.password, user.password, (err, result) => {
if (err)
res.status(500).send({ error: "Error during password validation" });
if (result) {
//if password matches hash
const accessToken = jwt.sign(
{ name: user.name },
process.env.ACCESS_TOKEN_SECRET
);
const retUser = {
uid: user._id,
displayName: user.name,
email: user.email,
emailVerified: user.emailVerified,
metadata: { creationTime: user.createdAt },
};
res.json({ accessToken: accessToken, user: retUser });
} else {
//if password doesn't match hash
res.status(500).send({ error: "Password invalid" });
}
});
});
});
app.post("/api/signOut", (req, res) => {
/* Takes user id and token? */
//Logout user
//This route isn't necessary for jwt authentication
res.sendStatus(200);
});
app.post("/api/signUp", (req, res) => {
/* Takes name, email, password */
//check if name has been taken
User.exists({ name: req.body.name }).then((exists) => {
//should also check if email is used
if (exists) {
//user with that name already exists
res.status(500).send({ error: "Username taken" });
}
bcrypt.hash(req.body.password, saltRounds, function (err, hash) {
if (err) console.log(err);
const newuser = new User({
name: req.body.name,
email: req.body.email,
emailVerified: false,
password: hash,
});
newuser
.save()
.then((user) => {
//send email verification
//add account created event to analytics
//return user data and access token
const accessToken = jwt.sign(
{ name: req.body.name },
process.env.ACCESS_TOKEN_SECRET
);
const retUser = {
uid: user._id,
displayName: user.name,
email: user.email,
emailVerified: user.emailVerified,
metadata: { creationTime: user.createdAt },
};
res.json({ accessToken: accessToken, user: retUser });
})
.catch((e) => {
console.log(e);
res.status(500).send({ error: "Error when adding user" });
});
});
});
});
app.post("/api/passwordReset", (req, res) => {
const email = req.body.email;
//send email to the passed email requesting password reset
res.sendStatus(200);
});
app.get("/api/fetchSnapshot", (req, res) => {
const uid = req.body.uid;
const token = req.body.token; //should be passed through header authentication
//return a list of tags
//return list of presets
//return user data
//tags and presets should be included
res.sendStatus(200);
});
app.get("/api/userResults", (req, res) => {
const uid = req.body.uid; //might not need uid if token is given
//return list of results
res.sendStatus(200);
});
app.post("/api/saveConfig", (req, res) => {
const config = req.body.config;
//parse config object to prevent errors
//save passed config object to database
});
// ANALYTICS API
function newAnalyticsEvent() {}
app.post("/api/analytics/usedCommandLine", (req, res) => {
//save command used from command line to analytics
const command = req.body.command;
res.sendStatus(200);
});
app.post("/api/analytics/changedLanguage", (req, res) => {
//save what a user changed their language to
const language = req.body.language;
res.sendStatus(200);
});
app.post("/api/analytics/changedTheme", (req, res) => {
//save what a user changed their theme to
const theme = req.body.theme;
res.sendStatus(200);
});
app.post("/api/analytics/testStarted", (req, res) => {
//log that a test was started
res.sendStatus(200);
});
app.post("/api/analytics/testStartedNoLogin", (req, res) => {
//log that a test was started without login
res.sendStatus(200);
});
app.post("/api/analytics/testCompleted", (req, res) => {
//log that a test was completed
const completedEvent = req.body.completedEvent;
res.sendStatus(200);
});
app.post("/api/analytics/testCompletedNoLogin", (req, res) => {
//log that a test was completed and user was not logged in
const completedEvent = req.body.completedEvent;
res.sendStatus(200);
});
app.post("/api/analytics/testCompletedInvalid", (req, res) => {
//log that a test was completed and is invalid
const completedEvent = req.body.completedEvent;
res.sendStatus(200);
});
// CLOUD FUNCTIONS
// STATIC FILES
app.get("/privacy-policy", (req, res) => {
res.sendFile(__dirname + "/dist/privacy-policy.html");
});
app.use((req, res, next) => {
//sends index.html if the route is not found above
res.sendFile(__dirname + "/dist/index.html");
});
// LISTENER
app.listen(port, () => {
console.log(`Listening to requests on http://localhost:${port}`);
});

View file

@ -13,52 +13,41 @@ import * as AllTimeStats from "./all-time-stats";
import * as DB from "./db";
import * as TestLogic from "./test-logic";
import * as UI from "./ui";
import axios from "axios";
import Cookies from "js-cookie";
var gmailProvider = new firebase.auth.GoogleAuthProvider();
//var gmailProvider = new firebase.auth.GoogleAuthProvider();
export function signIn() {
$(".pageLogin .preloader").removeClass("hidden");
let email = $(".pageLogin .login input")[0].value;
let password = $(".pageLogin .login input")[1].value;
if ($(".pageLogin .login #rememberMe input").prop("checked")) {
//remember me
firebase
.auth()
.setPersistence(firebase.auth.Auth.Persistence.LOCAL)
.then(function () {
return firebase
.auth()
.signInWithEmailAndPassword(email, password)
.then((e) => {
// UI.changePage("test");
})
.catch(function (error) {
Notifications.add(error.message, -1);
$(".pageLogin .preloader").addClass("hidden");
});
});
} else {
//dont remember
firebase
.auth()
.setPersistence(firebase.auth.Auth.Persistence.SESSION)
.then(function () {
return firebase
.auth()
.signInWithEmailAndPassword(email, password)
.then((e) => {
// UI.changePage("test");
})
.catch(function (error) {
Notifications.add(error.message, -1);
$(".pageLogin .preloader").addClass("hidden");
});
});
}
axios
.post("/api/signIn", {
email: email,
password: password,
})
.then((response) => {
// UI.changePage("test");
if ($(".pageLogin .login #rememberMe input").prop("checked")) {
// TODO: set user login cookie that persists after session
Cookies.set("accessToken", response.data.accessToken);
} else {
//set user login cookie to persist only as long as the session lives
Cookies.set("accessToken", response.data.accessToken);
}
userStateChanged(response.data.user);
})
.catch((e) => {
console.log("Could not be signed in");
Notifications.add(e.message, -1);
$(".pageLogin .preloader").addClass("hidden");
});
}
export async function signInWithGoogle() {
console.log("Login with google temporarily unavailable");
/*
$(".pageLogin .preloader").removeClass("hidden");
if ($(".pageLogin .login #rememberMe input").prop("checked")) {
@ -90,9 +79,12 @@ export async function signInWithGoogle() {
$(".pageLogin .preloader").addClass("hidden");
});
}
*/
}
export function linkWithGoogle() {
console.log("Link with google temporarily unavailable");
/*
firebase
.auth()
.currentUser.linkWithPopup(gmailProvider)
@ -102,21 +94,27 @@ export function linkWithGoogle() {
.catch(function (error) {
console.log(error);
});
*/
}
export function signOut() {
firebase
.auth()
.signOut()
.then(function () {
const uid = DB.currentUser().uid;
axios
.post("/api/signOut", {
uid: uid,
//should there be a token necessary to sign out?
//if you send the token, that would eliminate the need to send uid
})
.then(() => {
Notifications.add("Signed out", 0, 2);
AllTimeStats.clear();
Settings.hideAccountSection();
AccountButton.update();
UI.changePage("login");
DB.setSnapshot(null);
userStateChanged(null);
})
.catch(function (error) {
.catch((error) => {
Notifications.add(error.message, -1);
});
}
@ -136,130 +134,62 @@ function signUp() {
return;
}
CloudFunctions.namecheck({ name: nname }).then((d) => {
if (d.data.resultCode === -1) {
Notifications.add("Name unavailable", -1);
axios
.post("/api/signUp", {
name: nname,
email: email,
password: password,
})
.then((response) => {
let usr = response.data.user;
Cookies.set("accessToken", response.data.accessToken);
//Cookies.set('refreshToken', response.data.refreshToken);
AllTimeStats.clear();
Notifications.add("Account created", 1, 3);
$("#menu .icon-button.account .text").text(nname);
$(".pageLogin .preloader").addClass("hidden");
$(".pageLogin .register .button").removeClass("disabled");
return;
} else if (d.data.resultCode === -2) {
Notifications.add(
"Name cannot contain special characters or contain more than 14 characters. Can include _ . and -",
-1
);
$(".pageLogin .preloader").addClass("hidden");
$(".pageLogin .register .button").removeClass("disabled");
return;
} else if (d.data.resultCode === 1) {
firebase
.auth()
.createUserWithEmailAndPassword(email, password)
.then((user) => {
// Account has been created here.
// dontCheckUserName = true;
let usr = user.user;
usr
.updateProfile({
displayName: nname,
})
.then(async function () {
// Update successful.
await firebase
.firestore()
.collection("users")
.doc(usr.uid)
.set({ name: nname }, { merge: true });
CloudFunctions.reserveName({ name: nname, uid: usr.uid }).catch(
(e) => {
console.error("Could not reserve name " + e);
throw "Could not reserve name";
}
);
usr.sendEmailVerification();
AllTimeStats.clear();
Notifications.add("Account created", 1, 3);
$("#menu .icon-button.account .text").text(nname);
try {
firebase.analytics().logEvent("accountCreated", usr.uid);
} catch (e) {
console.log("Analytics unavailable");
}
$(".pageLogin .preloader").addClass("hidden");
DB.setSnapshot({
results: [],
personalBests: {},
tags: [],
globalStats: {
time: undefined,
started: undefined,
completed: undefined,
},
});
if (TestLogic.notSignedInLastResult !== null) {
TestLogic.setNotSignedInUid(usr.uid);
CloudFunctions.testCompleted({
uid: usr.uid,
obj: TestLogic.notSignedInLastResult,
});
DB.getSnapshot().results.push(TestLogic.notSignedInLastResult);
}
UI.changePage("account");
usr.sendEmailVerification();
$(".pageLogin .register .button").removeClass("disabled");
})
.catch(function (error) {
// An error happened.
$(".pageLogin .register .button").removeClass("disabled");
console.error(error);
usr
.delete()
.then(function () {
// User deleted.
Notifications.add(
"Account not created. " + error.message,
-1
);
$(".pageLogin .preloader").addClass("hidden");
})
.catch(function (error) {
// An error happened.
$(".pageLogin .preloader").addClass("hidden");
Notifications.add(
"Something went wrong. " + error.message,
-1
);
console.error(error);
});
});
})
.catch(function (error) {
// Handle Errors here.
$(".pageLogin .register .button").removeClass("disabled");
Notifications.add(error.message, -1);
$(".pageLogin .preloader").addClass("hidden");
DB.setSnapshot({
results: [],
personalBests: {},
tags: [],
globalStats: {
time: undefined,
started: undefined,
completed: undefined,
},
});
if (TestLogic.notSignedInLastResult !== null) {
TestLogic.setNotSignedInUid(usr.uid);
CloudFunctions.testCompleted({
uid: usr.uid,
obj: TestLogic.notSignedInLastResult,
});
} else {
DB.getSnapshot().results.push(TestLogic.notSignedInLastResult);
}
UI.changePage("account");
$(".pageLogin .register .button").removeClass("disabled");
userStateChanged(usr);
})
.catch((error) => {
//Notifications.add("Account not created. " + error.message, -1);
Notifications.add(error, -1);
$(".pageLogin .preloader").addClass("hidden");
Notifications.add(
"Something went wrong when checking name: " + d.data.message,
-1
);
}
});
$(".pageLogin .register .button").removeClass("disabled");
return;
});
}
$(".pageLogin #forgotPasswordButton").click((e) => {
let email = prompt("Email address");
if (email) {
firebase
.auth()
.sendPasswordResetEmail(email)
.then(function () {
// Email sent.
axios
.post("/api/passwordReset", {
email: email,
})
.then(() => {
Notifications.add("Email sent", 1, 2);
})
.catch(function (error) {
// An error happened.
.catch((error) => {
Notifications.add(error.message, -1);
});
}
@ -286,7 +216,7 @@ $(".signOut").click((e) => {
signOut();
});
firebase.auth().onAuthStateChanged(function (user) {
function userStateChanged(user) {
if (user) {
// User is signed in.
$(".pageAccount .content p.accountVerificatinNotice").remove();
@ -348,7 +278,7 @@ firebase.auth().onAuthStateChanged(function (user) {
ChallengeController.setup(challengeName);
}, 1000);
}
});
}
$(".pageLogin .register input").keyup((e) => {
if ($(".pageLogin .register .button").hasClass("disabled")) return;

View file

@ -27,7 +27,7 @@ export function getDataAndInit() {
if (snap === null) {
throw "Missing db snapshot. Client likely could not connect to the backend.";
}
let user = firebase.auth().currentUser;
let user = DB.currentUser(); // I think that this should be stored in cookie
if (snap.name === undefined) {
//verify username
if (Misc.isUsernameValid(user.displayName)) {

View file

@ -1,45 +1,73 @@
export const testCompleted = firebase
.functions()
.httpsCallable("testCompleted");
export const addTag = firebase.functions().httpsCallable("addTag");
export const editTag = firebase.functions().httpsCallable("editTag");
export const removeTag = firebase.functions().httpsCallable("removeTag");
export const updateResultTags = firebase
.functions()
.httpsCallable("updateResultTags");
export const saveConfig = firebase.functions().httpsCallable("saveConfig");
export const addPreset = firebase.functions().httpsCallable("addPreset");
export const editPreset = firebase.functions().httpsCallable("editPreset");
export const removePreset = firebase.functions().httpsCallable("removePreset");
export const generatePairingCode = firebase
.functions()
.httpsCallable("generatePairingCode");
export const saveLbMemory = firebase.functions().httpsCallable("saveLbMemory");
export const unlinkDiscord = firebase
.functions()
.httpsCallable("unlinkDiscord");
export const verifyUser = firebase.functions().httpsCallable("verifyUser");
export const reserveName = firebase
.functions()
.httpsCallable("reserveDisplayName");
export const updateEmail = firebase.functions().httpsCallable("updateEmail");
export const namecheck = firebase
.functions()
.httpsCallable("checkNameAvailability");
export const getLeaderboard = firebase
.functions()
.httpsCallable("getLeaderboard");
export const clearTagPb = firebase.functions().httpsCallable("clearTagPb");
export const changeDisplayName = firebase
.functions()
.httpsCallable("changeDisplayName");
// all functions here should be changed into api calls
//export function testCompleted = axios.post('/api/testCompleted', )firebase
// .functions()
// .httpsCallable("testCompleted");
import axios from "axios";
export const removeSmallTests = firebase
.functions()
.httpsCallable("removeSmallTestsAndQPB");
export const resetPersonalBests = firebase
.functions()
.httpsCallable("resetPersonalBests");
export const checkLeaderboards = firebase
.functions()
.httpsCallable("checkLeaderboards");
export function testCompleted(input) {
console.log("testCompleted");
}
export function addTag(input) {
console.log("request data here");
}
export function editTag(input) {
console.log("request data here");
}
export function removeTag(input) {
console.log("request data here");
}
export function updateResultTags(input) {
console.log("request data here");
}
export function saveConfig(input) {
console.log("request data here");
}
export function addPreset(input) {
console.log("request data here");
}
export function editPreset(input) {
console.log("request data here");
}
export function removePreset(input) {
console.log("request data here");
}
export function generatePairingCode(input) {
console.log("request data here");
}
export function saveLbMemory(input) {
console.log("request data here");
}
export function unlinkDiscord(input) {
console.log("request data here");
}
export function verifyUser(input) {
console.log("request data here");
}
export function reserveName(input) {
console.log("request data here");
}
export function updateEmail(input) {
console.log("request data here");
}
export function namecheck(input) {
console.log("request data here");
}
export function getLeaderboard(input) {
console.log("request data here");
}
export function clearTagPb(input) {
console.log("request data here");
}
export function changeDisplayName(input) {
console.log("request data here");
}
export function removeSmallTests(input) {
console.log("request data here");
}
export function resetPersonalBests(input) {
console.log("request data here");
}
export function checkLeaderboards(input) {
console.log("request data here");
}

View file

@ -4,6 +4,7 @@ import Config, * as UpdateConfig from "./config";
import * as Focus from "./focus";
import * as CommandlineLists from "./commandline-lists";
import * as TestUI from "./test-ui";
import axios from "axios";
let commandLineMouseMode = false;
@ -163,13 +164,11 @@ function trigger(command) {
}
});
if (!subgroup && !input && !sticky) {
try {
firebase.analytics().logEvent("usedCommandLine", {
command: command,
axios
.post("/api/analytics/usedCommandLine", { command: command })
.catch(() => {
console.log("Analytics unavailable");
});
} catch (e) {
console.log("Analytics unavailable");
}
hide();
}
}
@ -325,13 +324,11 @@ $("#commandInput input").keydown((e) => {
}
}
});
try {
firebase.analytics().logEvent("usedCommandLine", {
command: command,
axios
.post("/api/analytics/usedCommandLine", { command: command })
.catch(() => {
console.log("Analytics unavailable");
});
} catch (e) {
console.log("Analytics unavailable");
}
hide();
}
return;

View file

@ -17,6 +17,7 @@ import * as UI from "./ui";
import * as CommandlineLists from "./commandline-lists";
import * as BackgroundFilter from "./custom-background-filter";
import LayoutList from "./layouts";
import axios from "axios";
export let localStorageConfig = null;
export let dbConfigLoaded = false;
@ -479,7 +480,7 @@ export function setPaceCaret(val, nosave) {
val = "off";
}
if (document.readyState === "complete") {
if (val == "pb" && firebase.auth().currentUser === null) {
if (val == "pb" && DB.currentUser() === null) {
Notifications.add("PB pace caret is unavailable without an account", 0);
return;
}
@ -1217,13 +1218,11 @@ export function setLanguage(language, nosave) {
language = "english";
}
config.language = language;
try {
firebase.analytics().logEvent("changedLanguage", {
language: language,
axios
.post("/api/analytics/changedLanguage", { language: language })
.catch(() => {
console.log("Analytics unavailable");
});
} catch (e) {
console.log("Analytics unavailable");
}
if (!nosave) saveToLocalStorage();
}
@ -1537,10 +1536,8 @@ export function apply(configObj) {
try {
setEnableAds(configObj.enableAds, true);
let addemo = false;
if (
firebase.app().options.projectId === "monkey-type-dev-67af4" ||
window.location.hostname === "localhost"
) {
//if the app is not running on production, play advertisement demo
if (window.location.hostname === "localhost") {
addemo = true;
}

View file

@ -2,14 +2,22 @@ import { loadTags } from "./result-filters";
import * as AccountButton from "./account-button";
import * as CloudFunctions from "./cloud-functions";
import * as Notifications from "./notifications";
import axios from "axios";
import Cookies from "js-cookie";
const db = firebase.firestore();
db.settings({ experimentalForceLongPolling: true });
//const db = firebase.firestore();
//db.settings({ experimentalForceLongPolling: true });
let dbSnapshot = null;
//I think that a lot of these functions could be simplified by api calls
export function updateName(uid, name) {
db.collection(`users`).doc(uid).set({ name: name }, { merge: true });
//db.collection(`users`).doc(uid).set({ name: name }, { merge: true });
axios.post("/api/updateName", {
uid: uid,
name: name,
});
}
export function getSnapshot() {
@ -26,8 +34,29 @@ export function setSnapshot(newSnapshot) {
dbSnapshot = newSnapshot;
}
export function currentUser() {
const token = Cookies.get("accessToken");
//probably don't need to request the server every time
//uid, email, and displayName could be stored in cookies
//however, this method proves that the token is valid
//might not be important
axios
.get("/api/currentUser", {
headers: { Authorization: `Bearer ${token}` },
})
.then((response) => {
return response.data.user;
})
.catch(() => {
return null;
});
}
export async function initSnapshot() {
let user = firebase.auth().currentUser;
//get identity and token
//send api request with token that returns tags, presets, and data needed for snap
/*
let user = currentUser();
if (user == null) return false;
let snap = {
results: undefined,
@ -56,7 +85,15 @@ export async function initSnapshot() {
completed: 0,
},
};
axios.get('/api/fetchSnapshot', {
uid: user.uid,
//not sure how I'm going to pass tokens right now
//token: user.token
}).then((response) => {
//set snapshot equal to response.data.snap or whatever I call it
})
try {
//add tags to snap tags list
await db
.collection(`users/${user.uid}/tags/`)
.get()
@ -82,6 +119,7 @@ export async function initSnapshot() {
.catch((e) => {
throw e;
});
//set presets? Not sure what this is at the moment
await db
.collection(`users/${user.uid}/presets/`)
.get()
@ -105,6 +143,7 @@ export async function initSnapshot() {
.catch((e) => {
throw e;
});
//set snap data to the data found in the database
await db
.collection("users")
.doc(user.uid)
@ -150,15 +189,28 @@ export async function initSnapshot() {
}
loadTags(dbSnapshot.tags);
return dbSnapshot;
*/
}
export async function getUserResults() {
let user = firebase.auth().currentUser;
let user = currentUser();
if (user == null) return false;
if (dbSnapshot === null) return false;
if (dbSnapshot.results !== undefined) {
return true;
} else {
axios
.get("/api/userResults", {
uid: user.uid,
})
.then((response) => {
dbSnapshot.results = response.data.results;
})
.catch((error) => {
console.log(error);
});
}
/*
try {
return await db
.collection(`users/${user.uid}/results/`)
@ -171,6 +223,7 @@ export async function getUserResults() {
let result = doc.data();
result.id = doc.id;
//this should be done server-side
if (result.bailedOut === undefined) result.bailedOut = false;
if (result.blindMode === undefined) result.blindMode = false;
if (result.difficulty === undefined) result.difficulty = "normal";
@ -191,6 +244,7 @@ export async function getUserResults() {
return false;
}
}
*/
}
export async function getUserHighestWpm(
@ -500,22 +554,27 @@ export async function saveLocalTagPB(
}
export function updateLbMemory(mode, mode2, type, value) {
//could dbSnapshot just be used here instead of getSnapshot()
getSnapshot().lbMemory[mode + mode2][type] = value;
}
export async function saveConfig(config) {
if (firebase.auth().currentUser !== null) {
if (currentUser() !== null) {
AccountButton.loading(true);
CloudFunctions.saveConfig({
uid: firebase.auth().currentUser.uid,
obj: config,
}).then((d) => {
AccountButton.loading(false);
if (d.data.resultCode !== 1) {
Notifications.add(`Error saving config to DB! ${d.data.message}`, 4000);
}
return;
});
axios
.post("/api/saveConfig", {
uid: currentUser().uid,
})
.then((response) => {
AccountButton.loading(false);
if (response.data.resultCode !== 1) {
Notifications.add(
`Error saving config to DB! ${response.data.message}`,
4000
);
}
return;
});
}
}

View file

@ -1,4 +1,5 @@
import * as UI from "./ui";
import * as DB from "./db";
export function loading(truefalse) {
if (truefalse) {
@ -13,7 +14,7 @@ export function loading(truefalse) {
}
export function update() {
if (firebase.auth().currentUser != null) {
if (DB.currentUser() != null) {
UI.swapElements(
$("#menu .icon-button.login"),
$("#menu .icon-button.account"),

View file

@ -1,6 +1,7 @@
import * as CloudFunctions from "./cloud-functions";
import * as Loader from "./loader";
import * as Notifications from "./notifications";
import * as DB from "./db";
let currentLeaderboard = "time_15";
@ -28,8 +29,8 @@ function update() {
let boardinfo = currentLeaderboard.split("_");
let uid = null;
if (firebase.auth().currentUser !== null) {
uid = firebase.auth().currentUser.uid;
if (DB.currentUser() !== null) {
uid = DB.currentUser().uid;
}
Loader.show();

View file

@ -1,8 +1,10 @@
import * as Loader from "./loader";
import * as DB from "./db";
import axios from "axios";
export function getuid() {
console.error("Only share this uid with Miodec and nobody else!");
console.log(firebase.auth().currentUser.uid);
console.log(DB.currentUser().uid);
console.error("Only share this uid with Miodec and nobody else!");
}
@ -313,8 +315,11 @@ export function migrateFromCookies() {
export function sendVerificationEmail() {
Loader.show();
let cu = firebase.auth().currentUser;
cu.sendEmailVerification()
let cu = DB.currentUser();
axios
.post("/api/sendEmailVerification", {
uid: cu.uid,
})
.then(() => {
Loader.hide();
showNotification("Email sent to " + cu.email, 4000);

View file

@ -72,7 +72,7 @@ function apply() {
if (action === "add") {
Loader.show();
CloudFunctions.addPreset({
uid: firebase.auth().currentUser.uid,
uid: DB.currentUser().uid,
obj: {
name: inputVal,
config: configChanges,
@ -99,7 +99,7 @@ function apply() {
} else if (action === "edit") {
Loader.show();
CloudFunctions.editPreset({
uid: firebase.auth().currentUser.uid,
uid: DB.currentUser().uid,
name: inputVal,
presetid,
config: configChanges,
@ -123,7 +123,7 @@ function apply() {
} else if (action === "remove") {
Loader.show();
CloudFunctions.removePreset({
uid: firebase.auth().currentUser.uid,
uid: DB.currentUser().uid,
presetid,
}).then((e) => {
Loader.hide();

View file

@ -67,7 +67,7 @@ function apply() {
if (action === "add") {
Loader.show();
CloudFunctions.addTag({
uid: firebase.auth().currentUser.uid,
uid: DB.currentUser().uid,
name: inputVal,
}).then((e) => {
Loader.hide();
@ -90,7 +90,7 @@ function apply() {
} else if (action === "edit") {
Loader.show();
CloudFunctions.editTag({
uid: firebase.auth().currentUser.uid,
uid: DB.currentUser().uid,
name: inputVal,
tagid: tagid,
}).then((e) => {
@ -115,7 +115,7 @@ function apply() {
} else if (action === "remove") {
Loader.show();
CloudFunctions.removeTag({
uid: firebase.auth().currentUser.uid,
uid: DB.currentUser().uid,
tagid: tagid,
}).then((e) => {
Loader.hide();

View file

@ -86,7 +86,7 @@ $("#resultEditTagsPanel .confirmButton").click((e) => {
Loader.show();
hide();
CloudFunctions.updateResultTags({
uid: firebase.auth().currentUser.uid,
uid: DB.currentUser().uid,
tags: newtags,
resultid: resultid,
}).then((r) => {

View file

@ -7,12 +7,15 @@ import * as RouteController from "./route-controller";
import * as UI from "./ui";
import * as SignOutButton from "./sign-out-button";
console.log("redy loaded");
ManualRestart.set();
Misc.migrateFromCookies();
UpdateConfig.loadFromLocalStorage();
Misc.getReleasesFromGitHub();
$(document).ready(() => {
console.log("Ready started");
//this makes the center content visible
RouteController.handleInitialPageClasses(window.location.pathname);
if (window.location.pathname === "/") {
$("#top .config").removeClass("hidden");

View file

@ -1,5 +1,6 @@
import * as Funbox from "./funbox";
import * as UI from "./ui";
import * as DB from "./db";
let mappedRoutes = {
"/": "pageTest",
@ -35,7 +36,7 @@ $(window).on("popstate", (e) => {
// show about
UI.changePage("about");
} else if (state == "account" || state == "login") {
if (firebase.auth().currentUser) {
if (DB.currentUser()) {
UI.changePage("account");
} else {
UI.changePage("login");

View file

@ -418,7 +418,7 @@ function showActiveTags() {
export function updateDiscordSection() {
//no code and no discord
if (firebase.auth().currentUser == null) {
if (DB.currentUser() == null) {
$(".pageSettings .section.discordIntegration").addClass("hidden");
} else {
if (DB.getSnapshot() == null) return;
@ -449,7 +449,7 @@ function setActiveFunboxButton() {
}
function refreshTagsSettingsSection() {
if (firebase.auth().currentUser !== null && DB.getSnapshot() !== null) {
if (DB.currentUser() !== null && DB.getSnapshot() !== null) {
let tagsEl = $(".pageSettings .section.tags .tagsList").empty();
DB.getSnapshot().tags.forEach((tag) => {
let tagPbString = "No PB found";
@ -476,7 +476,7 @@ function refreshTagsSettingsSection() {
}
function refreshPresetsSettingsSection() {
if (firebase.auth().currentUser !== null && DB.getSnapshot() !== null) {
if (DB.currentUser() !== null && DB.getSnapshot() !== null) {
let presetsEl = $(".pageSettings .section.presets .presetsList").empty();
DB.getSnapshot().presets.forEach((preset) => {
presetsEl.append(`
@ -605,7 +605,7 @@ $(
).click((e) => {
Loader.show();
CloudFunctions.generatePairingCode({
uid: firebase.auth().currentUser.uid,
uid: DB.currentUser().uid,
})
.then((ret) => {
Loader.hide();
@ -631,7 +631,7 @@ $(".pageSettings .section.discordIntegration #unlinkDiscordButton").click(
if (confirm("Are you sure?")) {
Loader.show();
CloudFunctions.unlinkDiscord({
uid: firebase.auth().currentUser.uid,
uid: DB.currentUser().uid,
}).then((ret) => {
Loader.hide();
console.log(ret);

View file

@ -152,7 +152,7 @@ list.updateEmail = new SimplePopup(
try {
Loader.show();
CloudFunctions.updateEmail({
uid: firebase.auth().currentUser.uid,
uid: DB.currentUser().uid,
previousEmail: previousEmail,
newEmail: newEmail,
}).then((data) => {
@ -189,7 +189,7 @@ list.clearTagPb = new SimplePopup(
let tagid = eval("this.parameters[0]");
Loader.show();
CloudFunctions.clearTagPb({
uid: firebase.auth().currentUser.uid,
uid: DB.currentUser().uid,
tagid: tagid,
})
.then((res) => {
@ -247,7 +247,7 @@ list.resetPersonalBests = new SimplePopup(
Loader.show();
CloudFunctions.resetPersonalBests({
uid: firebase.auth().currentUser.uid,
uid: DB.currentUser().uid,
}).then((res) => {
if (res) {
Loader.hide();

View file

@ -114,7 +114,7 @@ export function show(data, mode2) {
string = globalLbString + "<br>" + dailyLbString;
// CloudFunctions.saveLbMemory({
// uid: firebase.auth().currentUser.uid,
// uid: DB.currentUser().uid,
// obj: DB.getSnapshot().lbMemory,
// }).then((d) => {
// if (d.data.returnCode === 1) {

View file

@ -310,10 +310,10 @@ export function startTest() {
UpdateConfig.setChangedBeforeDb(true);
}
try {
if (firebase.auth().currentUser != null) {
firebase.analytics().logEvent("testStarted");
if (DB.currentUser() != null) {
axios.post("/api/analytics/testStarted");
} else {
firebase.analytics().logEvent("testStartedNoLogin");
axios.post("/api/analytics/testStartedNoLogin");
}
} catch (e) {
console.log("Analytics unavailable");
@ -1383,8 +1383,8 @@ export function finish(difficultyFailed = false) {
stats.acc > 50 &&
stats.acc <= 100
) {
if (firebase.auth().currentUser != null) {
completedEvent.uid = firebase.auth().currentUser.uid;
if (DB.currentUser() != null) {
completedEvent.uid = DB.currentUser().uid;
//check local pb
AccountButton.loading(true);
let dontShowCrown = false;
@ -1542,7 +1542,7 @@ export function finish(difficultyFailed = false) {
);
}
CloudFunctions.testCompleted({
uid: firebase.auth().currentUser.uid,
uid: DB.currentUser().uid,
obj: completedEvent,
})
.then((e) => {
@ -1615,13 +1615,14 @@ export function finish(difficultyFailed = false) {
DB.getSnapshot().globalStats.completed += 1;
}
}
try {
firebase
.analytics()
.logEvent("testCompleted", completedEvent);
} catch (e) {
console.log("Analytics unavailable");
}
axios
.post("/api/analytics/testCompleted", {
completedEvent: completedEvent,
})
.catch(() => {
console.log("Analytics unavailable");
});
if (e.data.resultCode === 2) {
//new pb
@ -1656,25 +1657,29 @@ export function finish(difficultyFailed = false) {
});
});
} else {
try {
firebase.analytics().logEvent("testCompletedNoLogin", completedEvent);
} catch (e) {
console.log("Analytics unavailable");
}
axios
.post("/api/analytics/testCompletedNoLogin", {
completedEvent: completedEvent,
})
.catch((e) => {
console.log("Analytics unavailable");
});
notSignedInLastResult = completedEvent;
}
} else {
Notifications.add("Test invalid", 0);
TestStats.setInvalid();
try {
firebase.analytics().logEvent("testCompletedInvalid", completedEvent);
} catch (e) {
console.log("Analytics unavailable");
}
axios
.post("/api/analytics/testCompletedInvalid", {
completedEvent: completedEvent,
})
.catch((e) => {
console.log("Analytics unavailable");
});
}
}
if (firebase.auth().currentUser != null) {
if (DB.currentUser() != null) {
$("#result .loginTip").addClass("hidden");
} else {
$("#result .stats .leaderboards").addClass("hidden");

View file

@ -211,7 +211,7 @@ export function screenshot() {
$("#commandLineMobileButton").removeClass("hidden");
$(".pageTest .ssWatermark").addClass("hidden");
$(".pageTest .buttons").removeClass("hidden");
if (firebase.auth().currentUser == null)
if (DB.currentUser() == null)
$(".pageTest .loginTip").removeClass("hidden");
} else {
navigator.clipboard
@ -229,7 +229,7 @@ export function screenshot() {
Notifications.add("Copied to clipboard", 1, 2);
$(".pageTest .ssWatermark").addClass("hidden");
$(".pageTest .buttons").removeClass("hidden");
if (firebase.auth().currentUser == null)
if (DB.currentUser() == null)
$(".pageTest .loginTip").removeClass("hidden");
});
}
@ -242,7 +242,7 @@ export function screenshot() {
);
$(".pageTest .ssWatermark").addClass("hidden");
$(".pageTest .buttons").removeClass("hidden");
if (firebase.auth().currentUser == null)
if (DB.currentUser() == null)
$(".pageTest .loginTip").removeClass("hidden");
}
});
@ -253,7 +253,7 @@ export function screenshot() {
Notifications.add("Error creating image: " + e.message, -1);
$(".pageTest .ssWatermark").addClass("hidden");
$(".pageTest .buttons").removeClass("hidden");
if (firebase.auth().currentUser == null)
if (DB.currentUser() == null)
$(".pageTest .loginTip").removeClass("hidden");
}
setTimeout(() => {
@ -261,7 +261,7 @@ export function screenshot() {
$("#commandLineMobileButton").removeClass("hidden");
$(".pageTest .ssWatermark").addClass("hidden");
$(".pageTest .buttons").removeClass("hidden");
if (firebase.auth().currentUser == null)
if (DB.currentUser() == null)
$(".pageTest .loginTip").removeClass("hidden");
}, 3000);
}

View file

@ -5,6 +5,7 @@ import * as Notifications from "./notifications";
import Config from "./config";
import * as UI from "./ui";
import tinycolor from "tinycolor2";
import axios from "axios";
let isPreviewingTheme = false;
export let randomTheme = null;
@ -95,13 +96,13 @@ export function apply(themeName) {
});
}
try {
firebase.analytics().logEvent("changedTheme", {
axios
.post("/api/analytics/changedTheme", {
theme: themeName,
})
.catch((e) => {
console.log("Analytics unavailable");
});
} catch (e) {
console.log("Analytics unavailable");
}
setTimeout(() => {
$(".keymap-key").attr("style", "");
ChartController.updateAllChartColors();

View file

@ -14,6 +14,7 @@ import * as Settings from "./settings";
import * as Account from "./account";
import * as Leaderboards from "./leaderboards";
import * as Funbox from "./funbox";
import * as DB from "./db";
export let pageTransition = false;
@ -161,10 +162,8 @@ export function changePage(page) {
TestConfig.hide();
SignOutButton.hide();
} else if (page == "account") {
if (!firebase.auth().currentUser) {
console.log(
`current user is ${firebase.auth().currentUser}, going back to login`
);
if (!DB.currentUser()) {
console.log(`current user is ${DB.currentUser()}, going back to login`);
changePage("login");
} else {
setPageTransition(true);
@ -187,7 +186,7 @@ export function changePage(page) {
TestConfig.hide();
}
} else if (page == "login") {
if (firebase.auth().currentUser != null) {
if (DB.currentUser() != null) {
changePage("account");
} else {
setPageTransition(true);
@ -204,6 +203,8 @@ export function changePage(page) {
}
}
//checking if the project is the development site
/*
if (firebase.app().options.projectId === "monkey-type-dev-67af4") {
$("#top .logo .bottom").text("monkey-dev");
$("head title").text("Monkey Dev");
@ -211,6 +212,7 @@ if (firebase.app().options.projectId === "monkey-type-dev-67af4") {
`<div class="devIndicator tr">DEV</div><div class="devIndicator bl">DEV</div>`
);
}
*/
if (window.location.hostname === "localhost") {
window.onerror = function (error) {
@ -218,7 +220,7 @@ if (window.location.hostname === "localhost") {
};
$("#top .logo .top").text("localhost");
$("head title").text($("head title").text() + " (localhost)");
firebase.functions().useFunctionsEmulator("http://localhost:5001");
//firebase.functions().useFunctionsEmulator("http://localhost:5001");
$("body").append(
`<div class="devIndicator tl">local</div><div class="devIndicator br">local</div>`
);

View file

@ -4231,18 +4231,7 @@
<div id="nitropay_ad_right" class="hidden"></div>
</div>
</body>
<!-- The core Firebase JS SDK is always required and must be listed first -->
<script src="/__/firebase/8.0.2/firebase-app.js"></script>
<!-- TODO: Add SDKs for Firebase products that you want to use
https://firebase.google.com/docs/web/setup#available-libraries -->
<script src="/__/firebase/8.0.2/firebase-analytics.js"></script>
<script src="/__/firebase/8.0.2/firebase-auth.js"></script>
<script src="/__/firebase/8.0.2/firebase-firestore.js"></script>
<script src="/__/firebase/8.0.2/firebase-functions.js"></script>
<!-- Initialize Firebase -->
<script src="/__/firebase/init.js"></script>
<script src="js/jquery-3.5.1.min.js"></script>
<script src="js/jquery.color.min.js"></script>
<script src="js/easing.js"></script>