From 7624e1928a9e2aceafd8fab669e8f48db9a17302 Mon Sep 17 00:00:00 2001 From: rainbow napkin Date: Wed, 1 Jan 2025 23:00:03 -0500 Subject: [PATCH] Updated ban UI to handle IP-Bans. --- src/schemas/user/userBanSchema.js | 27 ++- src/schemas/user/userSchema.js | 7 +- src/validators/accountValidator.js | 7 +- src/views/partial/adminPanel/userBanList.ejs | 20 +-- src/views/partial/popup/userBan.ejs | 4 + www/css/adminPanel.css | 4 - www/js/adminPanel.js | 166 ++++++++++++++----- www/js/utils.js | 1 + 8 files changed, 165 insertions(+), 71 deletions(-) diff --git a/src/schemas/user/userBanSchema.js b/src/schemas/user/userBanSchema.js index 947730d..8af9502 100644 --- a/src/schemas/user/userBanSchema.js +++ b/src/schemas/user/userBanSchema.js @@ -306,24 +306,31 @@ userBanSchema.statics.unban = async function(user){ } userBanSchema.statics.getBans = async function(){ - const banDB = await this.find({}).populate('user'); + //Get the ban, populating users and alts + const banDB = await this.find({}).populate('user').populate('alts'); + //Create an empty array to hold ban records var bans = []; banDB.forEach((ban) => { - //Calcualte expiration date + //Create array to hold alts + var alts = []; + //Calculate expiration date var expirationDate = new Date(ban.banDate); expirationDate.setDate(expirationDate.getDate() + ban.expirationDays); //Make sure we're not about to read the properties of a null object if(ban.user != null){ - var userObj = { - id: ban.user.id, - user: ban.user.user, - img: ban.user.img, - date: ban.user.date - } + var userObj = ban.user.getProfile(); } + + //For each alt in the ban + for(alt of ban.alts){ + //Get the profile and push it to the alt list + alts.push(alt.getProfile()); + } + + //Create ban object const banObj = { banDate: ban.banDate, expirationDays: ban.expirationDays, @@ -331,14 +338,16 @@ userBanSchema.statics.getBans = async function(){ daysUntilExpiration: ban.getDaysUntilExpiration(), user: userObj, ips: ban.ips, - alts: ban.alts, + alts, deletedNames: ban.deletedNames, permanent: ban.permanent } + //Add it to the array bans.push(banObj); }); + //Return the array return bans; } diff --git a/src/schemas/user/userSchema.js b/src/schemas/user/userSchema.js index ab84e66..0e3d271 100644 --- a/src/schemas/user/userSchema.js +++ b/src/schemas/user/userSchema.js @@ -74,22 +74,23 @@ const userSchema = new mongoose.Schema({ required: true, default: "/img/johnny.png" }, + //These should be larger than validator values to make room for escaped characters bio: { type: mongoose.SchemaTypes.String, required: true, - maxLength: 1000, + maxLength: 2000, default: "Bio not set!" }, pronouns:{ type: mongoose.SchemaTypes.String, optional: true, - maxLength: 20, + maxLength: 50, default: "" }, signature: { type: mongoose.SchemaTypes.String, required: true, - maxLength: 150, + maxLength: 300, default: "Signature not set!" }, highLevel: { diff --git a/src/validators/accountValidator.js b/src/validators/accountValidator.js index 377ecbf..9d1e9ed 100644 --- a/src/validators/accountValidator.js +++ b/src/validators/accountValidator.js @@ -32,11 +32,12 @@ module.exports = { img: (field = 'img') => body(field).optional().isURL({require_tld: false, require_host: false}).trim(), - pronouns: (field = 'pronouns') => body(field).optional().escape().trim().isLength({min: 0, max: 15}), + //Length check before escaping to keep symbols from throwing the count + pronouns: (field = 'pronouns') => body(field).optional().trim().isLength({min: 0, max: 15}).escape(), - signature: (field = 'signature') => body(field).optional().escape().trim().isLength({min: 1, max: 25}), + signature: (field = 'signature') => body(field).optional().trim().isLength({min: 1, max: 25}).escape(), - bio: (field = 'bio') => body(field).optional().escape().trim().isLength({min: 1, max: 1000}), + bio: (field = 'bio') => body(field).optional().trim().isLength({min: 1, max: 1000}).escape(), rank: (field = 'rank') => body(field).escape().trim().custom(isRank), diff --git a/src/views/partial/adminPanel/userBanList.ejs b/src/views/partial/adminPanel/userBanList.ejs index 50666ba..d17db95 100644 --- a/src/views/partial/adminPanel/userBanList.ejs +++ b/src/views/partial/adminPanel/userBanList.ejs @@ -18,17 +18,17 @@ along with this program. If not, see . %>
- + + - - -
-

Img

+
+

Main
User

+
+

Alt
Users

+
+

Nuked
Users

-

ID

-
-

Name

-
-

Sign-Up
Date

+
+

Banned
IPs

Ban
Date

diff --git a/src/views/partial/popup/userBan.ejs b/src/views/partial/popup/userBan.ejs index 94e6c72..b3c91c4 100644 --- a/src/views/partial/popup/userBan.ejs +++ b/src/views/partial/popup/userBan.ejs @@ -16,6 +16,10 @@ along with this program. If not, see . %>
+ + + + diff --git a/www/css/adminPanel.css b/www/css/adminPanel.css index 6f34016..25b3676 100644 --- a/www/css/adminPanel.css +++ b/www/css/adminPanel.css @@ -98,10 +98,6 @@ img.admin-list-entry-item{ width: 8em; } -#admin-ban-list-entry-expiration-date-title{ - width: 11em; -} - div.toke-command-list{ display: grid; grid-template-columns: repeat(auto-fit, minmax(15em, 1fr)); diff --git a/www/js/adminPanel.js b/www/js/adminPanel.js index 819f563..e707296 100644 --- a/www/js/adminPanel.js +++ b/www/js/adminPanel.js @@ -32,6 +32,7 @@ class canopyAdminUtils{ //Setup title text real quick-like :P this.title.innerHTML = `Ban ${this.target}`; + this.ipBan = document.querySelector("#ban-popup-ip"); this.permBan = document.querySelector("#ban-popup-perm"); this.expiration = document.querySelector("#ban-popup-expiration"); this.expirationPrefix = document.querySelector("#ban-popup-expiration-prefix"); @@ -62,7 +63,8 @@ class canopyAdminUtils{ this.popup.closePopup(); //Submit the user ban based on input - const data = await adminUtil.banUser(this.target, this.permBan.checked, this.expiration.value); + console.log(this.ipBan.checked) + const data = await adminUtil.banUser(this.target, this.permBan.checked, this.ipBan.checked, this.expiration.value); //For some reason comparing this against undefined or null wasnt working in and of itself... if(data != null){ @@ -457,66 +459,146 @@ class adminUserBanList{ } renderBanList(banList){ + //Clear out the ban list this.clearBanList(); + + //For each ban received banList.forEach((ban) => { //Calculate expiration date and expiration days - var expirationDateString = `${new Date(ban.expirationDate).toDateString()}
(${ban.daysUntilExpiration} day(s) left)`; - var banActionString = ban.permanent ? "Nuke
Accounts" : "Un-Ban"; - if(ban.user == null){ + let expirationDateString = `${new Date(ban.expirationDate).toLocaleDateString()}
(${ban.daysUntilExpiration} day(s) left)`; + let banActionString = ban.permanent ? "Nuke
Accounts" : "Un-Ban"; + let nuked = ban.user == null; + + //If the user is null (the ban has been nuked) + if(nuked){ //Fudge the user object if it's already been deleted ban.user = { - img: "/img/nuked.png", - id: "-", - user: ban.deletedNames[0] ? ban.deletedNames[0] : "UNKNOWN", - deleted: true + //Use dead name if we got one + user: ban.deletedNames[0] != null ? ban.deletedNames[0] : "Nuked" }; + ban.alts[0] = { + user: "Nuked" + } + //Fake the display string - var signUpDateString = "-" expirationDateString = "Accounts
Nuked" banActionString = "Accounts
Nuked" - - }else{ - var signUpDateString = new Date(ban.user.date).toDateString() } - //Create entry row - const entryRow = document.createElement('tr'); - entryRow.classList.add("admin-list-entry"); - - //Create IMG node inside of IMG cell - const imgNode = document.createElement('img'); - imgNode.classList.add("admin-list-entry","admin-list-entry-item"); - imgNode.src = ban.user.img; - - //Create unban icon - const unbanIcon = document.createElement('i'); - unbanIcon.classList.add("bi-emoji-smile-fill","admin-user-list-icon","admin-user-list-unban-icon"); - unbanIcon.id = `admin-user-list-unban-icon-${ban.user.user}`; - unbanIcon.title = `Unban ${ban.user.user}`; - unbanIcon.addEventListener("click", this.unban.bind(this)); - - //Create nuke account icon - const nukeAccount = document.createElement('i'); - nukeAccount.classList.add("bi-radioactive","admin-user-list-icon","admin-user-list-unban-icon"); - nukeAccount.id = `admin-user-list-unban-icon-${ban.user.user}`; - nukeAccount.title = `Nuke accounts`; - nukeAccount.addEventListener("click",console.log); - - //append img cell to row - entryRow.appendChild(utils.ux.newTableCell(imgNode, true)); //Generate and append row to table this.table.appendChild(utils.ux.newTableRow([ - ban.user.id, - ban.user.user, - signUpDateString, - new Date(ban.banDate).toDateString(), + this.renderUser(ban.user, nuked), + this.renderUsers(ban.alts, nuked), + this.renderDeadUsers(ban.deletedNames), + this.renderIPs(ban.ips), + new Date(ban.banDate).toLocaleDateString(), expirationDateString, banActionString, - (ban.user.deleted ? unbanIcon : [unbanIcon, nukeAccount]) + this.renderIcons(ban.user) ])); }); } + + renderUser(user, nuked = false){ + if(nuked){ + var userNode = document.createElement('img'); + userNode.classList.add("admin-list-entry","admin-list-entry-item"); + userNode.src = '/img/nuked.png' + userNode.title = "Nuked" + }else{ + var userNode = document.createElement('p'); + userNode.innerHTML = user.user; + } + return userNode; + } + + renderUsers(users, nuked){ + //Create userlist span + let userList = document.createElement('span'); + + if(!nuked){ + //For each user + for(let user of users){ + //Render out the user + userList.appendChild(this.renderUser(user)); + } + }else{ + userList = document.createElement('img'); + userList.classList.add("admin-list-entry","admin-list-entry-item"); + userList.src = '/img/nuked.png' + userList.title = "Nuked" + } + + //return our list + return userList; + } + + renderDeadUsers(users){ + //Create userlist span + const deadUsers = document.createElement('span'); + + //For each ip + for(let user of users){ + //Create a node + const userNode = document.createElement('p'); + //Fill it wit the ip + userNode.innerHTML = user; + //Append it + deadUsers.appendChild(userNode); + } + + //return our list + return deadUsers; + } + + renderIPs(ips){ + //Create userlist span + const ipList = document.createElement('span'); + + //For each ip + for(let ip of ips.plaintext){ + //Create a node + const ipNode = document.createElement('p'); + //Fill it wit the ip + ipNode.innerHTML = ip; + //Append it + ipList.appendChild(ipNode); + } + + //For each user + for(let hash of ips.hashed){ + //Create a node + const ipNode = document.createElement('p'); + //List it as a hashed ip with the hash as alt text + ipNode.innerHTML = '[Hashed IP]'; + ipNode.title = hash; + //Append the node + ipList.appendChild(ipNode); + } + + //return our list + return ipList; + } + + renderIcons(user){ + //Create unban icon + const unbanIcon = document.createElement('i'); + unbanIcon.classList.add("bi-emoji-smile-fill","admin-user-list-icon","admin-user-list-unban-icon"); + unbanIcon.id = `admin-user-list-unban-icon-${user.user}`; + unbanIcon.title = `Unban ${user.user}`; + unbanIcon.addEventListener("click", this.unban.bind(this)); + + //Create nuke account icon + const nukeAccount = document.createElement('i'); + nukeAccount.classList.add("bi-radioactive","admin-user-list-icon","admin-user-list-unban-icon"); + nukeAccount.id = `admin-user-list-unban-icon-${user.user}`; + nukeAccount.title = `Nuke accounts`; + nukeAccount.addEventListener("click",console.log); + + //If our user has been deleted don't return the nuke icon + return (user.deleted ? unbanIcon : [unbanIcon, nukeAccount]); + } } class adminTokeCommandList{ diff --git a/www/js/utils.js b/www/js/utils.js index b723215..e6b8019 100644 --- a/www/js/utils.js +++ b/www/js/utils.js @@ -57,6 +57,7 @@ class canopyUXUtils{ //add single items addContent(content); }else{ + console.log(content); //Crawl through content array content.forEach((item)=>{ //add each item