diff --git a/src/routers/api/accountRouter.js b/src/routers/api/accountRouter.js index 6bca190..8377d36 100644 --- a/src/routers/api/accountRouter.js +++ b/src/routers/api/accountRouter.js @@ -18,7 +18,7 @@ along with this program. If not, see .*/ const { Router } = require('express'); //local imports -const {accountValidator} = require("../../utils/validators"); +const accountValidator = require("../../validators/accountValidator"); const loginController = require("../../controllers/api/account/loginController"); const logoutController = require("../../controllers/api/account/logoutController"); const registerController = require("../../controllers/api/account/registerController"); diff --git a/src/routers/api/adminRouter.js b/src/routers/api/adminRouter.js index d94c11d..d2cbb15 100644 --- a/src/routers/api/adminRouter.js +++ b/src/routers/api/adminRouter.js @@ -19,7 +19,7 @@ const { Router } = require('express'); //local imports -const {accountValidator} = require("../../utils/validators"); +const accountValidator = require("../../validators/accountValidator"); const permissionSchema = require("../../schemas/permissionSchema"); const listUsersController = require("../../controllers/api/admin/listUsersController"); const listChannelsController = require("../../controllers/api/admin/listChannelsController"); @@ -29,7 +29,7 @@ const changeRankController = require("../../controllers/api/admin/changeRankCont const router = Router(); //Use authentication middleware -router.use(permissionSchema.reqPermCheck("adminPanel")) +router.use(permissionSchema.reqPermCheck("adminAPI")); //routing functions router.get('/listUsers', listUsersController.get); diff --git a/src/routers/api/channelRouter.js b/src/routers/api/channelRouter.js index 5562b66..68ea401 100644 --- a/src/routers/api/channelRouter.js +++ b/src/routers/api/channelRouter.js @@ -19,7 +19,7 @@ const { Router } = require('express'); //local imports const permissionSchema = require("../../schemas/permissionSchema"); -const {channelValidator} = require("../../utils/validators"); +const channelValidator = require("../../validators/channelValidator"); const registerController = require("../../controllers/api/channel/registerController"); const listController = require("../../controllers/api/channel/listController"); const settingsController = require("../../controllers/api/channel/settingsController"); diff --git a/src/schemas/permissionSchema.js b/src/schemas/permissionSchema.js index ba2e384..1fc2bc9 100644 --- a/src/schemas/permissionSchema.js +++ b/src/schemas/permissionSchema.js @@ -26,6 +26,12 @@ const permissionSchema = new mongoose.Schema({ default: "admin", required: true }, + adminAPI: { + type: mongoose.SchemaTypes.String, + enum: rankEnum, + default: "admin", + required: true + }, registerChannel: { type: mongoose.SchemaTypes.String, enum: rankEnum, diff --git a/src/utils/validators.js b/src/validators/accountValidator.js similarity index 73% rename from src/utils/validators.js rename to src/validators/accountValidator.js index 3faf476..063d895 100644 --- a/src/utils/validators.js +++ b/src/validators/accountValidator.js @@ -26,13 +26,13 @@ function isRank(value){ return rankVal != -1; } -module.exports.accountValidator = { +module.exports = { user: (field = 'user') => body(field).escape().trim().isLength({min: 1, max: 22}), //Password security requirements may change over time, therefore we should only validate against strongPassword() when creating new accounts //that way we don't break old ones upon change pass: (field = 'pass') => body(field).notEmpty().escape().trim(), - securePass: (field) => this.accountValidator.pass(field).isStrongPassword({minLength: 8, minLowercase: 1, minUppercase: 1, minNumbers: 1, minSymbols: 1}), + securePass: (field) => module.exports.pass(field).isStrongPassword({minLength: 8, minLowercase: 1, minUppercase: 1, minNumbers: 1, minSymbols: 1}), email: (field = 'email') => body(field).optional().isEmail().normalizeEmail(), @@ -43,19 +43,4 @@ module.exports.accountValidator = { bio: (field = 'bio') => body(field).optional().escape().trim().isLength({min: 1, max: 1000}), rank: (field = 'rank') => body(field).escape().trim().custom(isRank) -} - -module.exports.channelValidator = { - name: (field = 'name') => check(field).escape().trim().isLength({min: 1, max: 50}), - - description: (field = 'description') => body(field).escape().trim().isLength({min: 1, max: 1000}), - - thumbnail: (field = 'thumbnail') => this.accountValidator.img(field), - - settingsMap: () => checkExact(checkSchema({ - 'settingsMap.hidden': { - optional: true, - isBoolean: true, - } - })) } \ No newline at end of file diff --git a/src/validators/channelValidator.js b/src/validators/channelValidator.js new file mode 100644 index 0000000..e74a9b9 --- /dev/null +++ b/src/validators/channelValidator.js @@ -0,0 +1,36 @@ +/*Canopy - The next generation of stoner streaming software +Copyright (C) 2024 Rainbownapkin and the TTN Community + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as +published by the Free Software Foundation, either version 3 of the +License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see .*/ + +//NPM Imports +const { check, body, checkSchema, checkExact} = require('express-validator'); + +//local imports +const accountValidator = require('./accountValidator'); + +module.exports = { + name: (field = 'name') => check(field).escape().trim().isLength({min: 1, max: 50}), + + description: (field = 'description') => body(field).escape().trim().isLength({min: 1, max: 1000}), + + thumbnail: (field = 'thumbnail') => accountValidator.img(field), + + settingsMap: () => checkExact(checkSchema({ + 'settingsMap.hidden': { + optional: true, + isBoolean: true, + } + })) +} \ No newline at end of file diff --git a/www/js/profile.js b/www/js/profile.js index 7620549..a7ee1e3 100644 --- a/www/js/profile.js +++ b/www/js/profile.js @@ -63,11 +63,10 @@ class profileEditPrompt{ updateObj[this.field] = this.prompt.value; //contact server, and collect response - var response = await utils.ajax.updateProfile(updateObj); - var updated_content = (await response.json())[this.field]; + var updated_content = (await utils.ajax.updateProfile(updateObj))[this.field]; //Update label - if(response.status == 200){ + if(updated_content != null){ if(this.field == "img"){ this.content.src = updated_content; }else{ @@ -76,7 +75,6 @@ class profileEditPrompt{ } this.finish(); }else if(event.key == "Escape" || event.key == "Enter"){ - console.log(response); this.finish(); } } @@ -144,7 +142,7 @@ class passwordResetPrompt{ const response = await utils.ajax.updateProfile(updateObj); - if(response.status == 200){ + if(response != null){ //Return user homepage after good pass change, as we've probably been logged out by the server for security. window.location.pathname = '/'; } @@ -167,13 +165,7 @@ class deleteAccountPrompt{ async deletePrompt(event){ const pass = window.prompt("Warning: You are about to nuke your account off of the face of the fucking planet, no taksie-backsies.\n \n (todo: replace with dialog that has obscured password input) \n Enter your password to confirm."); - const response = await utils.ajax.deleteAccount(pass); - - if(response.status == 200){ - window.location.pathname = '/'; - }else{ - utils.ux.displayResponseError(await response.json()); - } + await utils.ajax.deleteAccount(pass); } } diff --git a/www/js/utils.js b/www/js/utils.js index c980c6c..dedb55a 100644 --- a/www/js/utils.js +++ b/www/js/utils.js @@ -137,6 +137,8 @@ class canopyAjaxUtils{ if(response.status == 200){ location = "/"; + }else{ + utils.ux.displayResponseError(await response.json()); } } @@ -151,6 +153,8 @@ class canopyAjaxUtils{ if(response.status == 200){ location.reload(); + }else{ + utils.ux.displayResponseError(await response.json()); } } @@ -161,27 +165,42 @@ class canopyAjaxUtils{ if(response.status == 200){ location.reload(); + }else{ + utils.ux.displayResponseError(await response.json()); } } + //We need to fix this one to use displayResponseError function async updateProfile(update){ - return await fetch(`/api/account/update`,{ + const response = await fetch(`/api/account/update`,{ method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(update) }); + + if(response.status == 200){ + return await response.json(); + }else{ + utils.ux.displayResponseError(await response.json()); + } } async deleteAccount(pass){ - return await fetch(`/api/account/delete`,{ + const response = await fetch(`/api/account/delete`,{ method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({pass}) }); + + if(response.status == 200){ + window.location.pathname = '/'; + }else{ + utils.ux.displayResponseError(await response.json()); + } } async newChannel(name, description, thumbnail){ @@ -195,6 +214,8 @@ class canopyAjaxUtils{ if(response.status == 200){ location = "/"; + }else{ + utils.ux.displayResponseError(await response.json()); } } @@ -210,6 +231,8 @@ class canopyAjaxUtils{ if(response.status == 200){ return await response.json(); + }else{ + utils.ux.displayResponseError(await response.json()); } } @@ -224,6 +247,8 @@ class canopyAjaxUtils{ if(response.status == 200){ location = "/"; + }else{ + utils.ux.displayResponseError(await response.json()); } } }