Worked ban frontend and api.

This commit is contained in:
rainbow napkin 2024-11-29 08:00:25 -05:00
parent 5c936462a6
commit c848994c1d
18 changed files with 513 additions and 41 deletions

View file

@ -36,6 +36,31 @@ module.exports.get = async function(req, res){
}
module.exports.post = async function(req, res){
try{
const validResult = validationResult(req);
if(validResult.isEmpty()){
const {user, permanent, expirationDays} = matchedData(req);
const userDB = await userModel.findOne({user});
if(userDB == null){
res.status(400);
return res.send({errors:[{type: "Bad Query", msg: "User not found.", date: new Date()}]});
}
await banModel.banByUserDoc(userDB, permanent, expirationDays);
res.status(200);
return res.send(await banModel.getBans());
}else{
res.status(400);
return res.send({errors: validResult.array()})
}
}catch(err){
return exceptionHandler(res, err);
}
}
module.exports.delete = async function(req, res){
try{
const validResult = validationResult(req);
if(validResult.isEmpty()){
@ -44,11 +69,13 @@ module.exports.post = async function(req, res){
if(userDB == null){
res.status(400);
res.send({errors:[{type: "Bad Query", msg: "User not found.", date: new Date()}]});
return res.send({errors:[{type: "Bad Query", msg: "User not found.", date: new Date()}]});
}
await banModel.banByUserDoc(userDB);
await banModel.unbanByUserDoc(userDB);
res.status(200);
return res.send(await banModel.getBans());
}else{
res.status(400);
return res.send({errors: validResult.array()})

View file

@ -0,0 +1,20 @@
/*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 <https://www.gnu.org/licenses/>.*/
//root index functions
module.exports.get = async function(req, res){
res.render('partial/popup/placeholder', {});
}

View file

@ -0,0 +1,20 @@
/*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 <https://www.gnu.org/licenses/>.*/
//root index functions
module.exports.get = async function(req, res){
res.render('partial/popup/userBan');
}

View file

@ -15,7 +15,7 @@ You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.*/
//npm imports
const { checkExact } = require('express-validator');
const { body, checkExact} = require('express-validator');
const { Router } = require('express');
@ -42,6 +42,8 @@ router.get('/permissions', permissionsController.get);
router.post('/permissions', checkExact([permissionsValidator.permissionsMap(), channelPermissionValidator.channelPermissionsMap()]), permissionsController.post);
router.post('/changeRank', accountValidator.user(), accountValidator.rank(), changeRankController.post);
router.get('/ban', banController.get);
router.post('/ban', accountValidator.user(), banController.post);
//Sometimes they're so simple you don't need to put your validators in their own special place :P
router.post('/ban', accountValidator.user(), body("permanent").isBoolean(), body("expirationDays").isInt(), banController.post);
router.delete('/ban', accountValidator.user(), banController.delete);
module.exports = router;

View file

@ -0,0 +1,32 @@
/*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 <https://www.gnu.org/licenses/>.*/
//npm imports
const { Router } = require('express');
//local imports
const placeholderController = require("../controllers/popup/placeholderController");
const userBanController = require("../controllers/popup/userBanController");
//globals
const router = Router();
//routing functions
router.get('/placeholder', placeholderController.get);
router.get('/userBan', userBanController.get);
module.exports = router;

View file

@ -48,7 +48,7 @@ const userBanSchema = new mongoose.Schema({
default: 30
},
//If true, then expiration date deletes associated accounts instead of deleting the ban record
deleteAccountOnExpire: {
permanent: {
type: mongoose.SchemaTypes.Boolean,
required: true,
default: false
@ -73,17 +73,58 @@ userBanSchema.statics.checkBan = async function(user){
return this.checkBanByUserDoc(userDB);
}
userBanSchema.statics.banByUserDoc = async function(userDB){
userBanSchema.statics.banByUserDoc = async function(userDB, permanent, expirationDays){
//Prevent missing users
if(userDB == null){
throw new Error("User not found")
}
//Ensure the user isn't already banned
if(await this.checkBanByUserDoc(userDB) != null){
throw new Error("User already banned");
}
return await this.create({user: userDB._id});
if(expirationDays < 0){
throw new Error("Expiration Days must be a positive integer!");
}else if(expirationDays < 30 && permanent){
throw new Error("Permanent bans must be given at least 30 days before automatic account deletion!");
}else if(expirationDays > 185){
throw new Error("Expiration/Deletion date cannot be longer than half a year out from the original ban date.");
}
//Log the user out
await userDB.killAllSessions();
//Add the ban to the database
return await this.create({user: userDB._id, permanent, expirationDays});
}
userBanSchema.statics.ban = async function(user){
userBanSchema.statics.ban = async function(user, permanent, expirationDays){
const userDB = await userModel.findOne({user: user.user});
return this.banByUserDoc(userDB);
return this.banByUserDoc(userDB, permanent, expirationDays);
}
userBanSchema.statics.unbanByUserDoc = async function(userDB){
//Prevent missing users
if(userDB == null){
throw new Error("User not found")
}
const ban = await this.checkBanByUserDoc(userDB);
if(!ban){
throw new Error("User already un-banned");
}
//Use _id in-case mongoose wants to be a cunt
var oldBan = await this.deleteOne({_id: ban._id});
return oldBan;
}
userBanSchema.statics.unban = async function(user){
const userDB = await userModel.findOne({user: user.user});
return this.unbanByUserDoc(userDB);
}
userBanSchema.statics.getBans = async function(){
@ -109,7 +150,7 @@ userBanSchema.statics.getBans = async function(){
user: userObj,
ips: ban.ips,
alts: ban.alts,
deleteAccountOnExpire: ban.deleteAccountOnExpire
permanent: ban.permanent
}
bans.push(banObj);

View file

@ -211,7 +211,7 @@ userSchema.statics.getUserList = async function(fullList = false){
//create a user object with limited properties (safe for public consumption)
var userObj = {
id: user.id,
name: user.user,
user: user.user,
img: user.img,
date: user.date
}
@ -270,7 +270,7 @@ userSchema.methods.nuke = async function(pass){
if(this.checkPass(pass)){
//Annoyingly there isnt a good way to do this from 'this'
var oldUser = await module.exports.deleteOne(this);
var oldUser = await module.exports.userModel.deleteOne(this);
if(oldUser){
await this.killAllSessions();

View file

@ -34,6 +34,7 @@ const adminPanelRouter = require('./routers/adminPanelRouter');
const channelRouter = require('./routers/channelRouter');
const newChannelRouter = require('./routers/newChannelRouter');
const panelRouter = require('./routers/panelRouter');
const popupRouter = require('./routers/popupRouter');
const apiRouter = require('./routers/apiRouter');
//Define Config
@ -92,6 +93,8 @@ app.use('/c', channelRouter);
app.use('/newChannel', newChannelRouter);
//Panel
app.use('/panel', panelRouter);
//Popup
app.use('/popup', popupRouter);
//Bot-Ready
app.use('/api', apiRouter);

View file

@ -38,6 +38,9 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.-->
<td id="admin-ban-list-entry-expiration-type-title" class="admin-list-entry admin-list-entry-title admin-list-entry-item admin-list-entry-not-first-col">
<h3>Expiration Action</h3>
</td>
<td id="admin-ban-list-entry-actions-type-title" class="admin-list-entry admin-list-entry-title admin-list-entry-item admin-list-entry-not-first-col">
<h3>Actions</h3>
</td>
</tr>
</table>
</div>

View file

@ -29,33 +29,36 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.-->
<td id="admin-user-list-entry-rank-title" class="admin-list-entry admin-list-entry-title admin-list-entry-item admin-list-entry-not-first-col">
<h3>Rank</h3>
</td>
<td id="admin-user-list-entry-rank-title" class="admin-list-entry admin-list-entry-title admin-list-entry-item admin-list-entry-not-first-col">
<td id="admin-user-list-entry-mail-title" class="admin-list-entry admin-list-entry-title admin-list-entry-item admin-list-entry-not-first-col">
<h3>E-Mail</h3>
</td>
<td id="admin-user-list-entry-rank-title" class="admin-list-entry admin-list-entry-title admin-list-entry-item admin-list-entry-not-first-col">
<td id="admin-user-list-entry-date-title" class="admin-list-entry admin-list-entry-title admin-list-entry-item admin-list-entry-not-first-col">
<h3>Sign-Up Date</h3>
</td>
<td id="admin-user-list-entry-control-title" class="admin-list-entry admin-list-entry-title admin-list-entry-item admin-list-entry-not-first-col">
<h3>Actions</h3>
</td>
</tr>
<% userList.forEach((curUser) => { %>
<tr id="admin-user-list-entry-<%- curUser.name %>" class="admin-list-entry">
<td id="admin-user-list-entry-img-<%- curUser.name %>" class="admin-list-entry admin-list-entry-item">
<a href="/profile/<%- curUser.name %>" class="admin-list-entry admin-list-entry-item">
<img id="admin-user-list-entry-img-<%- curUser.name %>" class="admin-list-entry admin-list-entry-item" src="<%- curUser.img %>">
<tr id="admin-user-list-entry-<%- curUser.user %>" class="admin-list-entry">
<td id="admin-user-list-entry-img-<%- curUser.user %>" class="admin-list-entry admin-list-entry-item">
<a href="/profile/<%- curUser.user %>" class="admin-list-entry admin-list-entry-item">
<img id="admin-user-list-entry-img-<%- curUser.user %>" class="admin-list-entry admin-list-entry-item" src="<%- curUser.img %>">
</a>
</td>
<td id="admin-user-list-entry-id-<%- curUser.name %>" class="admin-list-entry admin-list-entry-item admin-list-entry-not-first-col">
<a href="/profile/<%- curUser.name %>" class="admin-list-entry admin-list-entry-item">
<td id="admin-user-list-entry-id-<%- curUser.user %>" class="admin-list-entry admin-list-entry-item admin-list-entry-not-first-col">
<a href="/profile/<%- curUser.user %>" class="admin-list-entry admin-list-entry-item">
<%- curUser.id %>
</a>
</td>
<td id="admin-user-list-entry-name-<%- curUser.name %>" class="admin-list-entry admin-list-entry-item admin-list-entry-not-first-col">
<a href="/profile/<%- curUser.name %>" class="admin-list-entry admin-list-entry-item">
<%- curUser.name %>
<td id="admin-user-list-entry-name-<%- curUser.user %>" class="admin-list-entry admin-list-entry-item admin-list-entry-not-first-col">
<a href="/profile/<%- curUser.user %>" class="admin-list-entry admin-list-entry-item">
<%- curUser.user %>
</a>
</td>
<td id="admin-user-list-entry-rank-<%- curUser.name %>" class="admin-list-entry admin-list-entry-item admin-list-entry-not-first-col">
<td id="admin-user-list-entry-rank-<%- curUser.user %>" class="admin-list-entry admin-list-entry-item admin-list-entry-not-first-col">
<% if(rankEnum.indexOf(curUser.rank) < rankEnum.indexOf(user.rank)){%>
<select id="admin-user-list-rank-select-<%- curUser.name %>" class="admin-user-list-rank-select">
<select id="admin-user-list-rank-select-<%- curUser.user %>" class="admin-user-list-rank-select">
<%rankEnum.slice().reverse().forEach((rank)=>{ %>
<option <%if(curUser.rank == rank){%> selected <%}%> value="<%- rank %>"><%- rank %></option>
<% }); %>
@ -64,12 +67,16 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.-->
<%- curUser.rank %>
<% } %>
</td>
<td id="admin-user-list-entry-email-<%- curUser.name %>" class="admin-list-entry admin-list-entry-item admin-list-entry-not-first-col">
<td id="admin-user-list-entry-email-<%- curUser.user %>" class="admin-list-entry admin-list-entry-item admin-list-entry-not-first-col">
<%- curUser.email ? curUser.email : "N/A" %>
</td>
<td id="admin-user-list-entry-date-<%- curUser.name %>" class="admin-list-entry admin-list-entry-item admin-list-entry-not-first-col">
<td id="admin-user-list-entry-date-<%- curUser.user %>" class="admin-list-entry admin-list-entry-item admin-list-entry-not-first-col">
<%- curUser.date.toUTCString() %>
</td>
<td id="admin-user-list-entry-action-<%- curUser.user %>" class="admin-list-entry admin-list-entry-item admin-list-entry-not-first-col">
<!-- It's either this or add whitespce >:( -->
<i class="bi-radioactive admin-user-list-icon admin-user-list-nuke-icon" id="admin-user-list-nuke-icon-<%- curUser.user %>" title="Nuke Account: <%- curUser.user %>"></i><i class="bi-fire admin-user-list-icon admin-user-list-ban-icon" id="admin-user-list-ban-icon-<%- curUser.user %>" title="Ban User: <%- curUser.user %>"></i><i class="bi-arrow-clockwise admin-user-list-icon admin-user-list-pw-reset-icon" id="admin-user-list-pw-reset-icon-<%- curUser.user %>" title="Generate Password Reset Link for <%- curUser.user %>"></i>
</td>
</tr>
<% }); %>
</table>

View file

@ -0,0 +1,16 @@
<!--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 <https://www.gnu.org/licenses/>.-->
<h1>This is a test popup!</h1>

View file

@ -0,0 +1,33 @@
<!--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 <https://www.gnu.org/licenses/>.-->
<link rel="stylesheet" type="text/css" href="/css/popup/userBan.css">
<h3 class="popup-title">Ban NULL</h3>
<form class="ban-popup-content" action="javascript:">
<span>
<label for="ban-popup-perm">Perma-Ban:</label>
<input type="checkbox" name="ban-popup-perm" id="ban-popup-perm">
</span>
<span>
<label id="ban-popup-expiration-prefix" for="ban-popup-expiration">Ban Expires In: </label>
<input type="number" name="ban-popup-expiration" id="ban-popup-expiration" value="14">
<label fid="ban-popup-expiration-postfix" or="ban-popup-expiration"> Days</label>
</span>
<span id="ban-popup-button-span">
<button id="ban-popup-cancel-button">Cancel</button>
<span id="ban-popup-button-span-spacer"></span>
<button id="ban-popup-ban-button">Ban</button>
</span>
</form>