diff --git a/src/controllers/api/account/registerController.js b/src/controllers/api/account/registerController.js
index 4ab51c4..bcd5411 100644
--- a/src/controllers/api/account/registerController.js
+++ b/src/controllers/api/account/registerController.js
@@ -35,8 +35,7 @@ module.exports.post = async function(req, res){
//if we found any related nuked bans
if(nukedBans != null){
//Shit our pants!
- res.status(401);
- return res.send({errors:[{msg:"Cannot re-create banned account!",type:"unauthorized"}]});
+ return errorHandler(res, 'Cannon re-create banned account!', 'unauthorized');
}
await userModel.register(user)
diff --git a/src/controllers/api/admin/tokeCommandController.js b/src/controllers/api/admin/tokeCommandController.js
new file mode 100644
index 0000000..8231fc6
--- /dev/null
+++ b/src/controllers/api/admin/tokeCommandController.js
@@ -0,0 +1,64 @@
+/*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 {validationResult, matchedData} = require('express-validator');
+
+//local imports
+const {exceptionHandler, errorHandler} = require('../../../utils/loggerUtils.js');
+const tokeCommandModel = require('../../../schemas/tokebot/tokeCommandSchema.js');
+
+module.exports.get = async function(req, res){
+ try{
+ const tokeList = await tokeCommandModel.getCommandStrings();
+
+ res.status(200);
+ return res.send(tokeList);
+ }catch(err){
+ return exceptionHandler(res, err);
+ }
+}
+
+module.exports.post = async function(req, res){
+ try{
+ //get validation error results
+ const validResult = validationResult(req);
+
+ //if they're empty
+ if(validResult.isEmpty()){
+ const {command} = matchedData(req);
+ const foundToke = await tokeCommandModel.findOne({command});
+
+ if(foundToke != null){
+ return errorHandler(res, `Toke command '!${command}' already exists!`);
+ }
+
+ //Add the toke
+ const tokeDB = await tokeCommandModel.create({command});
+
+ //Return the updated command list
+ res.status(200);
+ return res.send(await tokeCommandModel.getCommandStrings());
+ }else{
+ //otherwise scream
+ res.status(400);
+ return res.send({errors: validResult.array()})
+ }
+
+ }catch(err){
+ return exceptionHandler(res, err);
+ }
+}
\ No newline at end of file
diff --git a/src/routers/api/accountRouter.js b/src/routers/api/accountRouter.js
index d22e2d7..df16f34 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("../../validators/accountValidator");
+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 1a0e254..d065d1a 100644
--- a/src/routers/api/adminRouter.js
+++ b/src/routers/api/adminRouter.js
@@ -20,14 +20,16 @@ const { Router } = require('express');
//local imports
-const {accountValidator} = require("../../validators/accountValidator");
+const accountValidator = require("../../validators/accountValidator");
const {permissionsValidator, channelPermissionValidator} = require("../../validators/permissionsValidator");
+const tokebotValidator = require("../../validators/tokebotValidator");
const permissionSchema = require("../../schemas/permissionSchema");
const listUsersController = require("../../controllers/api/admin/listUsersController");
const listChannelsController = require("../../controllers/api/admin/listChannelsController");
const changeRankController = require("../../controllers/api/admin/changeRankController");
const permissionsController = require("../../controllers/api/admin/permissionsController");
const banController = require("../../controllers/api/admin/banController");
+const tokeCommandController = require('../../controllers/api/admin/tokeCommandController');
//globals
const router = Router();
@@ -42,5 +44,7 @@ router.get('/ban', permissionSchema.reqPermCheck("adminPanel"), banController.g
//Sometimes they're so simple you don't need to put your validators in their own special place :P
router.post('/ban', permissionSchema.reqPermCheck("banUser"), accountValidator.user(), body("permanent").isBoolean(), body("expirationDays").isInt(), banController.post);
router.delete('/ban', permissionSchema.reqPermCheck("banUser"), accountValidator.user(), banController.delete);
+router.get('/tokeCommands', permissionSchema.reqPermCheck("adminPanel"), tokeCommandController.get);
+router.post('/tokeCommands', permissionSchema.reqPermCheck("editTokeCommands"), tokebotValidator.command(), tokeCommandController.post);
module.exports = router;
diff --git a/src/routers/api/channelRouter.js b/src/routers/api/channelRouter.js
index 88f85b9..2722938 100644
--- a/src/routers/api/channelRouter.js
+++ b/src/routers/api/channelRouter.js
@@ -21,8 +21,8 @@ const { Router } = require('express');
//local imports
const permissionSchema = require("../../schemas/permissionSchema");
const channelModel = require("../../schemas/channel/channelSchema");
-const {channelValidator} = require("../../validators/channelValidator");
-const {accountValidator} = require("../../validators/accountValidator");
+const channelValidator = require("../../validators/channelValidator");
+const accountValidator = require("../../validators/accountValidator");
const {channelPermissionValidator} = require("../../validators/permissionsValidator");
const registerController = require("../../controllers/api/channel/registerController");
const listController = require("../../controllers/api/channel/listController");
diff --git a/src/schemas/permissionSchema.js b/src/schemas/permissionSchema.js
index 8f361bd..a8354e9 100644
--- a/src/schemas/permissionSchema.js
+++ b/src/schemas/permissionSchema.js
@@ -51,6 +51,12 @@ const permissionSchema = new mongoose.Schema({
default: "admin",
required: true
},
+ editTokeCommands: {
+ type: mongoose.SchemaTypes.String,
+ enum: rankEnum,
+ default: "admin",
+ required: true
+ },
banUser: {
type: mongoose.SchemaTypes.String,
enum: rankEnum,
diff --git a/src/utils/loggerUtils.js b/src/utils/loggerUtils.js
index fc38e50..049f716 100644
--- a/src/utils/loggerUtils.js
+++ b/src/utils/loggerUtils.js
@@ -15,10 +15,15 @@ You should have received a copy of the GNU Affero General Public License
along with this program. If not, see .*/
//At some point this will be a bit more advanced, right now it's just a placeholder :P
+module.exports.errorHandler = function(res, msg, type = "Generic"){
+ res.status(400);
+ return res.send({errors: [{type, msg, date: new Date()}]});
+}
+
module.exports.exceptionHandler = function(res, err){
//if not yell at the browser for fucking up, and tell it what it did wrong.
res.status(400);
- return res.send({errors: [{type: "Caught Exception", msg: err.message, date: new Date()}]});
+ module.exports.errorHandler(res, err.message, "Caught Exception");
}
module.exports.socketExceptionHandler = function(socket, err){
diff --git a/src/validators/accountValidator.js b/src/validators/accountValidator.js
index 16f562d..d875abd 100644
--- a/src/validators/accountValidator.js
+++ b/src/validators/accountValidator.js
@@ -20,13 +20,13 @@ const { check, body, checkSchema, checkExact} = require('express-validator');
//local imports
const {isRank} = require('./permissionsValidator');
-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) => module.exports.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(),
diff --git a/src/validators/channelValidator.js b/src/validators/channelValidator.js
index d4baafc..3309c30 100644
--- a/src/validators/channelValidator.js
+++ b/src/validators/channelValidator.js
@@ -18,9 +18,9 @@ along with this program. If not, see .*/
const { check, body, checkSchema, checkExact} = require('express-validator');
//local imports
-const {accountValidator} = require('./accountValidator');
+const accountValidator = require('./accountValidator');
-module.exports.channelValidator = {
+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}),
diff --git a/src/validators/permissionsValidator.js b/src/validators/permissionsValidator.js
index a244b5f..1ff4662 100644
--- a/src/validators/permissionsValidator.js
+++ b/src/validators/permissionsValidator.js
@@ -52,6 +52,12 @@ module.exports.permissionsValidator = {
options: module.exports.isRank
},
},
+ 'permissionsMap.editTokeCommands': {
+ optional: true,
+ custom: {
+ options: module.exports.isRank
+ }
+ },
'permissionsMap.banUser': {
optional: true,
custom: {
diff --git a/src/validators/tokebotValidator.js b/src/validators/tokebotValidator.js
new file mode 100644
index 0000000..67744e5
--- /dev/null
+++ b/src/validators/tokebotValidator.js
@@ -0,0 +1,22 @@
+/*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 } = require('express-validator');
+
+module.exports = {
+ command: (field = 'command') => check(field).escape().trim().isAlphanumeric().isLength({min: 1, max: 30}),
+}
\ No newline at end of file
diff --git a/www/js/adminPanel.js b/www/js/adminPanel.js
index d48e82c..8637d90 100644
--- a/www/js/adminPanel.js
+++ b/www/js/adminPanel.js
@@ -169,6 +169,35 @@ class canopyAdminUtils{
utils.ux.displayResponseError(await response.json());
}
}
+
+ async getTokeCommands(){
+ var response = await fetch(`/api/admin/tokeCommands`,{
+ method: "GET"
+ });
+
+ if(response.status == 200){
+ return await response.json();
+ }else{
+ utils.ux.displayResponseError(await response.json());
+ }
+ }
+
+ async addTokeCommand(command){
+ var response = await fetch(`/api/admin/tokeCommands`,{
+ method: "POST",
+ headers: {
+ "Content-Type": "application/json"
+ },
+ //Unfortunately JSON doesn't natively handle ES6 maps, and god forbid someone update the standard in a way that's backwards compatible...
+ body: JSON.stringify({command})
+ });
+
+ if(response.status == 200){
+ return await response.json();
+ }else{
+ utils.ux.displayResponseError(await response.json());
+ }
+ }
}
class adminUserList{