From affdd9857279277e441c79fbb99504ee50b54947 Mon Sep 17 00:00:00 2001
From: rainbow napkin
Date: Wed, 5 Nov 2025 18:35:48 -0500
Subject: [PATCH 01/23] Fixed errors loading default items
---
README.md | 2 +-
src/schemas/emoteSchema.js | 7 +++++--
src/schemas/tokebot/tokeCommandSchema.js | 19 ++++++++++++-------
3 files changed, 18 insertions(+), 10 deletions(-)
diff --git a/README.md b/README.md
index 3be348e..d04e2ca 100644
--- a/README.md
+++ b/README.md
@@ -9,7 +9,7 @@ Canopy
-0.4-INDEV
+0.4-INDEV Hotfix 1
=========
Canopy - /ˈkæ.nə.pi/:
diff --git a/src/schemas/emoteSchema.js b/src/schemas/emoteSchema.js
index c059a0c..8a35dc5 100644
--- a/src/schemas/emoteSchema.js
+++ b/src/schemas/emoteSchema.js
@@ -51,8 +51,11 @@ const emoteSchema = new mongoose.Schema({
* Post-Save function, ensures all new emotes are broadcastes to actively connected clients
*/
emoteSchema.post('save', async function (next){
- //broadcast updated emotes
- server.channelManager.broadcastSiteEmotes();
+ //Ensure the channel manager is actually up
+ if(server.channelManager != null){
+ //broadcast updated emotes
+ server.channelManager.broadcastSiteEmotes();
+ }
});
/**
diff --git a/src/schemas/tokebot/tokeCommandSchema.js b/src/schemas/tokebot/tokeCommandSchema.js
index 892b7d0..19283f8 100644
--- a/src/schemas/tokebot/tokeCommandSchema.js
+++ b/src/schemas/tokebot/tokeCommandSchema.js
@@ -36,15 +36,20 @@ const tokeCommandSchema = new mongoose.Schema({
* Pre-Save middleware, ensures tokebot receives all new toke commands
*/
tokeCommandSchema.pre('save', async function (next){
- //if the command was changed
+ //if the channel manager, chat handler, and chat post-processor are all loaded up...
if(this.isModified("command")){
- //Get server tokebot object
- const tokebot = server.channelManager.chatHandler.chatPreprocessor.tokebot;
+ if(server.channelManager != null &&
+ server.channelManager.chatHandler != null &&
+ server.channelManager.chatHandler.chatPreprocessor == null){
- //If tokebot is up and running
- if(tokebot != null && tokebot.tokeCommands != null){
- //Pop the command on to the end
- tokebot.tokeCommands.push(this.command);
+ //Get server tokebot object
+ const tokebot = server.channelManager.chatHandler.chatPreprocessor.tokebot;
+
+ //If tokebot is up and running
+ if(tokebot != null && tokebot.tokeCommands != null){
+ //Pop the command on to the end
+ tokebot.tokeCommands.push(this.command);
+ }
}
}
From 001945a284920680f4740ad0b3f295c8adf62317 Mon Sep 17 00:00:00 2001
From: rainbow napkin
Date: Wed, 5 Nov 2025 18:46:51 -0500
Subject: [PATCH 02/23] Whoops...
---
src/schemas/tokebot/tokeCommandSchema.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/schemas/tokebot/tokeCommandSchema.js b/src/schemas/tokebot/tokeCommandSchema.js
index 19283f8..635f487 100644
--- a/src/schemas/tokebot/tokeCommandSchema.js
+++ b/src/schemas/tokebot/tokeCommandSchema.js
@@ -40,7 +40,7 @@ tokeCommandSchema.pre('save', async function (next){
if(this.isModified("command")){
if(server.channelManager != null &&
server.channelManager.chatHandler != null &&
- server.channelManager.chatHandler.chatPreprocessor == null){
+ server.channelManager.chatHandler.chatPreprocessor != null){
//Get server tokebot object
const tokebot = server.channelManager.chatHandler.chatPreprocessor.tokebot;
From d7c55fe3da6ef0698a157160010d69bca45e8cf5 Mon Sep 17 00:00:00 2001
From: rainbow napkin
Date: Thu, 6 Nov 2025 19:00:30 -0500
Subject: [PATCH 03/23] Whoops...
---
src/schemas/tokebot/tokeCommandSchema.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/schemas/tokebot/tokeCommandSchema.js b/src/schemas/tokebot/tokeCommandSchema.js
index 19283f8..635f487 100644
--- a/src/schemas/tokebot/tokeCommandSchema.js
+++ b/src/schemas/tokebot/tokeCommandSchema.js
@@ -40,7 +40,7 @@ tokeCommandSchema.pre('save', async function (next){
if(this.isModified("command")){
if(server.channelManager != null &&
server.channelManager.chatHandler != null &&
- server.channelManager.chatHandler.chatPreprocessor == null){
+ server.channelManager.chatHandler.chatPreprocessor != null){
//Get server tokebot object
const tokebot = server.channelManager.chatHandler.chatPreprocessor.tokebot;
From be01417cdfe69eae93c671a7b1df12471cd2a67c Mon Sep 17 00:00:00 2001
From: rainbow napkin
Date: Thu, 6 Nov 2025 19:01:22 -0500
Subject: [PATCH 04/23] Updated vague DB query which could lead to pwned
accounts
---
README.md | 2 +-
src/controllers/api/account/deleteController.js | 2 +-
src/controllers/api/account/updateController.js | 2 +-
3 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/README.md b/README.md
index d04e2ca..9902977 100644
--- a/README.md
+++ b/README.md
@@ -9,7 +9,7 @@ Canopy
-0.4-INDEV Hotfix 1
+0.4-INDEV Hotfix 2
=========
Canopy - /ˈkæ.nə.pi/:
diff --git a/src/controllers/api/account/deleteController.js b/src/controllers/api/account/deleteController.js
index 94b9cdf..af70cbd 100644
--- a/src/controllers/api/account/deleteController.js
+++ b/src/controllers/api/account/deleteController.js
@@ -38,7 +38,7 @@ module.exports.post = async function(req, res){
return res.send('Invalid Session! Cannot delete account while logged out!');
}
- const userDB = await userModel.findOne(user);
+ const userDB = await userModel.findOne({user: user.user});
if(!userDB){
diff --git a/src/controllers/api/account/updateController.js b/src/controllers/api/account/updateController.js
index befca96..3a04355 100644
--- a/src/controllers/api/account/updateController.js
+++ b/src/controllers/api/account/updateController.js
@@ -46,7 +46,7 @@ module.exports.post = async function(req, res){
const {field, change} = data;
const {user} = req.session;
- const userDB = await userModel.findOne(user);
+ const userDB = await userModel.findOne({user: user.user});
const update = {};
From e94914a1525c499d3c71054765f67c13bdaf32bf Mon Sep 17 00:00:00 2001
From: rainbow napkin
Date: Thu, 6 Nov 2025 19:36:52 -0500
Subject: [PATCH 05/23] Pretty things up a bit, after fixing that nasty profile
bug.
---
src/controllers/api/account/deleteController.js | 9 +++------
src/controllers/api/account/updateController.js | 9 +++++++--
2 files changed, 10 insertions(+), 8 deletions(-)
diff --git a/src/controllers/api/account/deleteController.js b/src/controllers/api/account/deleteController.js
index af70cbd..0552da7 100644
--- a/src/controllers/api/account/deleteController.js
+++ b/src/controllers/api/account/deleteController.js
@@ -33,17 +33,14 @@ module.exports.post = async function(req, res){
const data = matchedData(req);
//make sure we're not bullshitting ourselves here.
- if(user == null){
- res.status(400);
- return res.send('Invalid Session! Cannot delete account while logged out!');
+ if(user == null || user.user == null){
+ return errorHandler(res, 'You must be logged in to delete your account!', 'unauthorized');
}
const userDB = await userModel.findOne({user: user.user});
-
if(!userDB){
- res.status(400);
- return res.send('Invalid User! Account must exist in order to delete!');
+ return errorHandler(res, 'User not found!', 'unauthorized');
}
await userDB.nuke(data.pass);
diff --git a/src/controllers/api/account/updateController.js b/src/controllers/api/account/updateController.js
index 3a04355..bb3fd3f 100644
--- a/src/controllers/api/account/updateController.js
+++ b/src/controllers/api/account/updateController.js
@@ -46,6 +46,12 @@ module.exports.post = async function(req, res){
const {field, change} = data;
const {user} = req.session;
+ //If the user is null
+ if(user == null || user.user == null){
+ //BEFORE YOU BREAK MY HEART!!!
+ return errorHandler(res, 'You must be logged in to preform this action!', 'unauthorized');
+ }
+
const userDB = await userModel.findOne({user: user.user});
const update = {};
@@ -86,8 +92,7 @@ module.exports.post = async function(req, res){
res.status(200);
return res.send(update);
}else{
- res.status(400);
- return res.send({errors: [{msg:"User not found!"}]});
+ return errorHandler(res, 'User not found!', 'unauthorized');
}
}else{
res.status(400);
From e7558d7d9d288fd5c43aad9c9647313bf48bff90 Mon Sep 17 00:00:00 2001
From: rainbow napkin
Date: Mon, 10 Nov 2025 19:52:59 -0500
Subject: [PATCH 06/23] Update package.json
---
package.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/package.json b/package.json
index 0076162..b758038 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "canopy-of-indev",
- "version": "0.4",
+ "version": "0.4.2",
"license": "AGPL-3.0-only",
"dependencies": {
"@braintree/sanitize-url": "^7.1.1",
From eaca9db987b333d677c7ae2e5bb18ed38e568622 Mon Sep 17 00:00:00 2001
From: rainbow napkin
Date: Mon, 10 Nov 2025 20:11:34 -0500
Subject: [PATCH 07/23] Mods from old codebase now properly migrated. Version
number upgraded.
---
README.md | 2 +-
package.json | 2 +-
src/schemas/user/migrationSchema.js | 11 ++++++++++-
3 files changed, 12 insertions(+), 3 deletions(-)
diff --git a/README.md b/README.md
index 9902977..c6f2762 100644
--- a/README.md
+++ b/README.md
@@ -9,7 +9,7 @@ Canopy
-0.4-INDEV Hotfix 2
+0.4-INDEV Hotfix 3
=========
Canopy - /ˈkæ.nə.pi/:
diff --git a/package.json b/package.json
index b758038..d01d5aa 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "canopy-of-indev",
- "version": "0.4.2",
+ "version": "0.4.3",
"license": "AGPL-3.0-only",
"dependencies": {
"@braintree/sanitize-url": "^7.1.1",
diff --git a/src/schemas/user/migrationSchema.js b/src/schemas/user/migrationSchema.js
index c17ed51..9aff595 100644
--- a/src/schemas/user/migrationSchema.js
+++ b/src/schemas/user/migrationSchema.js
@@ -217,13 +217,22 @@ migrationSchema.statics.ingestLegacyUser = async function(rawProfile){
return;
}
+ //Pull rank, dropping over-ranked users down to current enum length
+ let rank = Math.min(Math.max(0, profileArray[3]), permissionModel.rankEnum.length - 1);
+
+ //If this user was a mod on the old site
+ if(rank == 2){
+ //Set them up as a mod here
+ rank = permissionModel.rankEnum.length - 2;
+ }
+
//Create migration profile object from scraped info
const migrationProfile = new this({
user: profileArray[1],
pass: profileArray[2],
//Clamp rank to 0 and the max setting allowed by the rank enum
- rank: Math.min(Math.max(0, profileArray[3]), permissionModel.rankEnum.length - 1),
+ rank,
email: validator.normalizeEmail(profileArray[4]),
date: profileArray[7],
})
From ecebcf0d32d8ca69bfd9f893f59bb5d1651dbe3d Mon Sep 17 00:00:00 2001
From: rainbow napkin
Date: Mon, 10 Nov 2025 20:31:07 -0500
Subject: [PATCH 08/23] Killed harmless bug related to official YT Api that
cluttered the console.
---
www/js/channel/channel.js | 15 +++++++++------
1 file changed, 9 insertions(+), 6 deletions(-)
diff --git a/www/js/channel/channel.js b/www/js/channel/channel.js
index 5d50499..6da26c3 100644
--- a/www/js/channel/channel.js
+++ b/www/js/channel/channel.js
@@ -337,13 +337,16 @@ function onYouTubeIframeAPIReady(){
//Set embed api to true
client.ytEmbedAPILoaded = true;
- //Get currently playing item
- const nowPlaying = client.player.mediaHandler.nowPlaying;
+ //If the player is ready and has a mediaHandler loaded
+ if(client.player != null && client.player.mediaHandler != null){
+ //Get currently playing item
+ const nowPlaying = client.player.mediaHandler.nowPlaying;
- //If we're playing a youtube video and the official embeds are enabled
- if(nowPlaying.type == 'yt' && localStorage.getItem('ytPlayerType') == "embed"){
- //Restart the video now that the embed api has loaded
- client.player.start({media: nowPlaying});
+ //If we're playing a youtube video and the official embeds are enabled
+ if(nowPlaying.type == 'yt' && localStorage.getItem('ytPlayerType') == "embed"){
+ //Restart the video now that the embed api has loaded
+ client.player.start({media: nowPlaying});
+ }
}
}
From dd4d789d9fe24e75339caf137d5895ac2aadcbc3 Mon Sep 17 00:00:00 2001
From: rainbow napkin
Date: Mon, 10 Nov 2025 20:46:07 -0500
Subject: [PATCH 09/23] Added 'PM' option to userlist context menu.
---
www/js/channel/panels/pmPanel.js | 19 +++++++++++++++++--
www/js/channel/userlist.js | 3 ++-
2 files changed, 19 insertions(+), 3 deletions(-)
diff --git a/www/js/channel/panels/pmPanel.js b/www/js/channel/panels/pmPanel.js
index aaab30b..bcebc97 100644
--- a/www/js/channel/panels/pmPanel.js
+++ b/www/js/channel/panels/pmPanel.js
@@ -24,7 +24,7 @@ class pmPanel extends panelObj{
* @param {channel} client - Parent client Management Object
* @param {Document} panelDocument - Panel Document
*/
- constructor(client, panelDocument){
+ constructor(client, panelDocument, startSesh){
super(client, "Private Messaging", "/panel/pm", panelDocument);
/**
@@ -71,7 +71,17 @@ class pmPanel extends panelObj{
//Tell PMHandler to start tracking this panel
this.client.pmHandler.panelList.set(this.uuid, null);
+ //Define network related listeners
this.defineListeners();
+
+ //If a start sesh was provided
+ if(startSesh != null && startSesh != ""){
+ //Send message out to server
+ this.client.pmSocket.emit("pm", {
+ recipients: startSesh.split(" "),
+ msg: ""
+ });
+ }
}
closer(){
@@ -126,7 +136,6 @@ class pmPanel extends panelObj{
this.seshSendButton.addEventListener("click", this.send.bind(this));
this.seshBuffer.addEventListener('scroll', this.scrollHandler.bind(this));
this.ownerDoc.defaultView.addEventListener('resize', this.handleAutoScroll.bind(this));
-
}
startSesh(event){
@@ -180,6 +189,12 @@ class pmPanel extends panelObj{
* Render out current sesh array to sesh list UI
*/
renderSeshList(){
+ //If we don't have a sesh list
+ if(this.seshList == null){
+ //Fuck off, you're not even done building the object yet.
+ return;
+ }
+
//Clear out the sesh list
this.seshList.innerHTML = "";
diff --git a/www/js/channel/userlist.js b/www/js/channel/userlist.js
index 7db5a5e..5a4f24c 100644
--- a/www/js/channel/userlist.js
+++ b/www/js/channel/userlist.js
@@ -173,7 +173,8 @@ class userList{
function renderContextMenu(event){
//Setup menu map
let menuMap = new Map([
- ["Profile", ()=>{this.client.cPanel.setActivePanel(new panelObj(this.client, `${user.user}`, `/panel/profile?user=${user.user}`))}],
+ ["Profile", ()=>{this.client.cPanel.setActivePanel(new panelObj(this.client, user.user, `/panel/profile?user=${user.user}`))}],
+ ["PM", ()=>{this.client.cPanel.setActivePanel(new pmPanel(client, undefined, user.user))}],
["Mention", ()=>{this.client.chatBox.catChat(`${user.user} `)}],
["Toke With", ()=>{this.client.chatBox.tokeWith(user.user)}],
]);
From 37e9658d5133ab3881784ee2610738b93b09bf22 Mon Sep 17 00:00:00 2001
From: rainbow napkin
Date: Mon, 10 Nov 2025 21:38:36 -0500
Subject: [PATCH 10/23] Livestream media handler now dynamically resizes UX to
aspect ratio on media change.
---
www/js/channel/mediaHandler.js | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/www/js/channel/mediaHandler.js b/www/js/channel/mediaHandler.js
index 3dd11ae..158e5cb 100644
--- a/www/js/channel/mediaHandler.js
+++ b/www/js/channel/mediaHandler.js
@@ -907,6 +907,10 @@ class hlsLiveStreamHandler extends hlsBase{
return;
}
+ //Resize chat box to video aspect, since this is the only event thats reliably called on ratio change
+ //Re-enforcing UX rules a little more often shouldnt cause too many issues anywho.
+ this.client.chatBox.resizeAspect();
+
//Calculate distance to end of stream
const difference = this.video.duration - this.video.currentTime;
From ed656cb53032fd79cf091dd8e09ad0715df44d9b Mon Sep 17 00:00:00 2001
From: rainbow napkin
Date: Mon, 10 Nov 2025 22:07:23 -0500
Subject: [PATCH 11/23] Added canopy version string to about page.
---
package.json | 1 +
src/controllers/aboutController.js | 3 ++-
src/server.js | 3 ++-
src/views/about.ejs | 2 ++
4 files changed, 7 insertions(+), 2 deletions(-)
diff --git a/package.json b/package.json
index d01d5aa..e0c23a8 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,7 @@
{
"name": "canopy-of-indev",
"version": "0.4.3",
+ "canopyDisplayVersion": "0.4-Indev Hotfix 3",
"license": "AGPL-3.0-only",
"dependencies": {
"@braintree/sanitize-url": "^7.1.1",
diff --git a/src/controllers/aboutController.js b/src/controllers/aboutController.js
index 2b32b84..187157b 100644
--- a/src/controllers/aboutController.js
+++ b/src/controllers/aboutController.js
@@ -16,6 +16,7 @@ along with this program. If not, see .*/
//Config
const config = require('../../config.json');
+const package = require('../../package.json');
//Local Imports
const csrfUtils = require('../utils/csrfUtils');
@@ -23,5 +24,5 @@ const csrfUtils = require('../utils/csrfUtils');
//register page functions
module.exports.get = async function(req, res){
//Render page
- return res.render('about', {aboutText: config.aboutText, instance: config.instanceName, user: req.session.user, csrfToken: csrfUtils.generateToken(req)});
+ return res.render('about', {aboutText: config.aboutText, instance: config.instanceName, user: req.session.user, version: package.canopyDisplayVersion, csrfToken: csrfUtils.generateToken(req)});
}
\ No newline at end of file
diff --git a/src/server.js b/src/server.js
index 50dcda0..f783a6d 100644
--- a/src/server.js
+++ b/src/server.js
@@ -75,6 +75,7 @@ const apiRouter = require('./routers/apiRouter');
//Define Config variables
const config = require('../config.json');
+const package = require('../package.json');
const port = config.port;
const dbUrl = `mongodb://${config.db.user}:${config.db.pass}@${config.db.address}:${config.db.port}/${config.db.database}`;
@@ -208,7 +209,7 @@ Might be better if she kicked off everything at once, and ran a while loop to ch
This runs once at server startup, and most startups will run fairly quickly so... Not worth it?*/
async function asyncKickStart(){
//Lettum fuckin' know wassup
- console.log(`${config.instanceName}(Powered by Canopy) is booting up!`);
+ console.log(`${config.instanceName}(Powered by Canopy ${package.canopyDisplayVersion}) is booting up!`);
//Run legacy migration
await migrationModel.ingestLegacyDump();
diff --git a/src/views/about.ejs b/src/views/about.ejs
index 8842ff0..cebfc35 100644
--- a/src/views/about.ejs
+++ b/src/views/about.ejs
@@ -40,6 +40,8 @@ along with this program. If not, see . %>
it was decided that the original cytube fork, fore.st, had been run past it's prime. In summer/fall 2024, work began on a
replacement. The resulting software became Canopy, which was
first used to run the ourfore.st instance in late 2025.
+
+ Canopy Ver: <%= version %>