Updated ban UI to handle IP-Bans.

This commit is contained in:
rainbow napkin 2025-01-01 23:00:03 -05:00
parent 977e8e1e2e
commit 7624e1928a
8 changed files with 165 additions and 71 deletions

View file

@ -306,24 +306,31 @@ userBanSchema.statics.unban = async function(user){
} }
userBanSchema.statics.getBans = async function(){ 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 = []; var bans = [];
banDB.forEach((ban) => { banDB.forEach((ban) => {
//Calcualte expiration date //Create array to hold alts
var alts = [];
//Calculate expiration date
var expirationDate = new Date(ban.banDate); var expirationDate = new Date(ban.banDate);
expirationDate.setDate(expirationDate.getDate() + ban.expirationDays); expirationDate.setDate(expirationDate.getDate() + ban.expirationDays);
//Make sure we're not about to read the properties of a null object //Make sure we're not about to read the properties of a null object
if(ban.user != null){ if(ban.user != null){
var userObj = { var userObj = ban.user.getProfile();
id: ban.user.id,
user: ban.user.user,
img: ban.user.img,
date: ban.user.date
}
} }
//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 = { const banObj = {
banDate: ban.banDate, banDate: ban.banDate,
expirationDays: ban.expirationDays, expirationDays: ban.expirationDays,
@ -331,14 +338,16 @@ userBanSchema.statics.getBans = async function(){
daysUntilExpiration: ban.getDaysUntilExpiration(), daysUntilExpiration: ban.getDaysUntilExpiration(),
user: userObj, user: userObj,
ips: ban.ips, ips: ban.ips,
alts: ban.alts, alts,
deletedNames: ban.deletedNames, deletedNames: ban.deletedNames,
permanent: ban.permanent permanent: ban.permanent
} }
//Add it to the array
bans.push(banObj); bans.push(banObj);
}); });
//Return the array
return bans; return bans;
} }

View file

