diff --git a/backend/api/controllers/user.js b/backend/api/controllers/user.js index a1b7aae4f..d7965e6a7 100644 --- a/backend/api/controllers/user.js +++ b/backend/api/controllers/user.js @@ -307,6 +307,19 @@ class UserController { } } + static async getCustomThemes(req, res, next) { + try { + const { uid } = req.decodedToken; + let customThemes = await UsersDAO.getThemes(uid); + console.log("Printing custom themes") + console.log(customThemes) + if (customThemes == undefined) customThemes = []; + return res.status(200).json(customThemes); + } catch (e) { + return next(e); + } + } + static async addCustomTheme(req, res, next) { try { const { uid } = req.decodedToken; @@ -330,6 +343,41 @@ class UserController { return next(e); } } + + static async removeCustomTheme(req, res, next) { + try { + const { uid } = req.decodedToken; + const { themeid } = req.body; + await UsersDAO.removeTheme(uid, themeid); + return res.sendStatus(200); + } catch (e) { + return next(e); + } + } + + static async editCustomTheme(req, res, next) { + try { + const { uid } = req.decodedToken; + const { themeid, theme } = req.body; + + if (!isThemeValid(theme)) + return res.status(400).json({ + message: + "Invalid Custom Theme. 1. Theme must contain a name. 2. All 10 colors for the theme must be present in the hexadecimal format" + }) + + if (!isTagPresetNameValid(theme.name)) + return res.status(400).json({ + message: + "Invalid Custom Theme. Theme's name cannot contain special characters or more than 16 characters. Can include: _ . -" + }); + + await UsersDAO.editTheme(uid, themeid, theme); + return res.status(200).json({ message: "Custom Theme successfully updated" }); + } catch (e) { + return next(e); + } + } } module.exports = UserController; diff --git a/backend/api/routes/user.js b/backend/api/routes/user.js index 96e374ba6..f3f542c91 100644 --- a/backend/api/routes/user.js +++ b/backend/api/routes/user.js @@ -91,12 +91,33 @@ router.post( UserController.editTag ); +router.get( + "/customThemes", + RateLimit.userCustomThemeGet, + authenticateRequest, + UserController.getCustomThemes +); + router.post( "/customThemes/add", RateLimit.userCustomThemeAdd, authenticateRequest, UserController.addCustomTheme -) +); + +router.post( + "/customThemes/remove", + RateLimit.userCustomThemeRemove, + authenticateRequest, + UserController.removeCustomTheme +); + +router.post( + "/customThemes/edit", + RateLimit.userCustomThemeEdit, + authenticateRequest, + UserController.editCustomTheme +); router.post( "/discord/link", diff --git a/backend/dao/user.js b/backend/dao/user.js index 1783cb43e..13fce2081 100644 --- a/backend/dao/user.js +++ b/backend/dao/user.js @@ -373,18 +373,74 @@ class UsersDAO { const user = await mongoDB().collection("users").findOne({ uid }); if (!user) throw new MonkeyError(404, "User not found", "add custom theme"); - const count = await user.customThemes.countDocuments(); + const count = user.customThemes.length; + console.log(count) + if (count >= 10) throw new MonkeyError(409, "Too many custom themes"); let _id = ObjectID(); - let addedTheme = await mongoDB() + await mongoDB() .collection("users") - .insertOne({ uid }, { $push: { customThemes: { _id, ...theme } } }); + .updateOne({ uid }, { $push: { customThemes: { _id, ...theme } } }); return { - insertedId: addedTheme.insertedId, + _id, + name: theme.name, }; } + + static async removeTheme(uid, _id) { + const user = await mongoDB().collection("users").findOne({ uid }); + if (!user) throw new MonkeyError(404, "User not found", "remove theme"); + if ( + user.customThemes === undefined || + user.customThemes.filter((t) => t._id == _id).length === 0 + ) + throw new MonkeyError(404, "Custom Theme not found"); + + return await mongoDB() + .collection("users") + .updateOne( + { + uid: uid, + "customThemes._id": ObjectID(_id), + }, + { $pull: { customThemes: { _id: ObjectID(_id) } } } + ); + } + + + static async editTheme(uid, _id, theme) { + const user = await mongoDB().collection("users").findOne({ uid }); + if (!user) throw new MonkeyError(404, "User not found", "edit theme"); + if ( + user.customThemes === undefined || + user.customThemes.filter((t) => t._id == _id).length === 0 + ) + throw new MonkeyError(404, "Custom Theme not found"); + + + return await mongoDB() + .collection("users") + .updateOne( + { + uid: uid, + "customThemes._id": ObjectID(_id), + }, + { + $set: { + "customThemes.$.name": theme.name, + "customThemes.$.colors": theme.colors + } + } + ); + } + + static async getThemes(uid) { + const user = await mongoDB().collection("users").findOne({ uid }); + if (!user) throw new MonkeyError(404, "User not found", "get themes"); + return user.customThemes; + } } module.exports = UsersDAO; diff --git a/backend/handlers/validation.js b/backend/handlers/validation.js index 480410fa2..dd543416e 100644 --- a/backend/handlers/validation.js +++ b/backend/handlers/validation.js @@ -93,15 +93,18 @@ function isThemeValid(theme) { if (theme.constructor != Object) return false; if (theme.name === null || theme.name === undefined || validateObjectValues(theme.name) != 0) return false; - - if (theme.colors === null || theme.colors === undefined || validateObjectValues(theme.colors) != 0) return false; + + if (theme.colors === null || theme.colors === undefined) return false; // Make sure the theme contains all the colors if (theme.colors.length != 9) return false; // Make sure all colors contain # symbol and length is 7 - for (let i = 0; i < 10; i++) + console.log(theme) + for (let i = 0; i < 9; i++){ + console.log(theme.colors[i]) if (theme.colors[i][0] != "#" || theme.colors[i].length != 7) return false; + } return true; } diff --git a/backend/middlewares/rate-limit.js b/backend/middlewares/rate-limit.js index a6d589780..a09e44d70 100644 --- a/backend/middlewares/rate-limit.js +++ b/backend/middlewares/rate-limit.js @@ -247,6 +247,27 @@ exports.userCustomThemeAdd = rateLimit({ keyGenerator: getAddress, }); +exports.userCustomThemeRemove = rateLimit({ + windowMs: 60 * 60 * 1000, // 60 min + max: 30 * multiplier, + message, + keyGenerator: getAddress, +}); + +exports.userCustomThemeEdit = rateLimit({ + windowMs: 60 * 60 * 1000, // 60 min + max: 30 * multiplier, + message, + keyGenerator: getAddress, +}); + +exports.userCustomThemeGet = rateLimit({ + windowMs: 60 * 60 * 1000, // 60 min + max: 30 * multiplier, + message, + keyGenerator: getAddress, +}); + exports.userDiscordLink = exports.usersTagsEdit = rateLimit({ windowMs: 60 * 60 * 1000, // 60 min max: 15 * multiplier,