mirror of
https://github.com/monkeytypegame/monkeytype.git
synced 2025-02-02 12:00:10 +08:00
Convert UserDAO to typescript & rename to UserDAL (#2821)
* Started conversion to typescript * Some more fixy fixy * Final fixy fixy or at least that is what it seems * Even more fixy fixy * Add verifed = false upon usre creation * Added return type * Removed extra data * Fixes * Removed class syntax of UserDAO * Name fix * Renamed UserDAO to UserDAL * Fixed errors * Fixed some problems * Fixed pb bug * Strict Equality * Var name change * Var name change
This commit is contained in:
parent
4b3cc55c17
commit
f07a6dd656
10 changed files with 645 additions and 475 deletions
|
@ -1,6 +1,6 @@
|
|||
import _ from "lodash";
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
import UserDAO from "../../dao/user";
|
||||
import { getUser, updateQuoteRatings } from "../../dao/user";
|
||||
import ReportDAO from "../../dao/report";
|
||||
import NewQuotesDao from "../../dao/new-quotes";
|
||||
import QuoteRatingsDAO from "../../dao/quote-ratings";
|
||||
|
@ -19,7 +19,7 @@ export async function getQuotes(
|
|||
req: MonkeyTypes.Request
|
||||
): Promise<MonkeyResponse> {
|
||||
const { uid } = req.ctx.decodedToken;
|
||||
let { quoteMod } = await UserDAO.getUser(uid);
|
||||
let quoteMod: boolean | undefined | string = (await getUser(uid)).quoteMod;
|
||||
if (quoteMod === true) quoteMod = "all";
|
||||
const data = await NewQuotesDao.get(quoteMod);
|
||||
return new MonkeyResponse("Quote submissions retrieved", data);
|
||||
|
@ -43,7 +43,7 @@ export async function approveQuote(
|
|||
const { uid } = req.ctx.decodedToken;
|
||||
const { quoteId, editText, editSource } = req.body;
|
||||
|
||||
const { name } = await UserDAO.getUser(uid);
|
||||
const { name } = await getUser(uid);
|
||||
|
||||
const data = await NewQuotesDao.approve(quoteId, editText, editSource, name);
|
||||
Logger.logToDb("system_quote_approved", data, uid);
|
||||
|
@ -79,7 +79,7 @@ export async function submitRating(
|
|||
const { uid } = req.ctx.decodedToken;
|
||||
const { quoteId, rating, language } = req.body;
|
||||
|
||||
const user = await UserDAO.getUser(uid);
|
||||
const user = await getUser(uid);
|
||||
if (!user) {
|
||||
throw new MonkeyError(401, "User not found.");
|
||||
}
|
||||
|
@ -107,7 +107,7 @@ export async function submitRating(
|
|||
Object
|
||||
);
|
||||
|
||||
await UserDAO.updateQuoteRatings(uid, userQuoteRatings);
|
||||
await updateQuoteRatings(uid, userQuoteRatings);
|
||||
|
||||
const responseMessage = `Rating ${
|
||||
shouldUpdateRating ? "updated" : "submitted"
|
||||
|
|
|
@ -1,5 +1,11 @@
|
|||
import ResultDAO from "../../dao/result";
|
||||
import UserDAO from "../../dao/user";
|
||||
import {
|
||||
getUser,
|
||||
checkIfPb,
|
||||
checkIfTagPb,
|
||||
incrementBananas,
|
||||
updateTypingStats,
|
||||
} from "../../dao/user";
|
||||
import PublicStatsDAO from "../../dao/public-stats";
|
||||
import BotDAO from "../../dao/bot";
|
||||
import { roundTo2, stdDev } from "../../utils/misc";
|
||||
|
@ -192,7 +198,7 @@ export async function addResult(
|
|||
//
|
||||
}
|
||||
|
||||
const user = await UserDAO.getUser(uid);
|
||||
const user = await getUser(uid);
|
||||
|
||||
//check keyspacing and duration here for bots
|
||||
if (
|
||||
|
@ -239,8 +245,8 @@ export async function addResult(
|
|||
|
||||
if (!result.bailedOut) {
|
||||
[isPb, tagPbs] = await Promise.all([
|
||||
UserDAO.checkIfPb(uid, user, result),
|
||||
UserDAO.checkIfTagPb(uid, user, result),
|
||||
checkIfPb(uid, user, result),
|
||||
checkIfTagPb(uid, user, result),
|
||||
]);
|
||||
}
|
||||
|
||||
|
@ -249,7 +255,7 @@ export async function addResult(
|
|||
}
|
||||
|
||||
if (result.mode === "time" && String(result.mode2) === "60") {
|
||||
UserDAO.incrementBananas(uid, result.wpm);
|
||||
incrementBananas(uid, result.wpm);
|
||||
if (isPb && user.discordId) {
|
||||
if (useRedisForBotTasks) {
|
||||
George.updateDiscordRole(user.discordId, result.wpm);
|
||||
|
@ -273,7 +279,7 @@ export async function addResult(
|
|||
afk = 0;
|
||||
}
|
||||
tt = result.testDuration + result.incompleteTestSeconds - afk;
|
||||
UserDAO.updateTypingStats(uid, result.restartCount, tt);
|
||||
updateTypingStats(uid, result.restartCount, tt);
|
||||
PublicStatsDAO.updateStats(result.restartCount, tt);
|
||||
|
||||
if (result.bailedOut === false) delete result.bailedOut;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import UsersDAO from "../../dao/user";
|
||||
import * as UserDAL from "../../dao/user";
|
||||
import BotDAO from "../../dao/bot";
|
||||
import MonkeyError from "../../utils/error";
|
||||
import Logger from "../../utils/logger";
|
||||
|
@ -13,7 +13,7 @@ export async function createNewUser(
|
|||
const { name } = req.body;
|
||||
const { email, uid } = req.ctx.decodedToken;
|
||||
|
||||
await UsersDAO.addUser(name, email, uid);
|
||||
await UserDAL.addUser(name, email, uid);
|
||||
Logger.logToDb("user_created", `${name} ${email}`, uid);
|
||||
|
||||
return new MonkeyResponse("User created");
|
||||
|
@ -24,8 +24,8 @@ export async function deleteUser(
|
|||
): Promise<MonkeyResponse> {
|
||||
const { uid } = req.ctx.decodedToken;
|
||||
|
||||
const userInfo = await UsersDAO.getUser(uid);
|
||||
await UsersDAO.deleteUser(uid);
|
||||
const userInfo = await UserDAL.getUser(uid);
|
||||
await UserDAL.deleteUser(uid);
|
||||
Logger.logToDb("user_deleted", `${userInfo.email} ${userInfo.name}`, uid);
|
||||
|
||||
return new MonkeyResponse("User deleted");
|
||||
|
@ -37,8 +37,8 @@ export async function updateName(
|
|||
const { uid } = req.ctx.decodedToken;
|
||||
const { name } = req.body;
|
||||
|
||||
const oldUser = await UsersDAO.getUser(uid);
|
||||
await UsersDAO.updateName(uid, name);
|
||||
const oldUser = await UserDAL.getUser(uid);
|
||||
await UserDAL.updateName(uid, name);
|
||||
Logger.logToDb(
|
||||
"user_name_updated",
|
||||
`changed name from ${oldUser.name} to ${name}`,
|
||||
|
@ -53,7 +53,7 @@ export async function clearPb(
|
|||
): Promise<MonkeyResponse> {
|
||||
const { uid } = req.ctx.decodedToken;
|
||||
|
||||
await UsersDAO.clearPb(uid);
|
||||
await UserDAL.clearPb(uid);
|
||||
Logger.logToDb("user_cleared_pbs", "", uid);
|
||||
|
||||
return new MonkeyResponse("User's PB cleared");
|
||||
|
@ -64,7 +64,7 @@ export async function checkName(
|
|||
): Promise<MonkeyResponse> {
|
||||
const { name } = req.params;
|
||||
|
||||
const available = await UsersDAO.isNameAvailable(name);
|
||||
const available = await UserDAL.isNameAvailable(name);
|
||||
if (!available) {
|
||||
throw new MonkeyError(409, "Username unavailable");
|
||||
}
|
||||
|
@ -79,7 +79,7 @@ export async function updateEmail(
|
|||
const { newEmail } = req.body;
|
||||
|
||||
try {
|
||||
await UsersDAO.updateEmail(uid, newEmail);
|
||||
await UserDAL.updateEmail(uid, newEmail);
|
||||
} catch (e) {
|
||||
throw new MonkeyError(404, e.message, "update email", uid);
|
||||
}
|
||||
|
@ -96,10 +96,10 @@ export async function getUser(
|
|||
|
||||
let userInfo;
|
||||
try {
|
||||
userInfo = await UsersDAO.getUser(uid);
|
||||
userInfo = await UserDAL.getUser(uid);
|
||||
} catch (e) {
|
||||
if (email && uid) {
|
||||
userInfo = await UsersDAO.addUser(undefined, email, uid);
|
||||
userInfo = await UserDAL.addUser(undefined, email, uid);
|
||||
} else {
|
||||
throw new MonkeyError(
|
||||
404,
|
||||
|
@ -126,7 +126,7 @@ export async function linkDiscord(
|
|||
|
||||
const useRedisForBotTasks = req.ctx.configuration.useRedisForBotTasks.enabled;
|
||||
|
||||
const userInfo = await UsersDAO.getUser(uid);
|
||||
const userInfo = await UserDAL.getUser(uid);
|
||||
if (userInfo.discordId) {
|
||||
throw new MonkeyError(
|
||||
409,
|
||||
|
@ -147,7 +147,7 @@ export async function linkDiscord(
|
|||
);
|
||||
}
|
||||
|
||||
const discordIdAvailable = await UsersDAO.isDiscordIdAvailable(discordId);
|
||||
const discordIdAvailable = await UserDAL.isDiscordIdAvailable(discordId);
|
||||
if (!discordIdAvailable) {
|
||||
throw new MonkeyError(
|
||||
409,
|
||||
|
@ -155,7 +155,7 @@ export async function linkDiscord(
|
|||
);
|
||||
}
|
||||
|
||||
await UsersDAO.linkDiscord(uid, discordId);
|
||||
await UserDAL.linkDiscord(uid, discordId);
|
||||
|
||||
if (useRedisForBotTasks) {
|
||||
George.linkDiscord(discordId, uid);
|
||||
|
@ -173,7 +173,7 @@ export async function unlinkDiscord(
|
|||
|
||||
const useRedisForBotTasks = req.ctx.configuration.useRedisForBotTasks.enabled;
|
||||
|
||||
const userInfo = await UsersDAO.getUser(uid);
|
||||
const userInfo = await UserDAL.getUser(uid);
|
||||
if (!userInfo.discordId) {
|
||||
throw new MonkeyError(404, "User does not have a linked Discord account");
|
||||
}
|
||||
|
@ -183,7 +183,7 @@ export async function unlinkDiscord(
|
|||
}
|
||||
await BotDAO.unlinkDiscord(uid, userInfo.discordId);
|
||||
|
||||
await UsersDAO.unlinkDiscord(uid);
|
||||
await UserDAL.unlinkDiscord(uid);
|
||||
Logger.logToDb("user_discord_unlinked", userInfo.discordId, uid);
|
||||
|
||||
return new MonkeyResponse("Discord account unlinked");
|
||||
|
@ -195,7 +195,7 @@ export async function addTag(
|
|||
const { uid } = req.ctx.decodedToken;
|
||||
const { tagName } = req.body;
|
||||
|
||||
const tag = await UsersDAO.addTag(uid, tagName);
|
||||
const tag = await UserDAL.addTag(uid, tagName);
|
||||
return new MonkeyResponse("Tag updated", tag);
|
||||
}
|
||||
|
||||
|
@ -205,7 +205,7 @@ export async function clearTagPb(
|
|||
const { uid } = req.ctx.decodedToken;
|
||||
const { tagId } = req.params;
|
||||
|
||||
await UsersDAO.removeTagPb(uid, tagId);
|
||||
await UserDAL.removeTagPb(uid, tagId);
|
||||
return new MonkeyResponse("Tag PB cleared");
|
||||
}
|
||||
|
||||
|
@ -215,7 +215,7 @@ export async function editTag(
|
|||
const { uid } = req.ctx.decodedToken;
|
||||
const { tagId, newName } = req.body;
|
||||
|
||||
await UsersDAO.editTag(uid, tagId, newName);
|
||||
await UserDAL.editTag(uid, tagId, newName);
|
||||
return new MonkeyResponse("Tag updated");
|
||||
}
|
||||
|
||||
|
@ -225,7 +225,7 @@ export async function removeTag(
|
|||
const { uid } = req.ctx.decodedToken;
|
||||
const { tagId } = req.params;
|
||||
|
||||
await UsersDAO.removeTag(uid, tagId);
|
||||
await UserDAL.removeTag(uid, tagId);
|
||||
return new MonkeyResponse("Tag deleted");
|
||||
}
|
||||
|
||||
|
@ -234,7 +234,7 @@ export async function getTags(
|
|||
): Promise<MonkeyResponse> {
|
||||
const { uid } = req.ctx.decodedToken;
|
||||
|
||||
const tags = await UsersDAO.getTags(uid);
|
||||
const tags = await UserDAL.getTags(uid);
|
||||
return new MonkeyResponse("Tags retrieved", tags ?? []);
|
||||
}
|
||||
|
||||
|
@ -242,9 +242,10 @@ export async function updateLbMemory(
|
|||
req: MonkeyTypes.Request
|
||||
): Promise<MonkeyResponse> {
|
||||
const { uid } = req.ctx.decodedToken;
|
||||
const { mode, mode2, language, rank } = req.body;
|
||||
const { mode, language, rank } = req.body;
|
||||
const mode2 = req.body.mode2 as MonkeyTypes.Mode2<MonkeyTypes.Mode>;
|
||||
|
||||
await UsersDAO.updateLbMemory(uid, mode, mode2, language, rank);
|
||||
await UserDAL.updateLbMemory(uid, mode, mode2, language, rank);
|
||||
return new MonkeyResponse("Leaderboard memory updated");
|
||||
}
|
||||
|
||||
|
@ -252,7 +253,7 @@ export async function getCustomThemes(
|
|||
req: MonkeyTypes.Request
|
||||
): Promise<MonkeyResponse> {
|
||||
const { uid } = req.ctx.decodedToken;
|
||||
const customThemes = await UsersDAO.getThemes(uid);
|
||||
const customThemes = await UserDAL.getThemes(uid);
|
||||
return new MonkeyResponse("Custom themes retrieved", customThemes);
|
||||
}
|
||||
|
||||
|
@ -262,7 +263,7 @@ export async function addCustomTheme(
|
|||
const { uid } = req.ctx.decodedToken;
|
||||
const { name, colors } = req.body;
|
||||
|
||||
const addedTheme = await UsersDAO.addTheme(uid, { name, colors });
|
||||
const addedTheme = await UserDAL.addTheme(uid, { name, colors });
|
||||
return new MonkeyResponse("Custom theme added", {
|
||||
theme: addedTheme,
|
||||
});
|
||||
|
@ -273,7 +274,7 @@ export async function removeCustomTheme(
|
|||
): Promise<MonkeyResponse> {
|
||||
const { uid } = req.ctx.decodedToken;
|
||||
const { themeId } = req.body;
|
||||
await UsersDAO.removeTheme(uid, themeId);
|
||||
await UserDAL.removeTheme(uid, themeId);
|
||||
return new MonkeyResponse("Custom theme removed");
|
||||
}
|
||||
|
||||
|
@ -283,7 +284,7 @@ export async function editCustomTheme(
|
|||
const { uid } = req.ctx.decodedToken;
|
||||
const { themeId, theme } = req.body;
|
||||
|
||||
await UsersDAO.editTheme(uid, themeId, theme);
|
||||
await UserDAL.editTheme(uid, themeId, theme);
|
||||
return new MonkeyResponse("Custom theme updated");
|
||||
}
|
||||
|
||||
|
@ -293,6 +294,11 @@ export async function getPersonalBests(
|
|||
const { uid } = req.ctx.decodedToken;
|
||||
const { mode, mode2 } = req.query;
|
||||
|
||||
const data = (await UsersDAO.getPersonalBests(uid, mode, mode2)) ?? null;
|
||||
const data =
|
||||
(await UserDAL.getPersonalBests(
|
||||
uid,
|
||||
mode as string,
|
||||
mode2 as string | undefined
|
||||
)) ?? null;
|
||||
return new MonkeyResponse("Personal bests retrieved", data);
|
||||
}
|
||||
|
|
|
@ -2,13 +2,13 @@ import { ObjectId } from "mongodb";
|
|||
import MonkeyError from "../utils/error";
|
||||
import db from "../init/db";
|
||||
|
||||
import UserDAO from "./user";
|
||||
import { getUser, getTags } from "./user";
|
||||
|
||||
class ResultDAO {
|
||||
static async addResult(uid, result) {
|
||||
let user;
|
||||
try {
|
||||
user = await UserDAO.getUser(uid);
|
||||
user = await getUser(uid);
|
||||
} catch (e) {
|
||||
user = null;
|
||||
}
|
||||
|
@ -30,7 +30,7 @@ class ResultDAO {
|
|||
.collection("results")
|
||||
.findOne({ _id: new ObjectId(resultid), uid });
|
||||
if (!result) throw new MonkeyError(404, "Result not found");
|
||||
const userTags = await UserDAO.getTags(uid);
|
||||
const userTags = await getTags(uid);
|
||||
const userTagIds = userTags.map((tag) => tag._id.toString());
|
||||
let validTags = true;
|
||||
tags.forEach((tagId) => {
|
||||
|
|
|
@ -1,404 +0,0 @@
|
|||
import _ from "lodash";
|
||||
import { isUsernameValid } from "../utils/validation";
|
||||
import { updateUserEmail } from "../utils/auth";
|
||||
import { checkAndUpdatePb } from "../utils/pb";
|
||||
import db from "../init/db";
|
||||
import MonkeyError from "../utils/error";
|
||||
import { ObjectId } from "mongodb";
|
||||
|
||||
class UsersDAO {
|
||||
static async addUser(name, email, uid) {
|
||||
const user = await db.collection("users").findOne({ uid });
|
||||
if (user) {
|
||||
throw new MonkeyError(409, "User document already exists", "addUser");
|
||||
}
|
||||
return await db
|
||||
.collection("users")
|
||||
.insertOne({ name, email, uid, addedAt: Date.now() });
|
||||
}
|
||||
|
||||
static async deleteUser(uid) {
|
||||
return await db.collection("users").deleteOne({ uid });
|
||||
}
|
||||
|
||||
static async updateName(uid, name) {
|
||||
if (!this.isNameAvailable(name)) {
|
||||
throw new MonkeyError(409, "Username already taken", name);
|
||||
}
|
||||
let user = await db.collection("users").findOne({ uid });
|
||||
if (
|
||||
Date.now() - user.lastNameChange < 2592000000 &&
|
||||
isUsernameValid(user.name)
|
||||
) {
|
||||
throw new MonkeyError(409, "You can change your name once every 30 days");
|
||||
}
|
||||
return await db
|
||||
.collection("users")
|
||||
.updateOne({ uid }, { $set: { name, lastNameChange: Date.now() } });
|
||||
}
|
||||
|
||||
static async clearPb(uid) {
|
||||
return await db
|
||||
.collection("users")
|
||||
.updateOne({ uid }, { $set: { personalBests: {}, lbPersonalBests: {} } });
|
||||
}
|
||||
|
||||
static async isNameAvailable(name) {
|
||||
const nameDocs = await db
|
||||
.collection("users")
|
||||
.find({ name })
|
||||
.collation({ locale: "en", strength: 1 })
|
||||
.limit(1)
|
||||
.toArray();
|
||||
if (nameDocs.length !== 0) {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
static async updateQuoteRatings(uid, quoteRatings) {
|
||||
const user = await db.collection("users").findOne({ uid });
|
||||
if (!user) {
|
||||
throw new MonkeyError(404, "User not found", "updateQuoteRatings");
|
||||
}
|
||||
await db.collection("users").updateOne({ uid }, { $set: { quoteRatings } });
|
||||
return true;
|
||||
}
|
||||
|
||||
static async updateEmail(uid, email) {
|
||||
const user = await db.collection("users").findOne({ uid });
|
||||
if (!user) throw new MonkeyError(404, "User not found", "update email");
|
||||
await updateUserEmail(uid, email);
|
||||
await db.collection("users").updateOne({ uid }, { $set: { email } });
|
||||
return true;
|
||||
}
|
||||
|
||||
static async getUser(uid) {
|
||||
const user = await db.collection("users").findOne({ uid });
|
||||
if (!user) throw new MonkeyError(404, "User not found", "get user");
|
||||
return user;
|
||||
}
|
||||
|
||||
static async isDiscordIdAvailable(discordId) {
|
||||
const user = await db.collection("users").findOne({ discordId });
|
||||
return _.isNil(user);
|
||||
}
|
||||
|
||||
static async addTag(uid, name) {
|
||||
const _id = new ObjectId();
|
||||
await db
|
||||
.collection("users")
|
||||
.updateOne({ uid }, { $push: { tags: { _id, name } } });
|
||||
return {
|
||||
_id,
|
||||
name,
|
||||
};
|
||||
}
|
||||
|
||||
static async getTags(uid) {
|
||||
const user = await db.collection("users").findOne({ uid });
|
||||
// if (!user) throw new MonkeyError(404, "User not found", "get tags");
|
||||
return user?.tags ?? [];
|
||||
}
|
||||
|
||||
static async editTag(uid, _id, name) {
|
||||
const user = await db.collection("users").findOne({ uid });
|
||||
if (!user) throw new MonkeyError(404, "User not found", "edit tag");
|
||||
if (
|
||||
user.tags === undefined ||
|
||||
user.tags.filter((t) => t._id == _id).length === 0
|
||||
) {
|
||||
throw new MonkeyError(404, "Tag not found");
|
||||
}
|
||||
return await db.collection("users").updateOne(
|
||||
{
|
||||
uid: uid,
|
||||
"tags._id": new ObjectId(_id),
|
||||
},
|
||||
{ $set: { "tags.$.name": name } }
|
||||
);
|
||||
}
|
||||
|
||||
static async removeTag(uid, _id) {
|
||||
const user = await db.collection("users").findOne({ uid });
|
||||
if (!user) throw new MonkeyError(404, "User not found", "remove tag");
|
||||
if (
|
||||
user.tags === undefined ||
|
||||
user.tags.filter((t) => t._id == _id).length === 0
|
||||
) {
|
||||
throw new MonkeyError(404, "Tag not found");
|
||||
}
|
||||
return await db.collection("users").updateOne(
|
||||
{
|
||||
uid: uid,
|
||||
"tags._id": new ObjectId(_id),
|
||||
},
|
||||
{ $pull: { tags: { _id: new ObjectId(_id) } } }
|
||||
);
|
||||
}
|
||||
|
||||
static async removeTagPb(uid, _id) {
|
||||
const user = await db.collection("users").findOne({ uid });
|
||||
if (!user) throw new MonkeyError(404, "User not found", "remove tag pb");
|
||||
if (
|
||||
user.tags === undefined ||
|
||||
user.tags.filter((t) => t._id == _id).length === 0
|
||||
) {
|
||||
throw new MonkeyError(404, "Tag not found");
|
||||
}
|
||||
return await db.collection("users").updateOne(
|
||||
{
|
||||
uid: uid,
|
||||
"tags._id": new ObjectId(_id),
|
||||
},
|
||||
{ $set: { "tags.$.personalBests": {} } }
|
||||
);
|
||||
}
|
||||
|
||||
static async updateLbMemory(uid, mode, mode2, language, rank) {
|
||||
const user = await db.collection("users").findOne({ uid });
|
||||
if (!user) throw new MonkeyError(404, "User not found", "update lb memory");
|
||||
if (user.lbMemory === undefined) user.lbMemory = {};
|
||||
if (user.lbMemory[mode] === undefined) user.lbMemory[mode] = {};
|
||||
if (user.lbMemory[mode][mode2] === undefined) {
|
||||
user.lbMemory[mode][mode2] = {};
|
||||
}
|
||||
user.lbMemory[mode][mode2][language] = rank;
|
||||
return await db.collection("users").updateOne(
|
||||
{ uid },
|
||||
{
|
||||
$set: { lbMemory: user.lbMemory },
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
static async checkIfPb(uid, user, result) {
|
||||
const { mode, funbox } = result;
|
||||
|
||||
if (funbox !== "none" && funbox !== "plus_one" && funbox !== "plus_two") {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (mode === "quote") {
|
||||
return false;
|
||||
}
|
||||
|
||||
let lbpb = user.lbPersonalBests;
|
||||
if (!lbpb) lbpb = {};
|
||||
|
||||
let pb = checkAndUpdatePb(user.personalBests, lbpb, result);
|
||||
|
||||
if (pb.isPb) {
|
||||
await db
|
||||
.collection("users")
|
||||
.updateOne({ uid }, { $set: { personalBests: pb.obj } });
|
||||
if (pb.lbObj) {
|
||||
await db
|
||||
.collection("users")
|
||||
.updateOne({ uid }, { $set: { lbPersonalBests: pb.lbObj } });
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static async checkIfTagPb(uid, user, result) {
|
||||
if (user.tags === undefined || user.tags.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const { mode, tags, funbox } = result;
|
||||
|
||||
if (funbox !== "none" && funbox !== "plus_one" && funbox !== "plus_two") {
|
||||
return [];
|
||||
}
|
||||
|
||||
if (mode === "quote") {
|
||||
return [];
|
||||
}
|
||||
|
||||
let tagsToCheck = [];
|
||||
user.tags.forEach((tag) => {
|
||||
tags.forEach((resultTag) => {
|
||||
if (resultTag == tag._id) {
|
||||
tagsToCheck.push(tag);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
let ret = [];
|
||||
|
||||
tagsToCheck.forEach(async (tag) => {
|
||||
let tagpb = checkAndUpdatePb(tag.personalBests, undefined, result);
|
||||
if (tagpb.isPb) {
|
||||
ret.push(tag._id);
|
||||
await db
|
||||
.collection("users")
|
||||
.updateOne(
|
||||
{ uid, "tags._id": new ObjectId(tag._id) },
|
||||
{ $set: { "tags.$.personalBests": tagpb.obj } }
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static async resetPb(uid) {
|
||||
const user = await db.collection("users").findOne({ uid });
|
||||
if (!user) throw new MonkeyError(404, "User not found", "reset pb");
|
||||
return await db
|
||||
.collection("users")
|
||||
.updateOne({ uid }, { $set: { personalBests: {} } });
|
||||
}
|
||||
|
||||
static async updateTypingStats(uid, restartCount, timeTyping) {
|
||||
return await db.collection("users").updateOne(
|
||||
{ uid },
|
||||
{
|
||||
$inc: {
|
||||
startedTests: restartCount + 1,
|
||||
completedTests: 1,
|
||||
timeTyping,
|
||||
},
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
static async linkDiscord(uid, discordId) {
|
||||
const user = await db.collection("users").findOne({ uid });
|
||||
if (!user) throw new MonkeyError(404, "User not found", "link discord");
|
||||
return await db
|
||||
.collection("users")
|
||||
.updateOne({ uid }, { $set: { discordId } });
|
||||
}
|
||||
|
||||
static async unlinkDiscord(uid) {
|
||||
const user = await db.collection("users").findOne({ uid });
|
||||
if (!user) throw new MonkeyError(404, "User not found", "unlink discord");
|
||||
return await db
|
||||
.collection("users")
|
||||
.updateOne({ uid }, { $set: { discordId: null } });
|
||||
}
|
||||
|
||||
static async incrementBananas(uid, wpm) {
|
||||
const user = await db.collection("users").findOne({ uid });
|
||||
if (!user) {
|
||||
throw new MonkeyError(404, "User not found", "increment bananas");
|
||||
}
|
||||
|
||||
let best60;
|
||||
try {
|
||||
best60 = Math.max(...user.personalBests.time[60].map((best) => best.wpm));
|
||||
} catch (e) {
|
||||
best60 = undefined;
|
||||
}
|
||||
|
||||
if (best60 === undefined || wpm >= best60 - best60 * 0.25) {
|
||||
//increment when no record found or wpm is within 25% of the record
|
||||
return await db
|
||||
.collection("users")
|
||||
.updateOne({ uid }, { $inc: { bananas: 1 } });
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
static themeDoesNotExist(customThemes, id) {
|
||||
return (
|
||||
(customThemes ?? []).filter((t) => t._id.toString() === id).length === 0
|
||||
);
|
||||
}
|
||||
|
||||
static async addTheme(uid, theme) {
|
||||
const user = await db.collection("users").findOne({ uid });
|
||||
if (!user) throw new MonkeyError(404, "User not found", "Add custom theme");
|
||||
|
||||
if ((user.customThemes ?? []).length >= 10) {
|
||||
throw new MonkeyError(409, "Too many custom themes");
|
||||
}
|
||||
|
||||
const _id = new ObjectId();
|
||||
await db.collection("users").updateOne(
|
||||
{ uid },
|
||||
{
|
||||
$push: {
|
||||
customThemes: {
|
||||
_id,
|
||||
name: theme.name,
|
||||
colors: theme.colors,
|
||||
},
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
return {
|
||||
_id,
|
||||
name: theme.name,
|
||||
};
|
||||
}
|
||||
|
||||
static async removeTheme(uid, _id) {
|
||||
const user = await db.collection("users").findOne({ uid });
|
||||
if (!user) {
|
||||
throw new MonkeyError(404, "User not found", "Remove custom theme");
|
||||
}
|
||||
|
||||
if (this.themeDoesNotExist(user.customThemes, _id)) {
|
||||
throw new MonkeyError(404, "Custom theme not found");
|
||||
}
|
||||
|
||||
return await db.collection("users").updateOne(
|
||||
{
|
||||
uid: uid,
|
||||
"customThemes._id": new ObjectId(_id),
|
||||
},
|
||||
{ $pull: { customThemes: { _id: new ObjectId(_id) } } }
|
||||
);
|
||||
}
|
||||
|
||||
static async editTheme(uid, _id, theme) {
|
||||
const user = await db.collection("users").findOne({ uid });
|
||||
if (!user) {
|
||||
throw new MonkeyError(404, "User not found", "Edit custom theme");
|
||||
}
|
||||
|
||||
if (this.themeDoesNotExist(user.customThemes, _id)) {
|
||||
throw new MonkeyError(404, "Custom Theme not found");
|
||||
}
|
||||
|
||||
return await db.collection("users").updateOne(
|
||||
{
|
||||
uid: uid,
|
||||
"customThemes._id": new ObjectId(_id),
|
||||
},
|
||||
{
|
||||
$set: {
|
||||
"customThemes.$.name": theme.name,
|
||||
"customThemes.$.colors": theme.colors,
|
||||
},
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
static async getThemes(uid) {
|
||||
const user = await db.collection("users").findOne({ uid });
|
||||
if (!user) {
|
||||
throw new MonkeyError(404, "User not found", "Get custom themes");
|
||||
}
|
||||
return user.customThemes ?? [];
|
||||
}
|
||||
|
||||
static async getPersonalBests(uid, mode, mode2) {
|
||||
const user = await db.collection("users").findOne({ uid });
|
||||
if (mode2) {
|
||||
return user?.personalBests?.[mode]?.[mode2];
|
||||
} else {
|
||||
return user?.personalBests?.[mode];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default UsersDAO;
|
536
backend/dao/user.ts
Normal file
536
backend/dao/user.ts
Normal file
|
@ -0,0 +1,536 @@
|
|||
import _ from "lodash";
|
||||
import { isUsernameValid } from "../utils/validation";
|
||||
import { updateUserEmail } from "../utils/auth";
|
||||
import { checkAndUpdatePb } from "../utils/pb";
|
||||
import db from "../init/db";
|
||||
import MonkeyError from "../utils/error";
|
||||
import { DeleteResult, InsertOneResult, ObjectId, UpdateResult } from "mongodb";
|
||||
|
||||
export async function addUser(
|
||||
name: string | undefined,
|
||||
email: string,
|
||||
uid: string
|
||||
): Promise<InsertOneResult<MonkeyTypes.User>> {
|
||||
const usersCollection = db.collection<MonkeyTypes.User>("users");
|
||||
|
||||
const user = await usersCollection.findOne({ uid });
|
||||
if (user) {
|
||||
throw new MonkeyError(409, "User document already exists", "addUser");
|
||||
}
|
||||
|
||||
const currentDate = Date.now();
|
||||
return await usersCollection.insertOne({
|
||||
name,
|
||||
email,
|
||||
uid,
|
||||
addedAt: currentDate,
|
||||
});
|
||||
}
|
||||
|
||||
export async function deleteUser(uid: string): Promise<DeleteResult> {
|
||||
return await db.collection<MonkeyTypes.User>("users").deleteOne({ uid });
|
||||
}
|
||||
|
||||
export async function updateName(
|
||||
uid: string,
|
||||
name: string
|
||||
): Promise<UpdateResult> {
|
||||
if (!isNameAvailable(name)) {
|
||||
throw new MonkeyError(409, "Username already taken", name);
|
||||
}
|
||||
|
||||
const user = await db.collection<MonkeyTypes.User>("users").findOne({ uid });
|
||||
|
||||
if (!user) {
|
||||
throw new MonkeyError(404, "User not found", "update name");
|
||||
}
|
||||
|
||||
if (Date.now() - (user.lastNameChange ?? 0) < 2592000000) {
|
||||
throw new MonkeyError(409, "You can change your name once every 30 days");
|
||||
}
|
||||
if (!isUsernameValid(name)) {
|
||||
throw new MonkeyError(400, "Invalid username");
|
||||
}
|
||||
|
||||
return await db
|
||||
.collection<MonkeyTypes.User>("users")
|
||||
.updateOne({ uid }, { $set: { name, lastNameChange: Date.now() } });
|
||||
}
|
||||
|
||||
export async function clearPb(uid: string): Promise<UpdateResult> {
|
||||
return await db.collection<MonkeyTypes.User>("users").updateOne(
|
||||
{ uid },
|
||||
{
|
||||
$set: {
|
||||
personalBests: {
|
||||
custom: {},
|
||||
quote: {},
|
||||
time: {},
|
||||
words: {},
|
||||
zen: {},
|
||||
},
|
||||
lbPersonalBests: {
|
||||
time: {},
|
||||
},
|
||||
},
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
export async function isNameAvailable(name: string): Promise<boolean> {
|
||||
const nameDocs = await db
|
||||
.collection<MonkeyTypes.User>("users")
|
||||
.find({ name })
|
||||
.collation({ locale: "en", strength: 1 })
|
||||
.limit(1)
|
||||
.toArray();
|
||||
|
||||
return nameDocs.length === 0;
|
||||
}
|
||||
|
||||
export async function updateQuoteRatings(
|
||||
uid: string,
|
||||
quoteRatings: MonkeyTypes.UserQuoteRatings
|
||||
): Promise<boolean> {
|
||||
const user = await db.collection<MonkeyTypes.User>("users").findOne({ uid });
|
||||
if (!user) {
|
||||
throw new MonkeyError(404, "User not found", "updateQuoteRatings");
|
||||
}
|
||||
|
||||
await db
|
||||
.collection<MonkeyTypes.User>("users")
|
||||
.updateOne({ uid }, { $set: { quoteRatings } });
|
||||
return true;
|
||||
}
|
||||
|
||||
export async function updateEmail(
|
||||
uid: string,
|
||||
email: string
|
||||
): Promise<boolean> {
|
||||
const user = await db.collection<MonkeyTypes.User>("users").findOne({ uid });
|
||||
if (!user) throw new MonkeyError(404, "User not found", "update email");
|
||||
await updateUserEmail(uid, email);
|
||||
await db
|
||||
.collection<MonkeyTypes.User>("users")
|
||||
.updateOne({ uid }, { $set: { email } });
|
||||
return true;
|
||||
}
|
||||
|
||||
export async function getUser(uid: string): Promise<MonkeyTypes.User> {
|
||||
const user = await db.collection<MonkeyTypes.User>("users").findOne({ uid });
|
||||
if (!user) throw new MonkeyError(404, "User not found", "get user");
|
||||
return user;
|
||||
}
|
||||
|
||||
export async function isDiscordIdAvailable(
|
||||
discordId: string
|
||||
): Promise<boolean> {
|
||||
const user = await db
|
||||
.collection<MonkeyTypes.User>("users")
|
||||
.findOne({ discordId });
|
||||
return _.isNil(user);
|
||||
}
|
||||
|
||||
export async function addTag(
|
||||
uid: string,
|
||||
name: string
|
||||
): Promise<MonkeyTypes.UserTag> {
|
||||
const _id = new ObjectId();
|
||||
await db
|
||||
.collection<MonkeyTypes.User>("users")
|
||||
.updateOne({ uid }, { $push: { tags: { _id, name } } });
|
||||
return {
|
||||
_id,
|
||||
name,
|
||||
};
|
||||
}
|
||||
|
||||
export async function getTags(uid: string): Promise<MonkeyTypes.UserTag[]> {
|
||||
const user = await db.collection<MonkeyTypes.User>("users").findOne({ uid });
|
||||
|
||||
if (!user) throw new MonkeyError(404, "User not found", "get tags");
|
||||
|
||||
return user.tags ?? [];
|
||||
}
|
||||
|
||||
export async function editTag(
|
||||
uid: string,
|
||||
_id: string,
|
||||
name: string
|
||||
): Promise<UpdateResult> {
|
||||
const user = await db.collection<MonkeyTypes.User>("users").findOne({ uid });
|
||||
if (!user) throw new MonkeyError(404, "User not found", "edit tag");
|
||||
if (
|
||||
user.tags === undefined ||
|
||||
user.tags.filter((t) => t._id.toHexString() === _id).length === 0
|
||||
) {
|
||||
throw new MonkeyError(404, "Tag not found");
|
||||
}
|
||||
return await db.collection<MonkeyTypes.User>("users").updateOne(
|
||||
{
|
||||
uid: uid,
|
||||
"tags._id": new ObjectId(_id),
|
||||
},
|
||||
{ $set: { "tags.$.name": name } }
|
||||
);
|
||||
}
|
||||
|
||||
export async function removeTag(
|
||||
uid: string,
|
||||
_id: string
|
||||
): Promise<UpdateResult> {
|
||||
const user = await db.collection<MonkeyTypes.User>("users").findOne({ uid });
|
||||
if (!user) throw new MonkeyError(404, "User not found", "remove tag");
|
||||
if (
|
||||
user.tags === undefined ||
|
||||
user.tags.filter((t) => t._id.toHexString() == _id).length === 0
|
||||
) {
|
||||
throw new MonkeyError(404, "Tag not found");
|
||||
}
|
||||
return await db.collection<MonkeyTypes.User>("users").updateOne(
|
||||
{
|
||||
uid: uid,
|
||||
"tags._id": new ObjectId(_id),
|
||||
},
|
||||
{ $pull: { tags: { _id: new ObjectId(_id) } } }
|
||||
);
|
||||
}
|
||||
|
||||
export async function removeTagPb(
|
||||
uid: string,
|
||||
_id: string
|
||||
): Promise<UpdateResult> {
|
||||
const usersCollection = db.collection<MonkeyTypes.User>("users");
|
||||
const user = await usersCollection.findOne({ uid });
|
||||
if (!user) throw new MonkeyError(404, "User not found", "remove tag pb");
|
||||
if (
|
||||
user.tags === undefined ||
|
||||
user.tags.filter((t) => t._id.toHexString() == _id).length === 0
|
||||
) {
|
||||
throw new MonkeyError(404, "Tag not found");
|
||||
}
|
||||
return await usersCollection.updateOne(
|
||||
{
|
||||
uid: uid,
|
||||
"tags._id": new ObjectId(_id),
|
||||
},
|
||||
{ $set: { "tags.$.personalBests": {} } }
|
||||
);
|
||||
}
|
||||
|
||||
export async function updateLbMemory(
|
||||
uid: string,
|
||||
mode: MonkeyTypes.Mode,
|
||||
mode2: MonkeyTypes.Mode2<MonkeyTypes.Mode>,
|
||||
language: string,
|
||||
rank: number
|
||||
): Promise<UpdateResult> {
|
||||
const user = await db.collection<MonkeyTypes.User>("users").findOne({ uid });
|
||||
if (!user) throw new MonkeyError(404, "User not found", "update lb memory");
|
||||
if (user.lbMemory === undefined) user.lbMemory = {};
|
||||
if (user.lbMemory[mode] === undefined) user.lbMemory[mode] = {};
|
||||
if (user.lbMemory[mode][mode2] === undefined) {
|
||||
user.lbMemory[mode][mode2] = {};
|
||||
}
|
||||
user.lbMemory[mode][mode2][language] = rank;
|
||||
return await db.collection<MonkeyTypes.User>("users").updateOne(
|
||||
{ uid },
|
||||
{
|
||||
$set: { lbMemory: user.lbMemory },
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
export async function checkIfPb(
|
||||
uid: string,
|
||||
user: MonkeyTypes.User,
|
||||
result: MonkeyTypes.Result<MonkeyTypes.Mode>
|
||||
): Promise<boolean> {
|
||||
const { mode, funbox } = result;
|
||||
|
||||
if (funbox !== "none" && funbox !== "plus_one" && funbox !== "plus_two") {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (mode === "quote") {
|
||||
return false;
|
||||
}
|
||||
|
||||
let lbPb = user.lbPersonalBests;
|
||||
if (!lbPb) lbPb = { time: {} };
|
||||
|
||||
const pb = checkAndUpdatePb(
|
||||
user.personalBests ?? {
|
||||
time: {},
|
||||
custom: {},
|
||||
quote: {},
|
||||
words: {},
|
||||
zen: {},
|
||||
},
|
||||
lbPb,
|
||||
result
|
||||
);
|
||||
|
||||
if (!pb.isPb) return false;
|
||||
|
||||
await db
|
||||
.collection<MonkeyTypes.User>("users")
|
||||
.updateOne({ uid }, { $set: { personalBests: pb.personalBests } });
|
||||
|
||||
if (pb.lbPersonalBests) {
|
||||
await db
|
||||
.collection<MonkeyTypes.User>("users")
|
||||
.updateOne({ uid }, { $set: { lbPersonalBests: pb.lbPersonalBests } });
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
export async function checkIfTagPb(
|
||||
uid: string,
|
||||
user: MonkeyTypes.User,
|
||||
result: MonkeyTypes.Result<MonkeyTypes.Mode>
|
||||
): Promise<string[]> {
|
||||
if (user.tags === undefined || user.tags.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const { mode, tags: resultTags, funbox } = result;
|
||||
|
||||
if (funbox !== "none" && funbox !== "plus_one" && funbox !== "plus_two") {
|
||||
return [];
|
||||
}
|
||||
|
||||
if (mode === "quote") {
|
||||
return [];
|
||||
}
|
||||
|
||||
const tagsToCheck: MonkeyTypes.UserTag[] = [];
|
||||
user.tags.forEach((userTag) => {
|
||||
resultTags.forEach((resultTag) => {
|
||||
if (resultTag === userTag._id.toHexString()) {
|
||||
tagsToCheck.push(userTag);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
const ret: string[] = [];
|
||||
|
||||
tagsToCheck.forEach(async (tag) => {
|
||||
const tagPbs: MonkeyTypes.PersonalBests = tag.personalBests ?? {
|
||||
time: {},
|
||||
words: {},
|
||||
zen: {},
|
||||
custom: {},
|
||||
quote: {},
|
||||
};
|
||||
|
||||
const tagpb = checkAndUpdatePb(tagPbs, undefined, result);
|
||||
if (tagpb.isPb) {
|
||||
ret.push(tag._id.toHexString());
|
||||
await db
|
||||
.collection<MonkeyTypes.User>("users")
|
||||
.updateOne(
|
||||
{ uid, "tags._id": new ObjectId(tag._id) },
|
||||
{ $set: { "tags.$.personalBests": tagpb.personalBests } }
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
export async function resetPb(uid: string): Promise<UpdateResult> {
|
||||
const user = await db.collection<MonkeyTypes.User>("users").findOne({ uid });
|
||||
if (!user) throw new MonkeyError(404, "User not found", "reset pb");
|
||||
return await db.collection<MonkeyTypes.User>("users").updateOne(
|
||||
{ uid },
|
||||
{
|
||||
$set: {
|
||||
personalBests: {
|
||||
time: {},
|
||||
custom: {},
|
||||
quote: {},
|
||||
words: {},
|
||||
zen: {},
|
||||
},
|
||||
},
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
export async function updateTypingStats(
|
||||
uid: string,
|
||||
restartCount: number,
|
||||
timeTyping: number
|
||||
): Promise<UpdateResult> {
|
||||
return await db.collection<MonkeyTypes.User>("users").updateOne(
|
||||
{ uid },
|
||||
{
|
||||
$inc: {
|
||||
startedTests: restartCount + 1,
|
||||
completedTests: 1,
|
||||
timeTyping,
|
||||
},
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
export async function linkDiscord(
|
||||
uid: string,
|
||||
discordId: string
|
||||
): Promise<UpdateResult> {
|
||||
const user = await db.collection<MonkeyTypes.User>("users").findOne({ uid });
|
||||
if (!user) throw new MonkeyError(404, "User not found", "link discord");
|
||||
return await db
|
||||
.collection<MonkeyTypes.User>("users")
|
||||
.updateOne({ uid }, { $set: { discordId } });
|
||||
}
|
||||
|
||||
export async function unlinkDiscord(uid: string): Promise<UpdateResult> {
|
||||
const usersCollection = db.collection<MonkeyTypes.User>("users");
|
||||
const user = await usersCollection.findOne({ uid });
|
||||
if (!user) throw new MonkeyError(404, "User not found", "unlink discord");
|
||||
|
||||
return await usersCollection.updateOne(
|
||||
{ uid },
|
||||
{ $set: { discordId: undefined } }
|
||||
);
|
||||
}
|
||||
|
||||
export async function incrementBananas(
|
||||
uid: string,
|
||||
wpm
|
||||
): Promise<UpdateResult | null> {
|
||||
const user = await db.collection<MonkeyTypes.User>("users").findOne({ uid });
|
||||
if (!user) {
|
||||
throw new MonkeyError(404, "User not found", "increment bananas");
|
||||
}
|
||||
|
||||
let best60: number | undefined;
|
||||
const personalBests60 = user.personalBests?.time[60];
|
||||
|
||||
if (personalBests60) {
|
||||
best60 = Math.max(...personalBests60.map((best) => best.wpm));
|
||||
}
|
||||
|
||||
if (best60 === undefined || wpm >= best60 - best60 * 0.25) {
|
||||
//increment when no record found or wpm is within 25% of the record
|
||||
return await db
|
||||
.collection<MonkeyTypes.User>("users")
|
||||
.updateOne({ uid }, { $inc: { bananas: 1 } });
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
export function themeDoesNotExist(customThemes, id): boolean {
|
||||
return (
|
||||
(customThemes ?? []).filter((t) => t._id.toString() === id).length === 0
|
||||
);
|
||||
}
|
||||
|
||||
export async function addTheme(
|
||||
uid: string,
|
||||
theme
|
||||
): Promise<{ _id: ObjectId; name: string }> {
|
||||
const user = await db.collection<MonkeyTypes.User>("users").findOne({ uid });
|
||||
if (!user) throw new MonkeyError(404, "User not found", "Add custom theme");
|
||||
|
||||
if ((user.customThemes ?? []).length >= 10) {
|
||||
throw new MonkeyError(409, "Too many custom themes");
|
||||
}
|
||||
|
||||
const _id = new ObjectId();
|
||||
await db.collection<MonkeyTypes.User>("users").updateOne(
|
||||
{ uid },
|
||||
{
|
||||
$push: {
|
||||
customThemes: {
|
||||
_id,
|
||||
name: theme.name,
|
||||
colors: theme.colors,
|
||||
},
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
return {
|
||||
_id,
|
||||
name: theme.name,
|
||||
};
|
||||
}
|
||||
|
||||
export async function removeTheme(uid: string, _id): Promise<UpdateResult> {
|
||||
const user = await db.collection<MonkeyTypes.User>("users").findOne({ uid });
|
||||
if (!user) {
|
||||
throw new MonkeyError(404, "User not found", "Remove custom theme");
|
||||
}
|
||||
|
||||
if (themeDoesNotExist(user.customThemes, _id)) {
|
||||
throw new MonkeyError(404, "Custom theme not found");
|
||||
}
|
||||
|
||||
return await db.collection<MonkeyTypes.User>("users").updateOne(
|
||||
{
|
||||
uid: uid,
|
||||
"customThemes._id": new ObjectId(_id),
|
||||
},
|
||||
{ $pull: { customThemes: { _id: new ObjectId(_id) } } }
|
||||
);
|
||||
}
|
||||
|
||||
export async function editTheme(
|
||||
uid: string,
|
||||
_id,
|
||||
theme
|
||||
): Promise<UpdateResult> {
|
||||
const user = await db.collection<MonkeyTypes.User>("users").findOne({ uid });
|
||||
if (!user) {
|
||||
throw new MonkeyError(404, "User not found", "Edit custom theme");
|
||||
}
|
||||
|
||||
if (themeDoesNotExist(user.customThemes, _id)) {
|
||||
throw new MonkeyError(404, "Custom Theme not found");
|
||||
}
|
||||
|
||||
return await db.collection<MonkeyTypes.User>("users").updateOne(
|
||||
{
|
||||
uid: uid,
|
||||
"customThemes._id": new ObjectId(_id),
|
||||
},
|
||||
{
|
||||
$set: {
|
||||
"customThemes.$.name": theme.name,
|
||||
"customThemes.$.colors": theme.colors,
|
||||
},
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
export async function getThemes(
|
||||
uid: string
|
||||
): Promise<MonkeyTypes.CustomTheme[]> {
|
||||
const user = await db.collection<MonkeyTypes.User>("users").findOne({ uid });
|
||||
if (!user) {
|
||||
throw new MonkeyError(404, "User not found", "Get custom themes");
|
||||
}
|
||||
return user.customThemes ?? [];
|
||||
}
|
||||
|
||||
export async function getPersonalBests(
|
||||
uid: string,
|
||||
mode: string,
|
||||
mode2?: string
|
||||
): Promise<MonkeyTypes.PersonalBest> {
|
||||
const user = await db.collection<MonkeyTypes.User>("users").findOne({ uid });
|
||||
|
||||
if (!user) {
|
||||
throw new MonkeyError(404, "User not found", "Get personal bests");
|
||||
}
|
||||
|
||||
if (mode2) {
|
||||
return user?.personalBests?.[mode]?.[mode2];
|
||||
}
|
||||
|
||||
return user?.personalBests?.[mode];
|
||||
}
|
|
@ -3,7 +3,7 @@ import joi from "joi";
|
|||
import MonkeyError from "../utils/error";
|
||||
import { Response, NextFunction, RequestHandler } from "express";
|
||||
import { handleMonkeyResponse, MonkeyResponse } from "../utils/monkey-response";
|
||||
import UsersDAO from "../dao/user";
|
||||
import { getUser } from "../dao/user";
|
||||
|
||||
interface ValidationOptions<T> {
|
||||
criteria: (data: T) => boolean;
|
||||
|
@ -52,9 +52,7 @@ function checkUserPermissions(
|
|||
try {
|
||||
const { uid } = req.ctx.decodedToken;
|
||||
|
||||
const userData = (await UsersDAO.getUser(
|
||||
uid
|
||||
)) as unknown as MonkeyTypes.User;
|
||||
const userData = (await getUser(uid)) as unknown as MonkeyTypes.User;
|
||||
const hasPermission = criteria(userData);
|
||||
|
||||
if (!hasPermission) {
|
||||
|
|
52
backend/types/types.d.ts
vendored
52
backend/types/types.d.ts
vendored
|
@ -1,3 +1,5 @@
|
|||
type ObjectId = import("mongodb").ObjectId;
|
||||
|
||||
type ExpressRequest = import("express").Request;
|
||||
|
||||
declare namespace MonkeyTypes {
|
||||
|
@ -49,19 +51,21 @@ declare namespace MonkeyTypes {
|
|||
interface User {
|
||||
// TODO, Complete the typings for the user model
|
||||
addedAt: number;
|
||||
bananas: number;
|
||||
completedTests: number;
|
||||
verified?: boolean;
|
||||
bananas?: number;
|
||||
completedTests?: number;
|
||||
discordId?: string;
|
||||
email: string;
|
||||
lastNameChange: number;
|
||||
lbMemory: object;
|
||||
lbPersonalBests: object;
|
||||
name: string;
|
||||
personalBests: object;
|
||||
quoteRatings?: Record<string, Record<string, number>>;
|
||||
startedTests: number;
|
||||
tags: object[];
|
||||
timeTyping: number;
|
||||
lastNameChange?: number;
|
||||
lbMemory?: object;
|
||||
lbPersonalBests?: LbPersonalBests;
|
||||
name?: string;
|
||||
customThemes?: CustomTheme[];
|
||||
personalBests?: PersonalBests;
|
||||
quoteRatings?: UserQuoteRatings;
|
||||
startedTests?: number;
|
||||
tags?: UserTag[];
|
||||
timeTyping?: number;
|
||||
uid: string;
|
||||
quoteMod?: boolean;
|
||||
cannotReport?: boolean;
|
||||
|
@ -69,6 +73,28 @@ declare namespace MonkeyTypes {
|
|||
canManageApeKeys?: boolean;
|
||||
}
|
||||
|
||||
type UserQuoteRatings = Record<string, Record<string, number>>;
|
||||
|
||||
interface LbPersonalBests {
|
||||
time: {
|
||||
[key: number]: {
|
||||
[key: string]: PersonalBest;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
interface UserTag {
|
||||
_id: ObjectId;
|
||||
name: string;
|
||||
personalBests?: PersonalBests;
|
||||
}
|
||||
|
||||
interface CustomTheme {
|
||||
_id: ObjectId;
|
||||
name: string;
|
||||
colors: string[];
|
||||
}
|
||||
|
||||
interface ApeKey {
|
||||
uid: string;
|
||||
name: string;
|
||||
|
@ -106,9 +132,9 @@ declare namespace MonkeyTypes {
|
|||
[key: number]: PersonalBest[];
|
||||
};
|
||||
quote: { [quote: string]: PersonalBest[] };
|
||||
custom: { custom: PersonalBest[] };
|
||||
custom: { custom?: PersonalBest[] };
|
||||
zen: {
|
||||
zen: PersonalBest[];
|
||||
zen?: PersonalBest[];
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -2,18 +2,19 @@ import _ from "lodash";
|
|||
|
||||
interface CheckAndUpdatePbResult {
|
||||
isPb: boolean;
|
||||
obj: object;
|
||||
lbObj?: object;
|
||||
personalBests: MonkeyTypes.PersonalBests;
|
||||
lbPersonalBests?: MonkeyTypes.LbPersonalBests;
|
||||
}
|
||||
|
||||
type Result = MonkeyTypes.Result<MonkeyTypes.Mode>;
|
||||
|
||||
export function checkAndUpdatePb(
|
||||
userPersonalBests: MonkeyTypes.User["personalBests"],
|
||||
lbPersonalBests: MonkeyTypes.User["lbPersonalBests"] | undefined,
|
||||
userPersonalBests: MonkeyTypes.PersonalBests,
|
||||
lbPersonalBests: MonkeyTypes.LbPersonalBests | undefined,
|
||||
result: Result
|
||||
): CheckAndUpdatePbResult {
|
||||
const { mode, mode2 } = result;
|
||||
const mode = result.mode;
|
||||
const mode2 = result.mode2 as 15 | 60;
|
||||
|
||||
const userPb = userPersonalBests ?? {};
|
||||
userPb[mode] = userPb[mode] ?? {};
|
||||
|
@ -40,8 +41,8 @@ export function checkAndUpdatePb(
|
|||
|
||||
return {
|
||||
isPb,
|
||||
obj: userPb,
|
||||
lbObj: lbPersonalBests,
|
||||
personalBests: userPb,
|
||||
lbPersonalBests: lbPersonalBests,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -95,15 +96,16 @@ function buildPersonalBest(result: Result): MonkeyTypes.PersonalBest {
|
|||
}
|
||||
|
||||
function updateLeaderboardPersonalBests(
|
||||
userPersonalBests: MonkeyTypes.User["personalBests"],
|
||||
lbPersonalBests: MonkeyTypes.User["lbPersonalBests"],
|
||||
userPersonalBests: MonkeyTypes.PersonalBests,
|
||||
lbPersonalBests: MonkeyTypes.LbPersonalBests,
|
||||
result: Result
|
||||
): void {
|
||||
if (!shouldUpdateLeaderboardPersonalBests(result)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { mode, mode2 } = result;
|
||||
const mode = result.mode;
|
||||
const mode2 = result.mode2 as MonkeyTypes.Mode2<"time">;
|
||||
|
||||
lbPersonalBests[mode] = lbPersonalBests[mode] ?? {};
|
||||
const lbMode2 = lbPersonalBests[mode][mode2];
|
||||
|
|
|
@ -700,8 +700,8 @@ export async function updateLbMemory<M extends MonkeyTypes.Mode>(
|
|||
//could dbSnapshot just be used here instead of getSnapshot()
|
||||
|
||||
if (mode === "time") {
|
||||
const timeMode = mode as "time",
|
||||
timeMode2 = mode2 as 15 | 60;
|
||||
const timeMode = mode as "time";
|
||||
const timeMode2 = mode2 as 15 | 60;
|
||||
|
||||
const snapshot = getSnapshot();
|
||||
if (snapshot.lbMemory === undefined) {
|
||||
|
|
Loading…
Reference in a new issue