mirror of
https://github.com/monkeytypegame/monkeytype.git
synced 2025-11-01 11:48:04 +08:00
Added Auth Router and DAO
This commit is contained in:
parent
3df8610735
commit
4c560a437f
12 changed files with 229 additions and 133 deletions
9
.gitignore
vendored
9
.gitignore
vendored
|
|
@ -71,6 +71,8 @@ node_modules/
|
|||
.vscode
|
||||
*.code-workspace
|
||||
|
||||
.idea
|
||||
|
||||
#firebase
|
||||
functions/serviceAccountKey.json
|
||||
functions/serviceAccountKey_live.json
|
||||
|
|
@ -78,8 +80,6 @@ functions/serviceAccountKey_copy.json
|
|||
functions/serviceAccountKey_live_copy.json
|
||||
.firebaserc
|
||||
.firebaserc_copy
|
||||
functions/serviceAccountKey_copy.json
|
||||
functions/serviceAccountKey_live_copy.json
|
||||
|
||||
#generated files
|
||||
dist/
|
||||
|
|
@ -87,4 +87,7 @@ dist/
|
|||
#cloudflare y
|
||||
.cloudflareKey.txt
|
||||
.cloudflareKey_copy.txt
|
||||
purgeCfCache.sh
|
||||
purgeCfCache.sh
|
||||
|
||||
backend/credentials
|
||||
backend/.env
|
||||
|
|
|
|||
5
.idea/inspectionProfiles/Project_Default.xml
generated
5
.idea/inspectionProfiles/Project_Default.xml
generated
|
|
@ -1,6 +1,11 @@
|
|||
<component name="InspectionProjectProfileManager">
|
||||
<profile version="1.0">
|
||||
<option name="myName" value="Project Default" />
|
||||
<inspection_tool class="DuplicatedCode" enabled="true" level="WEAK WARNING" enabled_by_default="true">
|
||||
<Languages>
|
||||
<language minSize="55" name="JavaScript" />
|
||||
</Languages>
|
||||
</inspection_tool>
|
||||
<inspection_tool class="Eslint" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
</profile>
|
||||
</component>
|
||||
37
backend/api/controllers/auth.js
Normal file
37
backend/api/controllers/auth.js
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
import UsersDAO from "../../dao/usersDAO";
|
||||
import { isUsernameValid } from "../../handlers/validation";
|
||||
|
||||
class AuthController {
|
||||
static async createNewUser(req, res, next) {
|
||||
try {
|
||||
const { name, email, uid } = req.body;
|
||||
await UsersDAO.addUser(name, email, uid);
|
||||
return res.sendStatus(200);
|
||||
} catch (e) {
|
||||
return next(e);
|
||||
}
|
||||
}
|
||||
|
||||
static async updateName(req, res, next) {
|
||||
try {
|
||||
const { name } = req.body;
|
||||
if (!isUsernameValid(name)) return next("Username unavailable!");
|
||||
await UsersDAO.updateName();
|
||||
return res.sendStatus(200);
|
||||
} catch (e) {
|
||||
return next(e);
|
||||
}
|
||||
}
|
||||
|
||||
static async getUser(req, res, next) {
|
||||
try {
|
||||
const { uid } = req.decodedToken;
|
||||
const userInfo = await UsersDAO.getUser(uid);
|
||||
return res.status(200).json(userInfo);
|
||||
} catch (e) {
|
||||
return next(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = AuthController;
|
||||
14
backend/api/routes/auth.js
Normal file
14
backend/api/routes/auth.js
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
import { authenticateRequest } from "../../middlewares/auth";
|
||||
|
||||
const { Router } = require("express");
|
||||
import AuthCtrl from "../controllers/auth";
|
||||
|
||||
const router = Router();
|
||||
|
||||
router.post("/signup", AuthCtrl.createNewUser);
|
||||
|
||||
router.post("/update/name", authenticateRequest, AuthCtrl.updateName);
|
||||
|
||||
router.get("/user", authenticateRequest, AuthCtrl.getUser);
|
||||
|
||||
module.exports = router;
|
||||
26
backend/dao/usersDAO.js
Normal file
26
backend/dao/usersDAO.js
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
const { mongoDB } = require("../init/mongodb");
|
||||
class UsersDAO {
|
||||
static async addUser(name, email, uid) {
|
||||
return await mongoDB()
|
||||
.collection("users")
|
||||
.insertOne({ name, email, uid, addedAt: Date.now() });
|
||||
}
|
||||
|
||||
static async updateName(uid, name) {
|
||||
const nameDoc = await mongoDB()
|
||||
.collection("users")
|
||||
.findOne({ name: { $regex: new RegExp(`^${name}$`, "i") } });
|
||||
if (nameDoc) throw new Error("Username already taken");
|
||||
return await mongoDB()
|
||||
.collection("users")
|
||||
.updateOne({ uid }, { $set: { name } });
|
||||
}
|
||||
|
||||
static async getUser(uid) {
|
||||
const user = await mongoDB().collection("users").findOne({ uid });
|
||||
if (!user) throw new Error("User not found");
|
||||
return user;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = UsersDAO;
|
||||
7
backend/handlers/auth.js
Normal file
7
backend/handlers/auth.js
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
const admin = require("firebase-admin");
|
||||
|
||||
module.exports = {
|
||||
async verifyIdToken(idToken) {
|
||||
return await admin.auth().verifyIdToken(idToken);
|
||||
},
|
||||
};
|
||||
10
backend/handlers/validation.js
Normal file
10
backend/handlers/validation.js
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
module.exports = {
|
||||
isUsernameValid(name) {
|
||||
if (name === null || name === undefined || name === "") return false;
|
||||
if (/miodec/.test(name.toLowerCase())) return false;
|
||||
if (/bitly/.test(name.toLowerCase())) return false;
|
||||
if (name.length > 14) return false;
|
||||
if (/^\..*/.test(name.toLowerCase())) return false;
|
||||
return /^[0-9a-zA-Z_.-]+$/.test(name);
|
||||
},
|
||||
};
|
||||
22
backend/init/mongodb.js
Normal file
22
backend/init/mongodb.js
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
const { MongoClient } = require("mongodb");
|
||||
|
||||
let mongoClient;
|
||||
|
||||
module.exports = {
|
||||
async connectDB() {
|
||||
return MongoClient.connect(process.env.DB_URI, {
|
||||
useNewUrlParser: true,
|
||||
useUnifiedTopology: true,
|
||||
})
|
||||
.then((client) => {
|
||||
mongoClient = client;
|
||||
})
|
||||
.catch((e) => {
|
||||
console.log(e);
|
||||
process.exit(1);
|
||||
});
|
||||
},
|
||||
mongoDB() {
|
||||
return mongoClient.db(process.env.DB_NAME);
|
||||
},
|
||||
};
|
||||
16
backend/middlewares/auth.js
Normal file
16
backend/middlewares/auth.js
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
const { verifyIdToken } = require("../handlers/auth");
|
||||
|
||||
module.exports = {
|
||||
async authenticateRequest(req, res, next) {
|
||||
try {
|
||||
const { authorization } = req.headers;
|
||||
if (!authorization) return next("Unauthorized");
|
||||
const token = authorization.split(" ");
|
||||
if (token[0] !== "Bearer ") return next("Invalid token");
|
||||
req.decodedToken = await verifyIdToken(token[1]);
|
||||
return next();
|
||||
} catch (e) {
|
||||
return next(e);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
|
@ -1,6 +1,10 @@
|
|||
const express = require("express");
|
||||
const { config } = require("dotenv");
|
||||
const path = require("path");
|
||||
config({ path: path.join(__dirname, ".env") });
|
||||
const bodyParser = require("body-parser");
|
||||
const mongoose = require("mongoose");
|
||||
const { MongoClient } = require("mongodb");
|
||||
const cors = require("cors");
|
||||
const admin = require("firebase-admin");
|
||||
const helmet = require("helmet");
|
||||
|
|
@ -11,7 +15,8 @@ const { Stats } = require("./models/stats");
|
|||
|
||||
// Firebase admin setup
|
||||
//currently uses account key in functions to prevent repetition
|
||||
const serviceAccount = require("../functions/serviceAccountKey.json");
|
||||
const serviceAccount = require("./credentials/serviceAccountKey.json");
|
||||
const { connectDB } = require("./init/mongodb");
|
||||
|
||||
admin.initializeApp({
|
||||
credential: admin.credential.cert(serviceAccount),
|
||||
|
|
@ -19,19 +24,24 @@ admin.initializeApp({
|
|||
|
||||
// MIDDLEWARE & SETUP
|
||||
const app = express();
|
||||
app.use(express.urlencoded({ extended: true }));
|
||||
app.use(express.json());
|
||||
app.use(cors());
|
||||
app.use(helmet());
|
||||
|
||||
const port = process.env.PORT || "5005";
|
||||
|
||||
mongoose.connect("mongodb://localhost:27017/monkeytype", {
|
||||
useNewUrlParser: true,
|
||||
useUnifiedTopology: true,
|
||||
});
|
||||
|
||||
const mtRootDir = __dirname.substring(0, __dirname.length - 8); //will this work for windows and mac computers?
|
||||
app.use(express.static(mtRootDir + "/dist"));
|
||||
app.use(bodyParser.json());
|
||||
connectDB()
|
||||
.then(() => {
|
||||
app.listen(process.env.PORT, () => {
|
||||
console.log(`listening on port ${process.env.PORT}`);
|
||||
});
|
||||
})
|
||||
.catch((e) => {
|
||||
console.log(e);
|
||||
});
|
||||
const authRouter = require("./api/routes/auth");
|
||||
app.use("/auth", authRouter);
|
||||
|
||||
// Daily leaderboard clear function
|
||||
function clearDailyLeaderboards() {
|
||||
|
|
@ -351,10 +361,10 @@ async function checkIfTagPB(obj, userdata) {
|
|||
if (restags.includes(doc._id.toString())) {
|
||||
//not sure what this is supposed to do
|
||||
/*
|
||||
let data = doc.data();
|
||||
data.id = doc.id;
|
||||
dbtags.push(data);
|
||||
*/
|
||||
let data = doc.data();
|
||||
data.id = doc.id;
|
||||
dbtags.push(data);
|
||||
*/
|
||||
dbtags.push(doc);
|
||||
}
|
||||
});
|
||||
|
|
@ -518,7 +528,6 @@ function incrementT60Bananas(uid, result, userData) {
|
|||
|
||||
if (best60 != undefined && result.wpm < best60 - best60 * 0.25) {
|
||||
// console.log("returning");
|
||||
return;
|
||||
} else {
|
||||
//increment
|
||||
// console.log("checking");
|
||||
|
|
@ -608,88 +617,8 @@ function isTagPresetNameValid(name) {
|
|||
return /^[0-9a-zA-Z_.-]+$/.test(name);
|
||||
}
|
||||
|
||||
function isUsernameValid(name) {
|
||||
if (name === null || name === undefined || name === "") return false;
|
||||
if (/miodec/.test(name.toLowerCase())) return false;
|
||||
if (/bitly/.test(name.toLowerCase())) return false;
|
||||
if (name.length > 14) return false;
|
||||
if (/^\..*/.test(name.toLowerCase())) return false;
|
||||
return /^[0-9a-zA-Z_.-]+$/.test(name);
|
||||
}
|
||||
|
||||
// API
|
||||
|
||||
app.get("/nameCheck/:name", (req, res) => {
|
||||
if (!isUsernameValid(req.params.name)) {
|
||||
res.status(200).send({
|
||||
resultCode: -2,
|
||||
message: "Username is not valid",
|
||||
});
|
||||
return;
|
||||
}
|
||||
User.findOne({ name: req.params.name }, (err, user) => {
|
||||
console.log(err);
|
||||
if (user) {
|
||||
res.status(200).send({
|
||||
resultCode: -1,
|
||||
message: "Username is taken",
|
||||
});
|
||||
return;
|
||||
} else {
|
||||
res.status(200).send({
|
||||
resultCode: 1,
|
||||
message: "Username is available",
|
||||
});
|
||||
return;
|
||||
}
|
||||
}).catch(() => {
|
||||
res.status(200).send({
|
||||
resultCode: -1,
|
||||
message: "Error when checking for names",
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
app.post("/signUp", (req, res) => {
|
||||
const newuser = new User({
|
||||
name: req.body.name,
|
||||
email: req.body.email,
|
||||
uid: req.body.uid,
|
||||
});
|
||||
newuser.save();
|
||||
res.status(200);
|
||||
res.json({ user: newuser });
|
||||
return;
|
||||
});
|
||||
|
||||
app.post("/updateName", authenticateToken, (req, res) => {
|
||||
if (isUsernameValid(name)) {
|
||||
User.findOne({ uid: req.uid }, (err, user) => {
|
||||
User.findOne({ name: req.body.name }, (err2, user2) => {
|
||||
if (!user2) {
|
||||
user.name = req.body.name;
|
||||
user.save();
|
||||
res.status(200).send({ status: 1 });
|
||||
} else {
|
||||
res.status(200).send({ status: -1, message: "Username taken" });
|
||||
}
|
||||
});
|
||||
});
|
||||
} else {
|
||||
res.status(200).send({ status: -1, message: "Username invalid" });
|
||||
}
|
||||
});
|
||||
|
||||
app.get("/fetchSnapshot", authenticateToken, (req, res) => {
|
||||
User.findOne({ uid: req.uid }, (err, user) => {
|
||||
if (err) res.status(500).send({ error: err });
|
||||
if (!user) res.status(200).send({ message: "No user found" }); //client doesn't do anything with this
|
||||
let snap = user;
|
||||
res.send({ snap: snap });
|
||||
return;
|
||||
});
|
||||
});
|
||||
|
||||
function stdDev(array) {
|
||||
const n = array.length;
|
||||
const mean = array.reduce((a, b) => a + b) / n;
|
||||
|
|
@ -740,6 +669,7 @@ app.post("/testCompleted", authenticateToken, (req, res) => {
|
|||
}
|
||||
return errCount;
|
||||
}
|
||||
|
||||
let errCount = verifyValue(obj);
|
||||
if (errCount > 0) {
|
||||
console.error(
|
||||
|
|
@ -960,13 +890,13 @@ app.post("/testCompleted", authenticateToken, (req, res) => {
|
|||
`saved result for ${req.uid} (new PB) - ${JSON.stringify(logobj)}`
|
||||
);
|
||||
/*
|
||||
User.findOne({ name: userdata.name }, (err, user2) => {
|
||||
console.log(user2.results[user2.results.length-1])
|
||||
console.log(user2.results[user2.results.length-1]).isPb
|
||||
user2.results[user2.results.length-1].isPb = true;
|
||||
user2.save();
|
||||
})
|
||||
*/
|
||||
User.findOne({ name: userdata.name }, (err, user2) => {
|
||||
console.log(user2.results[user2.results.length-1])
|
||||
console.log(user2.results[user2.results.length-1]).isPb
|
||||
user2.results[user2.results.length-1].isPb = true;
|
||||
user2.save();
|
||||
})
|
||||
*/
|
||||
request.obj.isPb = true;
|
||||
if (
|
||||
obj.mode === "time" &&
|
||||
|
|
@ -994,7 +924,6 @@ app.post("/testCompleted", authenticateToken, (req, res) => {
|
|||
}
|
||||
stripAndSave(req.uid, request.obj);
|
||||
res.status(200).send(returnobj);
|
||||
return;
|
||||
})
|
||||
.catch((e) => {
|
||||
console.error(
|
||||
|
|
@ -1003,7 +932,6 @@ app.post("/testCompleted", authenticateToken, (req, res) => {
|
|||
res
|
||||
.status(200)
|
||||
.send({ data: { resultCode: -999, message: e.message } });
|
||||
return;
|
||||
});
|
||||
} catch (e) {
|
||||
console.error(
|
||||
|
|
@ -1012,7 +940,6 @@ app.post("/testCompleted", authenticateToken, (req, res) => {
|
|||
)} - ${e}`
|
||||
);
|
||||
res.status(200).send({ resultCode: -999, message: e.message });
|
||||
return;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
@ -1041,7 +968,6 @@ app.post("/clearTagPb", authenticateToken, (req, res) => {
|
|||
resultCode: -999,
|
||||
message: e.message,
|
||||
});
|
||||
return;
|
||||
});
|
||||
res.sendStatus(200);
|
||||
});
|
||||
|
|
@ -1062,21 +988,18 @@ app.post("/unlinkDiscord", authenticateToken, (req, res) => {
|
|||
status: 1,
|
||||
message: "Unlinked",
|
||||
});
|
||||
return;
|
||||
})
|
||||
.catch((e) => {
|
||||
res.status(200).send({
|
||||
status: -999,
|
||||
message: e.message,
|
||||
});
|
||||
return;
|
||||
});
|
||||
} catch (e) {
|
||||
res.status(200).send({
|
||||
status: -999,
|
||||
message: e,
|
||||
});
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -1538,7 +1461,6 @@ app.post("/verifyDiscord", authenticateToken, (req, res) => {
|
|||
message:
|
||||
"This Discord account is already paired to a different Monkeytype account",
|
||||
});
|
||||
return;
|
||||
} else {
|
||||
User.findOne({ uid: req.uid }, (err, user2) => {
|
||||
user2.discordId = did;
|
||||
|
|
@ -1553,7 +1475,6 @@ app.post("/verifyDiscord", authenticateToken, (req, res) => {
|
|||
res
|
||||
.status(200)
|
||||
.send({ status: 1, message: "Verified", did: did });
|
||||
return;
|
||||
});
|
||||
}
|
||||
});
|
||||
|
|
@ -1564,11 +1485,9 @@ app.post("/verifyDiscord", authenticateToken, (req, res) => {
|
|||
e.message
|
||||
);
|
||||
response.status(200).send({ status: -1, message: e.message });
|
||||
return;
|
||||
});
|
||||
} catch (e) {
|
||||
response.status(200).send({ status: -1, message: e });
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -1678,10 +1597,10 @@ app.post("/attemptAddToLeaderboards", authenticateToken, (req, res) => {
|
|||
return;
|
||||
}
|
||||
/*
|
||||
if (user.verified === false) {
|
||||
res.status(200).send({ needsToVerify: true });
|
||||
return;
|
||||
}*/
|
||||
if (user.verified === false) {
|
||||
res.status(200).send({ needsToVerify: true });
|
||||
return;
|
||||
}*/
|
||||
Leaderboard.find(
|
||||
{
|
||||
mode: result.mode,
|
||||
|
|
@ -1781,7 +1700,6 @@ app.get("/getUserDiscordData/:uid", botAuth, (req, res) => {
|
|||
//for announceDailyLbResult
|
||||
User.findOne({ uid: req.body.uid }, (err, user) => {
|
||||
res.send({ name: user.name, discordId: user.discordId });
|
||||
return;
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -1790,10 +1708,8 @@ app.get("/getUserPbs/:discordId", botAuth, (req, res) => {
|
|||
User.findOne({ discordId: req.params.discordId }, (err, user) => {
|
||||
if (user) {
|
||||
res.send({ personalBests: user.personalBests });
|
||||
return;
|
||||
} else {
|
||||
res.send({ error: "No user found with that id" });
|
||||
return;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
@ -1803,10 +1719,8 @@ app.get("/getUserPbsByUid/:uid", botAuth, (req, res) => {
|
|||
User.findOne({ uid: req.params.uid }, (err, user) => {
|
||||
if (user) {
|
||||
res.send({ personalBests: user.personalBests });
|
||||
return;
|
||||
} else {
|
||||
res.send({ error: "No user found with that id" });
|
||||
return;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
@ -1821,7 +1735,6 @@ app.get("/getTimeLeaderboard/:mode2/:type", botAuth, (req, res) => {
|
|||
//get top 10 leaderboard
|
||||
lb.board.length = 10;
|
||||
res.send({ board: lb.board });
|
||||
return;
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -1833,7 +1746,6 @@ app.get("/getUserByDiscordId/:discordId", botAuth, (req, res) => {
|
|||
} else {
|
||||
res.send({ error: "No user found with that id" });
|
||||
}
|
||||
return;
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -1848,7 +1760,6 @@ app.get("/getRecentScore/:discordId", botAuth, (req, res) => {
|
|||
} else {
|
||||
res.send({ error: "No user found with that id" });
|
||||
}
|
||||
return;
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -1860,7 +1771,6 @@ app.get("/getUserStats/:discordId", botAuth, (req, res) => {
|
|||
} else {
|
||||
res.send({ error: "No user found with that id" });
|
||||
}
|
||||
return;
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
|||
53
package-lock.json
generated
53
package-lock.json
generated
|
|
@ -3607,6 +3607,11 @@
|
|||
"is-obj": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"dotenv": {
|
||||
"version": "10.0.0",
|
||||
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-10.0.0.tgz",
|
||||
"integrity": "sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q=="
|
||||
},
|
||||
"duplexer2": {
|
||||
"version": "0.1.4",
|
||||
"resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz",
|
||||
|
|
@ -7504,9 +7509,9 @@
|
|||
"integrity": "sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ=="
|
||||
},
|
||||
"mongodb": {
|
||||
"version": "3.6.8",
|
||||
"resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.6.8.tgz",
|
||||
"integrity": "sha512-sDjJvI73WjON1vapcbyBD3Ao9/VN3TKYY8/QX9EPbs22KaCSrQ5rXo5ZZd44tWJ3wl3FlnrFZ+KyUtNH6+1ZPQ==",
|
||||
"version": "3.6.9",
|
||||
"resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.6.9.tgz",
|
||||
"integrity": "sha512-1nSCKgSunzn/CXwgOWgbPHUWOO5OfERcuOWISmqd610jn0s8BU9K4879iJVabqgpPPbA6hO7rG48eq+fGED3Mg==",
|
||||
"requires": {
|
||||
"bl": "^2.2.1",
|
||||
"bson": "^1.1.4",
|
||||
|
|
@ -7533,6 +7538,21 @@
|
|||
"safe-buffer": "5.2.1",
|
||||
"sift": "13.5.2",
|
||||
"sliced": "1.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"mongodb": {
|
||||
"version": "3.6.8",
|
||||
"resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.6.8.tgz",
|
||||
"integrity": "sha512-sDjJvI73WjON1vapcbyBD3Ao9/VN3TKYY8/QX9EPbs22KaCSrQ5rXo5ZZd44tWJ3wl3FlnrFZ+KyUtNH6+1ZPQ==",
|
||||
"requires": {
|
||||
"bl": "^2.2.1",
|
||||
"bson": "^1.1.4",
|
||||
"denque": "^1.4.1",
|
||||
"optional-require": "^1.0.3",
|
||||
"safe-buffer": "^5.1.2",
|
||||
"saslprep": "^1.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"mongoose-legacy-pluralize": {
|
||||
|
|
@ -8316,6 +8336,30 @@
|
|||
"integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=",
|
||||
"dev": true
|
||||
},
|
||||
"path": {
|
||||
"version": "0.12.7",
|
||||
"resolved": "https://registry.npmjs.org/path/-/path-0.12.7.tgz",
|
||||
"integrity": "sha1-1NwqUGxM4hl+tIHr/NWzbAFAsQ8=",
|
||||
"requires": {
|
||||
"process": "^0.11.1",
|
||||
"util": "^0.10.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"inherits": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
|
||||
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
|
||||
},
|
||||
"util": {
|
||||
"version": "0.10.4",
|
||||
"resolved": "https://registry.npmjs.org/util/-/util-0.10.4.tgz",
|
||||
"integrity": "sha512-0Pm9hTQ3se5ll1XihRic3FDIku70C+iHUdT/W926rSgHV5QgXsYbKZN8MSC3tJtSkhuROzvsQjAaFENRXr+19A==",
|
||||
"requires": {
|
||||
"inherits": "2.0.3"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"path-browserify": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz",
|
||||
|
|
@ -8593,8 +8637,7 @@
|
|||
"process": {
|
||||
"version": "0.11.10",
|
||||
"resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz",
|
||||
"integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=",
|
||||
"dev": true
|
||||
"integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI="
|
||||
},
|
||||
"process-nextick-args": {
|
||||
"version": "2.0.1",
|
||||
|
|
|
|||
|
|
@ -48,12 +48,15 @@
|
|||
"chartjs-plugin-annotation": "^0.5.7",
|
||||
"chartjs-plugin-trendline": "^0.2.2",
|
||||
"cors": "^2.8.5",
|
||||
"dotenv": "^10.0.0",
|
||||
"express": "^4.17.1",
|
||||
"firebase-admin": "^9.9.0",
|
||||
"helmet": "^4.6.0",
|
||||
"howler": "^2.2.1",
|
||||
"mongodb": "^3.6.9",
|
||||
"mongoose": "^5.12.12",
|
||||
"nodemon": "^2.0.7",
|
||||
"path": "^0.12.7",
|
||||
"tinycolor2": "^1.4.2"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue