diff --git a/backend/mongo-todo.md b/backend/mongo-todo.md index 1f3021d05..6c7892cba 100644 --- a/backend/mongo-todo.md +++ b/backend/mongo-todo.md @@ -5,10 +5,9 @@ Make sure that the branch is ready for deployment - Add deploy script(s) to package.json -- Make sure that firebase hosted app can connect to api when deployed - Create a plan for apache/nginx server - - Api should probably be accessible via api.monkeytype.com or monkeytype.com/api - - Probably the previous since firebase might not work with seperate linux server + - Api should be accessible via api.monkeytype.com +- Add helmet middleware to express api? ## Bugs diff --git a/backend/server.js b/backend/server.js index 7a57b2a93..c66386831 100644 --- a/backend/server.js +++ b/backend/server.js @@ -3,6 +3,7 @@ const bodyParser = require("body-parser"); const mongoose = require("mongoose"); const cors = require("cors"); const admin = require("firebase-admin"); +const helmet = require("helmet"); const { User } = require("./models/user"); const { Leaderboard } = require("./models/leaderboard"); @@ -16,6 +17,7 @@ admin.initializeApp({ // MIDDLEWARE & SETUP const app = express(); app.use(cors()); +app.use(helmet()); const port = process.env.PORT || "5005"; @@ -567,7 +569,7 @@ function isUsernameValid(name) { // API -app.get("/api/nameCheck/:name", (req, res) => { +app.get("/nameCheck/:name", (req, res) => { if (!isUsernameValid(req.params.name)) { res.status(200).send({ resultCode: -2, @@ -598,7 +600,7 @@ app.get("/api/nameCheck/:name", (req, res) => { }); }); -app.post("/api/signUp", (req, res) => { +app.post("/signUp", (req, res) => { const newuser = new User({ name: req.body.name, email: req.body.email, @@ -610,20 +612,20 @@ app.post("/api/signUp", (req, res) => { return; }); -app.post("/api/updateName", (req, res) => { +app.post("/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/passwordReset", (req, res) => { +app.post("/passwordReset", (req, res) => { const email = req.body.email; //send email to the passed email requesting password reset res.sendStatus(200); }); -app.get("/api/fetchSnapshot", authenticateToken, (req, res) => { +app.get("/fetchSnapshot", authenticateToken, (req, res) => { /* Takes token and returns snap */ User.findOne({ uid: req.uid }, (err, user) => { if (err) res.status(500).send({ error: err }); @@ -644,7 +646,7 @@ function stdDev(array) { ); } -app.post("/api/testCompleted", authenticateToken, (req, res) => { +app.post("/testCompleted", authenticateToken, (req, res) => { User.findOne({ uid: req.uid }, (err, user) => { if (err) res.status(500).send({ error: err }); request = req.body; @@ -963,7 +965,7 @@ app.post("/api/testCompleted", authenticateToken, (req, res) => { }); }); -app.get("/api/userResults", authenticateToken, (req, res) => { +app.get("/userResults", authenticateToken, (req, res) => { User.findOne({ uid: req.uid }, (err, user) => { if (err) res.status(500).send({ error: err }); }); @@ -977,7 +979,7 @@ function isConfigKeyValid(name) { return /^[0-9a-zA-Z_.\-#+]+$/.test(name); } -app.post("/api/saveConfig", authenticateToken, (req, res) => { +app.post("/saveConfig", authenticateToken, (req, res) => { try { if (req.uid === undefined || req.body.obj === undefined) { console.error(`error saving config for ${req.uid} - missing input`); @@ -1059,7 +1061,7 @@ app.post("/api/saveConfig", authenticateToken, (req, res) => { } }); -app.post("/api/addPreset", authenticateToken, (req, res) => { +app.post("/addPreset", authenticateToken, (req, res) => { try { if (!isTagPresetNameValid(req.body.obj.name)) { return { resultCode: -1 }; @@ -1151,7 +1153,7 @@ app.post("/api/addPreset", authenticateToken, (req, res) => { } }); -app.post("/api/editPreset", authenticateToken, (req, res) => { +app.post("/editPreset", authenticateToken, (req, res) => { try { if (!isTagPresetNameValid(req.body.presetName)) { res.json({ resultCode: -1 }); @@ -1189,7 +1191,7 @@ app.post("/api/editPreset", authenticateToken, (req, res) => { } }); -app.post("/api/removePreset", authenticateToken, (req, res) => { +app.post("/removePreset", authenticateToken, (req, res) => { try { User.findOne({ uid: req.uid }, (err, user) => { for (i = 0; i < user.presets.length; i++) { @@ -1222,8 +1224,8 @@ function isTagPresetNameValid(name) { return /^[0-9a-zA-Z_.-]+$/.test(name); } -//could use /api/tags/add instead -app.post("/api/addTag", authenticateToken, (req, res) => { +//could use /tags/add instead +app.post("/addTag", authenticateToken, (req, res) => { try { if (!isTagPresetNameValid(req.body.tagName)) return { resultCode: -1 }; User.findOne({ uid: req.uid }, (err, user) => { @@ -1259,7 +1261,7 @@ app.post("/api/addTag", authenticateToken, (req, res) => { } }); -app.post("/api/editTag", authenticateToken, (req, res) => { +app.post("/editTag", authenticateToken, (req, res) => { try { if (!isTagPresetNameValid(req.body.tagName)) return { resultCode: -1 }; User.findOne({ uid: req.uid }, (err, user) => { @@ -1287,7 +1289,7 @@ app.post("/api/editTag", authenticateToken, (req, res) => { } }); -app.post("/api/removeTag", authenticateToken, (req, res) => { +app.post("/removeTag", authenticateToken, (req, res) => { try { User.findOne({ uid: req.uid }, (err, user) => { if (err) res.status(500).send({ error: err }); @@ -1312,7 +1314,7 @@ app.post("/api/removeTag", authenticateToken, (req, res) => { } }); -app.post("/api/resetPersonalBests", authenticateToken, (req, res) => { +app.post("/resetPersonalBests", authenticateToken, (req, res) => { try { User.findOne({ uid: req.uid }, (err, user) => { if (err) res.status(500).send({ error: err }); @@ -1389,7 +1391,7 @@ function addToLeaderboard(lb, result, username) { return lb, retData; } -app.post("/api/attemptAddToLeaderboards", authenticateToken, (req, res) => { +app.post("/attemptAddToLeaderboards", authenticateToken, (req, res) => { const result = req.body.result; let retData = {}; User.findOne({ uid: req.uid }, (err, user) => { @@ -1455,7 +1457,7 @@ app.post("/api/attemptAddToLeaderboards", authenticateToken, (req, res) => { res.status(200); }); -app.get("/api/getLeaderboard/:type/:mode/:mode2", (req, res) => { +app.get("/getLeaderboard/:type/:mode/:mode2", (req, res) => { Leaderboard.findOne( { mode: req.params.mode, mode2: req.params.mode2, type: req.params.type }, (err, lb) => { diff --git a/package-lock.json b/package-lock.json index 9499a6600..c5431c22a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,6 +18,7 @@ "cors": "^2.8.5", "express": "^4.17.1", "firebase-admin": "^9.9.0", + "helmet": "^4.6.0", "howler": "^2.2.1", "mongoose": "^5.12.12", "nodemon": "^2.0.7", @@ -7222,6 +7223,14 @@ "minimalistic-assert": "^1.0.1" } }, + "node_modules/helmet": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/helmet/-/helmet-4.6.0.tgz", + "integrity": "sha512-HVqALKZlR95ROkrnesdhbbZJFi/rIVSoNq6f3jA/9u6MIbTsPh3xZwihjeI5+DO/2sOV6HMHooXcEOuwskHpTg==", + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/hmac-drbg": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", @@ -20105,6 +20114,11 @@ "minimalistic-assert": "^1.0.1" } }, + "helmet": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/helmet/-/helmet-4.6.0.tgz", + "integrity": "sha512-HVqALKZlR95ROkrnesdhbbZJFi/rIVSoNq6f3jA/9u6MIbTsPh3xZwihjeI5+DO/2sOV6HMHooXcEOuwskHpTg==" + }, "hmac-drbg": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", diff --git a/package.json b/package.json index 10593e6f0..5842a2288 100644 --- a/package.json +++ b/package.json @@ -50,6 +50,7 @@ "cors": "^2.8.5", "express": "^4.17.1", "firebase-admin": "^9.9.0", + "helmet": "^4.6.0", "howler": "^2.2.1", "mongoose": "^5.12.12", "nodemon": "^2.0.7", diff --git a/src/js/account-controller.js b/src/js/account-controller.js index 424e89eda..6a3c34833 100644 --- a/src/js/account-controller.js +++ b/src/js/account-controller.js @@ -136,7 +136,7 @@ function signUp() { $(".pageLogin .register .button").removeClass("disabled"); return; } - axiosInstance.get(`/api/nameCheck/${nname}`).then((d) => { + axiosInstance.get(`/nameCheck/${nname}`).then((d) => { console.log(d.data); if (d.data.resultCode === -1) { Notifications.add("Name unavailable", -1); @@ -160,7 +160,7 @@ function signUp() { // dontCheckUserName = true; let usr = user.user; //maybe there's a better place for the api call - axiosInstance.post("/api/signUp", { + axiosInstance.post("/signUp", { name: nname, uid: usr.uid, email: email, diff --git a/src/js/axios-instance.js b/src/js/axios-instance.js index a74f34c2b..63487170a 100644 --- a/src/js/axios-instance.js +++ b/src/js/axios-instance.js @@ -1,7 +1,14 @@ import axios from "axios"; +let baseURL; +if (window.location.hostname === "localhost") { + baseURL = "http://localhost:5005"; +} else { + baseURL = "https://api.monkeytype.com"; +} + const axiosInstance = axios.create({ - baseURL: "http://localhost:5005", + baseURL: baseURL, }); // Request interceptor for API calls diff --git a/src/js/cloud-functions.js b/src/js/cloud-functions.js index 3d4b9d6b1..957c717df 100644 --- a/src/js/cloud-functions.js +++ b/src/js/cloud-functions.js @@ -1,5 +1,5 @@ // all functions here should be changed into api calls -//export function testCompleted = axios.post('/api/testCompleted', )firebase +//export function testCompleted = axios.post('/testCompleted', )firebase // .functions() // .httpsCallable("testCompleted"); import axiosInstance from "./axios-instance"; diff --git a/src/js/db.js b/src/js/db.js index 1e5b67473..e46b76f85 100644 --- a/src/js/db.js +++ b/src/js/db.js @@ -7,7 +7,7 @@ let dbSnapshot = null; export function updateName(uid, name) { //db.collection(`users`).doc(uid).set({ name: name }, { merge: true }); - axiosInstance.post("/api/updateName", { + axiosInstance.post("/updateName", { uid: uid, name: name, }); @@ -31,7 +31,7 @@ export async function initSnapshot() { //send api request with token that returns tags, presets, and data needed for snap if (firebase.auth().currentUser == null) return false; await axiosInstance - .get("/api/fetchSnapshot") + .get("/fetchSnapshot") .then((response) => { dbSnapshot = response.data.snap; loadTags(dbSnapshot.tags); @@ -50,7 +50,7 @@ export async function getUserResults() { return true; } else { axiosInstance - .get("/api/userResults", { + .get("/userResults", { uid: user.uid, }) .then((response) => { @@ -412,7 +412,7 @@ export async function saveConfig(config) { if (firebase.auth().currentUser !== null) { AccountButton.loading(true); axiosInstance - .post("/api/saveConfig", { + .post("/saveConfig", { obj: config, }) .then((response) => { diff --git a/src/js/elements/leaderboards.js b/src/js/elements/leaderboards.js index 86c54feb7..0198fe912 100644 --- a/src/js/elements/leaderboards.js +++ b/src/js/elements/leaderboards.js @@ -30,12 +30,8 @@ function update() { Loader.show(); Promise.all([ - axiosInstance.get( - `/api/getLeaderboard/daily/${boardinfo[0]}/${boardinfo[1]}` - ), - axiosInstance.get( - `/api/getLeaderboard/global/${boardinfo[0]}/${boardinfo[1]}` - ), + axiosInstance.get(`/getLeaderboard/daily/${boardinfo[0]}/${boardinfo[1]}`), + axiosInstance.get(`/getLeaderboard/global/${boardinfo[0]}/${boardinfo[1]}`), ]) .then((lbdata) => { Loader.hide(); diff --git a/src/js/misc.js b/src/js/misc.js index ab481af82..84c990c0a 100644 --- a/src/js/misc.js +++ b/src/js/misc.js @@ -316,7 +316,7 @@ export function sendVerificationEmail() { Loader.show(); let cu = firebase.auth().currentUser; axiosInstance - .post("/api/sendEmailVerification", {}) + .post("/sendEmailVerification", {}) .then(() => { Loader.hide(); showNotification("Email sent to " + cu.email, 4000); diff --git a/src/js/popups/edit-preset-popup.js b/src/js/popups/edit-preset-popup.js index 707f7606c..b16494ed5 100644 --- a/src/js/popups/edit-preset-popup.js +++ b/src/js/popups/edit-preset-popup.js @@ -73,7 +73,7 @@ function apply() { if (action === "add") { Loader.show(); axiosInstance - .post("/api/addPreset", { + .post("/addPreset", { obj: { name: inputVal, config: configChanges, @@ -103,7 +103,7 @@ function apply() { } else if (action === "edit") { Loader.show(); axiosInstance - .post("/api/editPreset", { + .post("/editPreset", { presetName: inputVal, presetid: presetid, config: configChanges, @@ -128,7 +128,7 @@ function apply() { } else if (action === "remove") { Loader.show(); axiosInstance - .post("/api/removePreset", { + .post("/removePreset", { presetid, }) .then((e) => { diff --git a/src/js/popups/edit-tags-popup.js b/src/js/popups/edit-tags-popup.js index 3e0efbbf9..739afe177 100644 --- a/src/js/popups/edit-tags-popup.js +++ b/src/js/popups/edit-tags-popup.js @@ -67,7 +67,7 @@ function apply() { if (action === "add") { Loader.show(); axiosInstance - .post("/api/addTag", { + .post("/addTag", { tagName: inputVal, }) .then((e) => { @@ -91,7 +91,7 @@ function apply() { } else if (action === "edit") { Loader.show(); axiosInstance - .post("/api/editTag", { + .post("/editTag", { tagName: inputVal, tagId: tagid, }) @@ -117,7 +117,7 @@ function apply() { } else if (action === "remove") { Loader.show(); axiosInstance - .post("/api/removeTag", { + .post("/removeTag", { tagId: tagid, }) .then((e) => { diff --git a/src/js/simple-popups.js b/src/js/simple-popups.js index 929fa6525..98d8fecf8 100644 --- a/src/js/simple-popups.js +++ b/src/js/simple-popups.js @@ -246,7 +246,7 @@ list.resetPersonalBests = new SimplePopup( () => { try { Loader.show(); - axiosInstance.post("/api/resetPersonalBests").then((res) => { + axiosInstance.post("/resetPersonalBests").then((res) => { if (res) { Loader.hide(); Notifications.add( diff --git a/src/js/test/test-leaderboards.js b/src/js/test/test-leaderboards.js index e26f9b771..c67e42f81 100644 --- a/src/js/test/test-leaderboards.js +++ b/src/js/test/test-leaderboards.js @@ -174,7 +174,7 @@ export async function check(completedEvent) { }) */ axiosInstance - .post("/api/attemptAddToLeaderboards", { + .post("/attemptAddToLeaderboards", { //user data can be retrieved from the database result: lbRes, }) diff --git a/src/js/test/test-logic.js b/src/js/test/test-logic.js index 30b6c7be1..22ab22e4d 100644 --- a/src/js/test/test-logic.js +++ b/src/js/test/test-logic.js @@ -1566,7 +1566,7 @@ export function finish(difficultyFailed = false) { Notifications.add("You are offline. Result not saved.", -1); } else { axiosInstance - .post("/api/testCompleted", { + .post("/testCompleted", { obj: completedEvent, }) .then((e) => {