Full JSDoc for src/schemas complete.

This commit is contained in:
rainbow napkin 2025-09-02 06:48:32 -04:00
parent ec37c2f59d
commit 76e2f56eb4
4 changed files with 152 additions and 4 deletions

View file

@ -17,6 +17,9 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.*/
//NPM Imports //NPM Imports
const {mongoose} = require('mongoose'); const {mongoose} = require('mongoose');
/**
* DB Schema for Documents representing a user ban from a single channel
*/
const channelBanSchema = new mongoose.Schema({ const channelBanSchema = new mongoose.Schema({
user: { user: {
type: mongoose.SchemaTypes.ObjectID, type: mongoose.SchemaTypes.ObjectID,
@ -41,6 +44,10 @@ const channelBanSchema = new mongoose.Schema({
}); });
//methods //methods
/**
* Calculates days until ban expiration
* @returns {Number} Days until the given ban expires
*/
channelBanSchema.methods.getDaysUntilExpiration = function(){ channelBanSchema.methods.getDaysUntilExpiration = function(){
//Get ban date //Get ban date
const expirationDate = new Date(this.banDate); const expirationDate = new Date(this.banDate);

View file

@ -17,10 +17,17 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.*/
//NPM Imports //NPM Imports
const {mongoose} = require('mongoose'); const {mongoose} = require('mongoose');
//This originally belonged to the permissionSchema, but this avoids circular dependencies. /**
* Rank Enum, lists all known permission ranks from lowest to highest.
*
* This originally belonged to the permissionSchema, but this avoids circular dependencies.
*/
const rankEnum = ["anon", "user", "gold", "bot", "mod", "admin"]; const rankEnum = ["anon", "user", "gold", "bot", "mod", "admin"];
//Since this is intended to be used as a child schema for multiple parent schemas, we won't export it as a model //Since this is intended to be used as a child schema for multiple parent schemas, we won't export it as a model
/**
* DB Schema for Sub-Document representing permission structure for a single channel
*/
const channelPermissionSchema = new mongoose.Schema({ const channelPermissionSchema = new mongoose.Schema({
manageChannel: { manageChannel: {
type: mongoose.SchemaTypes.String, type: mongoose.SchemaTypes.String,

View file

@ -35,6 +35,9 @@ const chatSchema = require('./chatSchema');
//Utils //Utils
const { exceptionHandler, errorHandler } = require('../../utils/loggerUtils'); const { exceptionHandler, errorHandler } = require('../../utils/loggerUtils');
/**
* DB Schema for Documents containing de-hydrated representations of Canopy Stream/Chat Channels
*/
const channelSchema = new mongoose.Schema({ const channelSchema = new mongoose.Schema({
id: { id: {
type: mongoose.SchemaTypes.Number, type: mongoose.SchemaTypes.Number,
@ -124,6 +127,9 @@ const channelSchema = new mongoose.Schema({
}); });
/**
* Channel pre-save function. Ensures name requirements (for some reason, we should move that to the schema probably), kicks users after rank change, and handles housekeeping after adding tokes/emotes
*/
channelSchema.pre('save', async function (next){ channelSchema.pre('save', async function (next){
if(this.isModified("name")){ if(this.isModified("name")){
if(this.name.match(/^[a-z0-9_\-.]+$/i) == null){ if(this.name.match(/^[a-z0-9_\-.]+$/i) == null){
@ -131,6 +137,7 @@ channelSchema.pre('save', async function (next){
} }
} }
//This entire block is just about finding users after rank-change and making sure they get kicked
//Getting the affected user would be a million times easier elsewhere //Getting the affected user would be a million times easier elsewhere
//But this ensures it happens every time channel rank gets changed no matter what //But this ensures it happens every time channel rank gets changed no matter what
if(this.isModified('rankList') && this.rankList != null){ if(this.isModified('rankList') && this.rankList != null){
@ -227,6 +234,11 @@ channelSchema.pre('save', async function (next){
}); });
//statics //statics
/**
* Registers a new channel to the DB
* @param {Object} channelObj - Channel Object from Browser to register
* @param {Mongoose.Document} ownerObj - DB Docuement representing user
*/
channelSchema.statics.register = async function(channelObj, ownerObj){ channelSchema.statics.register = async function(channelObj, ownerObj){
const {name, description, thumbnail} = channelObj; const {name, description, thumbnail} = channelObj;
@ -258,6 +270,11 @@ channelSchema.statics.register = async function(channelObj, ownerObj){
} }
} }
/**
* Generates Network-Friendly Browser-Digestable list of channels
* @param {Boolean} includeHidden - Whether or not to include hidden channels within the list
* @returns {Array} List of Network-Friendly Browser-Digestable Objects representing channels on the server
*/
channelSchema.statics.getChannelList = async function(includeHidden = false){ channelSchema.statics.getChannelList = async function(includeHidden = false){
const chanDB = await this.find({}); const chanDB = await this.find({});
var chanGuide = []; var chanGuide = [];
@ -280,9 +297,16 @@ channelSchema.statics.getChannelList = async function(includeHidden = false){
} }
//Middleware for rank checks //Middleware for rank checks
//Man, it would be really nice if express middleware actually supported async functions, you know, as if it where't still 2015 >:( /**
//Also holy shit, sharing a function between two middleware functions is a nightmare * Configurable Express Middleware for Per-Channel Endpoint Authorization
//I'd rather just have this check chanField for '/c/' to handle channels in URL, fuck me this was obnoxious to write *
* Man, it would be really nice if express middleware actually supported async functions, you know, as if it where't still 2015 >:(
* Also holy shit, sharing a function between two middleware functions is a nightmare
* I'd rather just have this check chanField for '/c/' to handle channels in URL, fuck me this was obnoxious to write
* @param {String} - Permission to check against
* @param {String} - Name of channel to authorize against
* @returns {Function} Express middleware function with arguments injected into logic
*/
channelSchema.statics.reqPermCheck = function(perm, chanField = "chanName"){ channelSchema.statics.reqPermCheck = function(perm, chanField = "chanName"){
return (req, res, next)=>{ return (req, res, next)=>{
try{ try{
@ -327,6 +351,9 @@ channelSchema.statics.reqPermCheck = function(perm, chanField = "chanName"){
} }
} }
/**
* Schedulable Function for Processing and Deleting Expired Channel-level User Bans
*/
channelSchema.statics.processExpiredBans = async function(){ channelSchema.statics.processExpiredBans = async function(){
const chanDB = await this.find({}); const chanDB = await this.find({});
@ -350,6 +377,11 @@ channelSchema.statics.processExpiredBans = async function(){
} }
//methods //methods
/**
* Updates settings map for a given channel document
* @param {Map} settingsMap - Map of settings updates to apply against channel document
* @returns {Map} Map of all channel settings
*/
channelSchema.methods.updateSettings = async function(settingsMap){ channelSchema.methods.updateSettings = async function(settingsMap){
settingsMap.forEach((value, key) => { settingsMap.forEach((value, key) => {
if(this.settings[key] == null){ if(this.settings[key] == null){
@ -364,6 +396,11 @@ channelSchema.methods.updateSettings = async function(settingsMap){
return this.settings; return this.settings;
} }
/**
* Crawls through channel rank and runs a callback against the requested user's rank sub-doc
* @param {Mongoose.Document} userDB - User DB Document to run the callback against
* @param {Function} cb - Callback Function to call against the given users rank sub-doc
*/
channelSchema.methods.rankCrawl = async function(userDB,cb){ channelSchema.methods.rankCrawl = async function(userDB,cb){
//Crawl through channel rank list //Crawl through channel rank list
//TODO: replace this with rank check function shared with setRank //TODO: replace this with rank check function shared with setRank
@ -376,6 +413,12 @@ channelSchema.methods.rankCrawl = async function(userDB,cb){
}); });
} }
/**
* Sets users rank by User Doc
* @param {Mongoose.Document} userDB - DB Document of user's channel rank to change
* @param {String} rank - Channel rank to set user to
* @returns {Array} Channel Rank List
*/
channelSchema.methods.setRank = async function(userDB,rank){ channelSchema.methods.setRank = async function(userDB,rank){
//Create variable to store found ranks //Create variable to store found ranks
var foundRankIndex = null; var foundRankIndex = null;
@ -409,6 +452,10 @@ channelSchema.methods.setRank = async function(userDB,rank){
return this.rankList; return this.rankList;
} }
/**
* Generates Network-Friendly Browser-Digestable channel rank list
* @returns {Array} Network-Friendly Browser-Digestable channel rank list
*/
channelSchema.methods.getRankList = async function(){ channelSchema.methods.getRankList = async function(){
//Create an empty array to hold the user list //Create an empty array to hold the user list
const rankList = new Map() const rankList = new Map()
@ -457,6 +504,11 @@ channelSchema.methods.getRankList = async function(){
return rankList; return rankList;
} }
/**
* Gets channel rank by user document
* @param {Mongoose.Document} userDB - DB Document of User to pull Channel Rank of
* @returns {String} Channel rank of requested user
*/
channelSchema.methods.getChannelRankByUserDoc = async function(userDB = null){ channelSchema.methods.getChannelRankByUserDoc = async function(userDB = null){
var foundRank = null; var foundRank = null;
@ -483,11 +535,22 @@ channelSchema.methods.getChannelRankByUserDoc = async function(userDB = null){
} }
} }
/**
* Gets channel rank by username
* @param {String} user - Username of user to pull channel rank of
* @returns {String} Channel rank of requested user
*/
channelSchema.methods.getChannelRank = async function(user){ channelSchema.methods.getChannelRank = async function(user){
const userDB = await userModel.findOne({user: user.user}); const userDB = await userModel.findOne({user: user.user});
return await this.getChannelRankByUserDoc(userDB); return await this.getChannelRankByUserDoc(userDB);
} }
/**
* Calculates a permission check against a specific channel permission for a given user by username
* @param {String} user - Username of user to check against
* @param {String} perm - Name of channel Permission to check against
* @returns {Boolean} Whether or not the given user passes the given channel perm check
*/
channelSchema.methods.permCheck = async function (user, perm){ channelSchema.methods.permCheck = async function (user, perm){
//Set userDB to null if we wheren't passed a real user //Set userDB to null if we wheren't passed a real user
if(user != null){ if(user != null){
@ -499,6 +562,12 @@ channelSchema.methods.permCheck = async function (user, perm){
return await this.permCheckByUserDoc(userDB, perm) return await this.permCheckByUserDoc(userDB, perm)
} }
/**
* Calculates a permission check against a specific channel permission for a given user by DB Document
* @param {Mongoose.Document} userDB - DB Document of user to check against
* @param {String} perm - Name of channel Permission to check against
* @returns {Boolean} Whether or not the given user passes the given channel perm check
*/
channelSchema.methods.permCheckByUserDoc = async function(userDB, perm){ channelSchema.methods.permCheckByUserDoc = async function(userDB, perm){
//Get site-wide rank as number, default to anon for anonymous users //Get site-wide rank as number, default to anon for anonymous users
const rank = userDB ? permissionModel.rankToNum(userDB.rank) : permissionModel.rankToNum("anon"); const rank = userDB ? permissionModel.rankToNum(userDB.rank) : permissionModel.rankToNum("anon");
@ -516,6 +585,11 @@ channelSchema.methods.permCheckByUserDoc = async function(userDB, perm){
return (permCheck || overrideCheck); return (permCheck || overrideCheck);
} }
/**
* Generates channel-wide permission map for a given user by user doc
* @param {Mongoose.Document} userDB - DB Document representing a single user account
* @returns {Object} Object containing two maps, one for channel perms, another for site-wide perms
*/
channelSchema.methods.getPermMapByUserDoc = async function(userDB){ channelSchema.methods.getPermMapByUserDoc = async function(userDB){
//Grap site-wide permissions //Grap site-wide permissions
const sitePerms = await permissionModel.getPerms(); const sitePerms = await permissionModel.getPerms();
@ -537,6 +611,11 @@ channelSchema.methods.getPermMapByUserDoc = async function(userDB){
}; };
} }
/**
* Checks if a specific user has been issued a channel-specific ban by DB doc
* @param {Mongoose.Document} userDB - DB Document representing a single user account
* @returns {Object} Found ban, if one exists
*/
channelSchema.methods.checkBanByUserDoc = async function(userDB){ channelSchema.methods.checkBanByUserDoc = async function(userDB){
var foundBan = null; var foundBan = null;
@ -565,6 +644,10 @@ channelSchema.methods.checkBanByUserDoc = async function(userDB){
return foundBan; return foundBan;
} }
/**
* Generates Network-Friendly Browser-Digestable list of channel emotes
* @returns {Array} Network-Friendly Browser-Digestable list of channel emotes
*/
channelSchema.methods.getEmotes = function(){ channelSchema.methods.getEmotes = function(){
//Create an empty array to hold our emote list //Create an empty array to hold our emote list
const emoteList = []; const emoteList = [];
@ -583,6 +666,10 @@ channelSchema.methods.getEmotes = function(){
return emoteList; return emoteList;
} }
/**
* Generates Network-Friendly Browser-Digestable list of channel playlists
* @returns {Array} Network-Friendly Browser-Digestable list of channel playlists
*/
channelSchema.methods.getPlaylists = function(){ channelSchema.methods.getPlaylists = function(){
//Create an empty array to hold our emote list //Create an empty array to hold our emote list
const playlists = []; const playlists = [];
@ -597,6 +684,10 @@ channelSchema.methods.getPlaylists = function(){
return playlists; return playlists;
} }
/**
* Crawls through channel playlists, running a given callback function against each one
* @param {Function} cb - Callback function to run against channel playlists
*/
channelSchema.methods.playlistCrawl = function(cb){ channelSchema.methods.playlistCrawl = function(cb){
for(let listIndex in this.media.playlists){ for(let listIndex in this.media.playlists){
//Grab the associated playlist //Grab the associated playlist
@ -607,6 +698,11 @@ channelSchema.methods.playlistCrawl = function(cb){
} }
} }
/**
* Finds channel playlist by playlist name
* @param {String} name - name of given playlist to find
* @returns {Mongoose.Document} - Sub-Document representing a single playlist
*/
channelSchema.methods.getPlaylistByName = function(name){ channelSchema.methods.getPlaylistByName = function(name){
//Create null value to hold our found playlist //Create null value to hold our found playlist
let foundPlaylist = null; let foundPlaylist = null;
@ -626,6 +722,10 @@ channelSchema.methods.getPlaylistByName = function(name){
return foundPlaylist; return foundPlaylist;
} }
/**
* Deletes channel playlist by playlist name
* @param {String} name - name of given playlist to Delete
*/
channelSchema.methods.deletePlaylistByName = async function(name){ channelSchema.methods.deletePlaylistByName = async function(name){
//Find the playlist //Find the playlist
let playlist = this.getPlaylistByName(name); let playlist = this.getPlaylistByName(name);
@ -637,6 +737,10 @@ channelSchema.methods.deletePlaylistByName = async function(name){
await this.save(); await this.save();
} }
/**
* Generates Network-Friendly Browser-Digestable list of Channel-Wide user bans
* @returns {Array} Network-Friendly Browser-Digestable list of Channel-Wide user bans
*/
channelSchema.methods.getChanBans = async function(){ channelSchema.methods.getChanBans = async function(){
//Create an empty list to hold our found bans //Create an empty list to hold our found bans
var banList = []; var banList = [];
@ -677,6 +781,12 @@ channelSchema.methods.getChanBans = async function(){
return banList; return banList;
} }
/**
* Issues channel-wide ban to user based on user DB document
* @param {Mongoose.Document} userDB - DB Document representing a single user account to ban
* @param {Number} expirationDays - Days until ban expiration
* @param {Boolean} banAlts - Whether or not to ban alts
*/
channelSchema.methods.banByUserDoc = async function(userDB, expirationDays, banAlts){ channelSchema.methods.banByUserDoc = async function(userDB, expirationDays, banAlts){
//Throw a shitfit if the user doesn't exist //Throw a shitfit if the user doesn't exist
if(userDB == null){ if(userDB == null){
@ -714,11 +824,23 @@ channelSchema.methods.banByUserDoc = async function(userDB, expirationDays, banA
await this.save(); await this.save();
} }
/**
* Syntatic sugar for banning users by username
* @param {String} user - Username of user to ban
* @param {Number} expirationDays - Days until ban expiration
* @param {Boolean} banAlts - Whether or not to ban alts
* @returns {Promise} promise from this.banByUserDoc
*/
channelSchema.methods.ban = async function(user, expirationDays, banAlts){ channelSchema.methods.ban = async function(user, expirationDays, banAlts){
const userDB = await userModel.find({user}); const userDB = await userModel.find({user});
return await this.banByUserDoc(userDB, expirationDays, banAlts); return await this.banByUserDoc(userDB, expirationDays, banAlts);
} }
/**
* Un-Bans user by DB Document
* @param {Mongoose.Document} userDB - DB Document representing a single user account to un-ban
* @returns {Mongoose.Document} Saved channel document
*/
channelSchema.methods.unbanByUserDoc = async function(userDB){ channelSchema.methods.unbanByUserDoc = async function(userDB){
//Throw a shitfit if the user doesn't exist //Throw a shitfit if the user doesn't exist
if(userDB == null){ if(userDB == null){
@ -739,11 +861,20 @@ channelSchema.methods.unbanByUserDoc = async function(userDB){
return await this.save(); return await this.save();
} }
/**
* Syntatic sugar for un-banning by username
* @param {String} user - Username of user to un-ban
* @returns {Mongoose.Document} Saved channel document
*/
channelSchema.methods.unban = async function(user){ channelSchema.methods.unban = async function(user){
const userDB = await userModel.find({user}); const userDB = await userModel.find({user});
return await this.unbanByUserDoc(userDB); return await this.unbanByUserDoc(userDB);
} }
/**
* Nukes channel upon channel-admin request
* @param {String} confirm - Channel name to confirm deletion of channel
*/
channelSchema.methods.nuke = async function(confirm){ channelSchema.methods.nuke = async function(confirm){
if(confirm == "" || confirm == null){ if(confirm == "" || confirm == null){
throw loggerUtils.exceptionSmith("Empty Confirmation String!", "validation"); throw loggerUtils.exceptionSmith("Empty Confirmation String!", "validation");

View file

@ -22,6 +22,9 @@ const linkSchema = new mongoose.Schema({
type: mongoose.SchemaTypes.String type: mongoose.SchemaTypes.String
}); });
/**
* DB Schema for documents representing a single chat message
*/
const chatSchema = new mongoose.Schema({ const chatSchema = new mongoose.Schema({
user: { user: {
type: mongoose.SchemaTypes.String, type: mongoose.SchemaTypes.String,