@ -74,22 +74,23 @@ const userSchema = new mongoose.Schema({
required: true, required: true,
default: "/img/johnny.png" default: "/img/johnny.png"
}, },
//These should be larger than validator values to make room for escaped characters
bio: { bio: {
type: mongoose.SchemaTypes.String, type: mongoose.SchemaTypes.String,
required: true, required: true,
maxLength: 1000, maxLength: 2000,
default: "Bio not set!" default: "Bio not set!"
}, },
pronouns:{ pronouns:{
type: mongoose.SchemaTypes.String, type: mongoose.SchemaTypes.String,
optional: true, optional: true,
maxLength: 20, maxLength: 50,
default: "" default: ""
}, },
signature: { signature: {
type: mongoose.SchemaTypes.String, type: mongoose.SchemaTypes.String,
required: true, required: true,
maxLength: 150, maxLength: 300,
default: "Signature not set!" default: "Signature not set!"
}, },
highLevel: { highLevel: {

View file

@ -32,11 +32,12 @@ module.exports = {
img: (field = 'img') => body(field).optional().isURL({require_tld: false, require_host: false}).trim(), 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), rank: (field = 'rank') => body(field).escape().trim().custom(isRank),

View file

@ -18,17 +18,17 @@ along with this program. If not, see <https://www.gnu.org/licenses/>. %>
<div class="dynamic-container"> <div class="dynamic-container">
<table id="admin-ban-list-table" class="admin-list-table"> <table id="admin-ban-list-table" class="admin-list-table">
<tr id="admin-ban-list-entry-title" class="admin-list-entry-title-row"> <tr id="admin-ban-list-entry-title" class="admin-list-entry-title-row">
<td id="admin-ban-list-entry-img-title" class="admin-list-entry-title admin-list-entry-item admin-list-entry-img-title"> <td id="admin-ban-list-entry-user-title" class="admin-list-entry-title admin-list-entry-item">
<h3>Img</h3> <h3>Main<br>User</h3>
</td> </td>
<td id="admin-ban-list-entry-id-title" class="admin-list-entry-title admin-list-entry-item not-first-col"> <td id="admin-ban-list-entry-alts-title" class="admin-list-entry-title admin-list-entry-item not-first-col">
<h3>ID</h3> <h3>Alt<br>Users</h3>
</td> </td>
<td id="admin-ban-list-entry-name-title" class="admin-list-entry-title admin-list-entry-item not-first-col"> <td id="admin-ban-list-entry-ban-nuked-users-title" class="admin-list-entry-title admin-list-entry-item not-first-col">
<h3>Name</h3> <h3>Nuked<br>Users</h3>
</td> </td>
<td id="admin-ban-list-entry-date-title" class="admin-list-entry-title admin-list-entry-item not-first-col"> <td id="admin-ban-list-entry-ban-ips-title" class="admin-list-entry-title admin-list-entry-item not-first-col">
<h3>Sign-Up<br>Date</h3> <h3>Banned<br>IPs</h3>
</td> </td>
<td id="admin-ban-list-entry-ban-date-title" class="admin-list-entry-title admin-list-entry-item not-first-col"> <td id="admin-ban-list-entry-ban-date-title" class="admin-list-entry-title admin-list-entry-item not-first-col">
<h3>Ban<br>Date</h3> <h3>Ban<br>Date</h3>

View file

@ -16,6 +16,10 @@ along with this program. If not, see <https://www.gnu.org/licenses/>. %>
<link rel="stylesheet" type="text/css" href="/css/popup/ban.css"> <link rel="stylesheet" type="text/css" href="/css/popup/ban.css">
<h3 class="popup-title">Ban NULL</h3> <h3 class="popup-title">Ban NULL</h3>
<div class="ban-popup-content"> <div class="ban-popup-content">
<span>
<label for="ban-popup-perm">IP-Ban:</label>
<input type="checkbox" name="ban-popup-ip" id="ban-popup-ip">
</span>
<span> <span>
<label for="ban-popup-perm">Perma-Ban:</label> <label for="ban-popup-perm">Perma-Ban:</label>
<input type="checkbox" name="ban-popup-perm" id="ban-popup-perm"> <input type="checkbox" name="ban-popup-perm" id="ban-popup-perm">

View file

@ -98,10 +98,6 @@ img.admin-list-entry-item{
width: 8em; width: 8em;
} }
#admin-ban-list-entry-expiration-date-title{
width: 11em;
}
div.toke-command-list{ div.toke-command-list{
display: grid; display: grid;
grid-template-columns: repeat(auto-fit, minmax(15em, 1fr)); grid-template-columns: repeat(auto-fit, minmax(15em, 1fr));

View file

@ -32,6 +32,7 @@ class canopyAdminUtils{
//Setup title text real quick-like :P //Setup title text real quick-like :P
this.title.innerHTML = `Ban ${this.target}`; this.title.innerHTML = `Ban ${this.target}`;
this.ipBan = document.querySelector("#ban-popup-ip");
this.permBan = document.querySelector("#ban-popup-perm"); this.permBan = document.querySelector("#ban-popup-perm");
this.expiration = document.querySelector("#ban-popup-expiration"); this.expiration = document.querySelector("#ban-popup-expiration");
this.expirationPrefix = document.querySelector("#ban-popup-expiration-prefix"); this.expirationPrefix = document.querySelector("#ban-popup-expiration-prefix");
@ -62,7 +63,8 @@ class canopyAdminUtils{
this.popup.closePopup(); this.popup.closePopup();
//Submit the user ban based on input //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... //For some reason comparing this against undefined or null wasnt working in and of itself...
if(data != null){ if(data != null){
@ -457,65 +459,145 @@ class adminUserBanList{
} }
renderBanList(banList){ renderBanList(banList){
//Clear out the ban list
this.clearBanList(); this.clearBanList();
//For each ban received
banList.forEach((ban) => { banList.forEach((ban) => {
//Calculate expiration date and expiration days //Calculate expiration date and expiration days
var expirationDateString = `${new Date(ban.expirationDate).toDateString()}<br>(${ban.daysUntilExpiration} day(s) left)`; let expirationDateString = `${new Date(ban.expirationDate).toLocaleDateString()}<br>(${ban.daysUntilExpiration} day(s) left)`;
var banActionString = ban.permanent ? "Nuke<br>Accounts" : "Un-Ban"; let banActionString = ban.permanent ? "Nuke<br>Accounts" : "Un-Ban";
if(ban.user == null){ 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 //Fudge the user object if it's already been deleted
ban.user = { ban.user = {
img: "/img/nuked.png", //Use dead name if we got one
id: "-", user: ban.deletedNames[0] != null ? ban.deletedNames[0] : "Nuked"
user: ban.deletedNames[0] ? ban.deletedNames[0] : "UNKNOWN",
deleted: true
}; };
ban.alts[0] = {
user: "Nuked"
}
//Fake the display string //Fake the display string
var signUpDateString = "-"
expirationDateString = "Accounts<br>Nuked" expirationDateString = "Accounts<br>Nuked"
banActionString = "Accounts<br>Nuked" banActionString = "Accounts<br>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 //Generate and append row to table
const imgNode = document.createElement('img'); this.table.appendChild(utils.ux.newTableRow([
imgNode.classList.add("admin-list-entry","admin-list-entry-item"); this.renderUser(ban.user, nuked),
imgNode.src = ban.user.img; this.renderUsers(ban.alts, nuked),
this.renderDeadUsers(ban.deletedNames),
this.renderIPs(ban.ips),
new Date(ban.banDate).toLocaleDateString(),
expirationDateString,
banActionString,
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 //Create unban icon
const unbanIcon = document.createElement('i'); const unbanIcon = document.createElement('i');
unbanIcon.classList.add("bi-emoji-smile-fill","admin-user-list-icon","admin-user-list-unban-icon"); 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.id = `admin-user-list-unban-icon-${user.user}`;
unbanIcon.title = `Unban ${ban.user.user}`; unbanIcon.title = `Unban ${user.user}`;
unbanIcon.addEventListener("click", this.unban.bind(this)); unbanIcon.addEventListener("click", this.unban.bind(this));
//Create nuke account icon //Create nuke account icon
const nukeAccount = document.createElement('i'); const nukeAccount = document.createElement('i');
nukeAccount.classList.add("bi-radioactive","admin-user-list-icon","admin-user-list-unban-icon"); 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.id = `admin-user-list-unban-icon-${user.user}`;
nukeAccount.title = `Nuke accounts`; nukeAccount.title = `Nuke accounts`;
nukeAccount.addEventListener("click",console.log); nukeAccount.addEventListener("click",console.log);
//append img cell to row //If our user has been deleted don't return the nuke icon
entryRow.appendChild(utils.ux.newTableCell(imgNode, true)); return (user.deleted ? unbanIcon : [unbanIcon, nukeAccount]);
//Generate and append row to table
this.table.appendChild(utils.ux.newTableRow([
ban.user.id,
ban.user.user,
signUpDateString,
new Date(ban.banDate).toDateString(),
expirationDateString,
banActionString,
(ban.user.deleted ? unbanIcon : [unbanIcon, nukeAccount])
]));
});
} }
} }

View file

@ -57,6 +57,7 @@ class canopyUXUtils{
//add single items //add single items
addContent(content); addContent(content);
}else{ }else{
console.log(content);
//Crawl through content array //Crawl through content array
content.forEach((item)=>{ content.forEach((item)=>{
//add each item //add each item