diff --git a/src/schemas/statSchema.js b/src/schemas/statSchema.js
index 0aed906..cdcd902 100644
--- a/src/schemas/statSchema.js
+++ b/src/schemas/statSchema.js
@@ -70,14 +70,6 @@ statSchema.statics.getStats = async function(){
var stats = await this.findOne({});
if(stats){
- //If the current listed version doesnt match current running version
- if(stats.version != package.version){
- //Update stat version
- stats.version = package.version;
- //Save stat document
- await stats.save();
- }
-
//If we found something then the statistics document exist and this is it,
//So long as no one else has fucked with the database it should be the only one. (is this forshadowing for a future bug?)
return stats;
diff --git a/src/schemas/tokebot/tokeCommandSchema.js b/src/schemas/tokebot/tokeCommandSchema.js
index 88b2709..3694afa 100644
--- a/src/schemas/tokebot/tokeCommandSchema.js
+++ b/src/schemas/tokebot/tokeCommandSchema.js
@@ -120,7 +120,7 @@ tokeCommandSchema.statics.loadDefaults = async function(){
if(toke != null){
loggerUtils.dumpError(err);
}else{
- loggerUtils.consoleWarn("Error, null toke!");
+ loggerUtils.consoleShout("Error, null toke!");
}
}
}
diff --git a/src/schemas/user/migrationSchema.js b/src/schemas/user/migrationSchema.js
index c516519..9092bf8 100644
--- a/src/schemas/user/migrationSchema.js
+++ b/src/schemas/user/migrationSchema.js
@@ -105,7 +105,7 @@ migrationSchema.statics.ingestLegacyDump = async function(){
//If we caught an error (most likely it's missing)
}catch(err){
- loggerUtils.consoleWarn("No migration files detected! Pleas provide legacy migration files or disable migration from config.json!");
+ loggerUtils.consoleShout("No migration files detected! Pleas provide legacy migration files or disable migration from config.json!");
//BAIL!
return;
}
@@ -151,7 +151,7 @@ migrationSchema.statics.ingestLegacyDump = async function(){
//Pass toke logs over to the stat model for further ingestion
await tokeModel.ingestLegacyTokes(tokeLogs);
- loggerUtils.consoleWarn(`Legacy Server Migration Completed at: ${new Date().toLocaleString()}`);
+ loggerUtils.consoleShout(`Legacy Server Migration Completed at: ${new Date().toLocaleString()}`);
}catch(err){
return loggerUtils.localExceptionHandler(err);
}
@@ -174,8 +174,8 @@ migrationSchema.statics.ingestLegacyUser = async function(rawProfile){
//If we have an invalid line
if(profileMatches <= 0){
- loggerUtils.consoleWarn('Bad profile detected in legacy dump:');
- loggerUtils.consoleWarn(rawProfile);
+ loggerUtils.consoleShout('Bad profile detected in legacy dump:');
+ loggerUtils.consoleShout(rawProfile);
//BAIL!
return;
}
@@ -199,8 +199,8 @@ migrationSchema.statics.ingestLegacyUser = async function(rawProfile){
//If profile array is the wrong length
if(profileArray.length != 10){
- loggerUtils.consoleWarn('Bad profile detected in legacy dump:');
- loggerUtils.consoleWarn(profileArray);
+ loggerUtils.consoleShout('Bad profile detected in legacy dump:');
+ loggerUtils.consoleShout(profileArray);
//BAIL!
return;
}
@@ -212,7 +212,7 @@ migrationSchema.statics.ingestLegacyUser = async function(rawProfile){
//If we found the user in the database
if(foundMigration != null || foundUser != null){
//Scream
- loggerUtils.consoleWarn(`Found legacy user ${profileArray[1]} in database, skipping migration!`);
+ loggerUtils.consoleShout(`Found legacy user ${profileArray[1]} in database, skipping migration!`);
//BAIL!
return;
}
diff --git a/src/server.js b/src/server.js
index 4330a53..8e78e99 100644
--- a/src/server.js
+++ b/src/server.js
@@ -40,6 +40,7 @@ const configCheck = require('./utils/configCheck');
const scheduler = require('./utils/scheduler');
const {errorMiddleware} = require('./utils/loggerUtils');
const sessionUtils = require('./utils/sessionUtils');
+const upgradeUtils = require('./utils/upgradeUtils');
//Validator
const accountValidator = require('./validators/accountValidator');
//DB Model
@@ -231,6 +232,9 @@ async function asyncKickStart(){
//Load default emotes
await emoteModel.loadDefaults();
+ //Check for and handle any server upgrades
+ await upgradeUtils.upgradeCheck();
+
//Kick off scheduled-jobs
scheduler.kickoff();
diff --git a/src/utils/configCheck.js b/src/utils/configCheck.js
index 84fd22d..d45e1c8 100644
--- a/src/utils/configCheck.js
+++ b/src/utils/configCheck.js
@@ -31,52 +31,52 @@ module.exports.securityCheck = function(){
//Check Protocol
if(config.protocol.toLowerCase() != 'https'){
//If it's insecure then warn the admin
- loggerUtil.consoleWarn("Starting in HTTP mode. This server should be used for development purposes only!");
+ loggerUtil.consoleShout("Starting in HTTP mode. This server should be used for development purposes only!");
}
//Check mail protocol
if(!config.mail.secure){
//If it's insecure then warn the admin
- loggerUtil.consoleWarn("Mail transport security disabled! This server should be used for development purposes only!");
+ loggerUtil.consoleShout("Mail transport security disabled! This server should be used for development purposes only!");
}
//check password pepper
if(!validator.isStrongPassword(config.secrets.passwordSecret) || config.secrets.passwordSecret == "CHANGE_ME"){
- loggerUtil.consoleWarn("Insecure Password Secret! Change Password Secret!");
+ loggerUtil.consoleShout("Insecure Password Secret! Change Password Secret!");
}
//check RememberMe pepper
if(!validator.isStrongPassword(config.secrets.rememberMeSecret) || config.secrets.rememberMeSecret == "CHANGE_ME"){
- loggerUtil.consoleWarn("Insecure RememberMe Secret! Change RememberMe Secret!");
+ loggerUtil.consoleShout("Insecure RememberMe Secret! Change RememberMe Secret!");
}
//check session secret
if(!validator.isStrongPassword(config.secrets.sessionSecret) || config.secrets.sessionSecret == "CHANGE_ME"){
- loggerUtil.consoleWarn("Insecure Session Secret! Change Session Secret!");
+ loggerUtil.consoleShout("Insecure Session Secret! Change Session Secret!");
}
//check altcha secret
if(!validator.isStrongPassword(config.secrets.altchaSecret) || config.secrets.altchaSecret == "CHANGE_ME"){
- loggerUtil.consoleWarn("Insecure Altcha Secret! Change Altcha Secret!");
+ loggerUtil.consoleShout("Insecure Altcha Secret! Change Altcha Secret!");
}
//check ipHash secret
if(!validator.isStrongPassword(config.secrets.ipSecret) || config.secrets.ipSecret == "CHANGE_ME"){
- loggerUtil.consoleWarn("Insecure IP Hashing Secret! Change IP Hashing Secret!");
+ loggerUtil.consoleShout("Insecure IP Hashing Secret! Change IP Hashing Secret!");
}
//check DB pass
if(!validator.isStrongPassword(config.db.pass) || config.db.pass == "CHANGE_ME" || config.db.pass == config.db.user){
- loggerUtil.consoleWarn("Insecure Database Password! Change Database password!");
+ loggerUtil.consoleShout("Insecure Database Password! Change Database password!");
}
//check email pass
if(!validator.isStrongPassword(config.mail.pass) || config.mail.pass == "CHANGE_ME"){
- loggerUtil.consoleWarn("Insecure Email Password! Change Email password!");
+ loggerUtil.consoleShout("Insecure Email Password! Change Email password!");
}
//check debug mode
if(config.debug){
- loggerUtil.consoleWarn("Debug mode enabled! Understand the risks and security implications before enabling on production servers!");
+ loggerUtil.consoleShout("Debug mode enabled! Understand the risks and security implications before enabling on production servers!");
}
}
\ No newline at end of file
diff --git a/src/utils/loggerUtils.js b/src/utils/loggerUtils.js
index bdac8db..1835e58 100644
--- a/src/utils/loggerUtils.js
+++ b/src/utils/loggerUtils.js
@@ -142,12 +142,20 @@ module.exports.socketCriticalExceptionHandler = function(socket, err){
return socket.disconnect();
}
+/**
+ * Shouts into the server console
+ * @param {String} string - String to shout into to the console
+ */
+module.exports.consoleShout = function(string){
+ console.warn('\x1b[31m\x1b[4m%s\x1b[0m',string);
+}
+
/**
* Prints warning text to server console
* @param {String} string - String to print to console
*/
module.exports.consoleWarn = function(string){
- console.warn('\x1b[31m\x1b[4m%s\x1b[0m',string);
+ console.warn('\x1b[33m%s\x1b[0m',string);
}
/**
@@ -208,7 +216,7 @@ module.exports.dumpLog = async function(content, name, subDir = '/', muzzle = fa
}catch(err){
if(!muzzle){
//Shout about it
- module.exports.consoleWarn("Log folder missing, mking dir!")
+ module.exports.consoleShout("Log folder missing, mking dir!")
}
//Make it if doesn't
@@ -223,17 +231,17 @@ module.exports.dumpLog = async function(content, name, subDir = '/', muzzle = fa
if(!muzzle){
//Whine about the error
- module.exports.consoleWarn(`Warning: Unexpected Server Crash gracefully dumped to '${path}'... SOMETHING MAY BE VERY BROKEN!!!!`);
+ module.exports.consoleShout(`Warning: Unexpected Server Crash gracefully dumped to '${path}'... SOMETHING MAY BE VERY BROKEN!!!!`);
}
//If somethine went really really wrong
}catch(doubleErr){
if(!muzzle){
//Use humor to cope with the pain
- module.exports.consoleWarn("Yo Dawg, I herd you like errors, so I put an error in your error dump, so you can dump while you dump:");
+ module.exports.consoleShout("Yo Dawg, I herd you like errors, so I put an error in your error dump, so you can dump while you dump:");
//Dump the original error to console
- module.exports.consoleWarn(err);
+ module.exports.consoleShout(err);
//Dump the error we had saving that error to file to console
- module.exports.consoleWarn(doubleErr);
+ module.exports.consoleShout(doubleErr);
}
}
}
diff --git a/src/utils/sessionUtils.js b/src/utils/sessionUtils.js
index fd8606b..bb746a9 100644
--- a/src/utils/sessionUtils.js
+++ b/src/utils/sessionUtils.js
@@ -18,6 +18,7 @@ along with this program. If not, see .*/
const {validationResult, matchedData} = require('express-validator');
//Local Imports
+const server = require('../server.js');
const config = require('../../config.json');
const {userModel} = require('../schemas/user/userSchema.js');
const userBanModel = require('../schemas/user/userBanSchema.js');
@@ -187,6 +188,22 @@ module.exports.killSession = async function(session){
session.destroy();
}
+module.exports.killAllSessions = function(){
+ //Grab the entire session store from the server
+ server.store.all((err, sessions) => {
+ //You guys ever hear of a 'not my' problem? Fucking y33tskies lmao, better use a try/catch
+ if(err){
+ throw err;
+ }
+
+ //crawl through active sessions
+ sessions.forEach((session) => {
+ //Fuckin' nukem!
+ server.store.destroy(session.seshid);
+ });
+ });
+}
+
/**
* Returns how many failed login attempts within the past day or so since the last login has occured for a given user
* @param {String} user - User to check map against
diff --git a/src/utils/upgradeUtils.js b/src/utils/upgradeUtils.js
new file mode 100644
index 0000000..be6e52c
--- /dev/null
+++ b/src/utils/upgradeUtils.js
@@ -0,0 +1,177 @@
+ /*Canopy - The next generation of stoner streaming software
+Copyright (C) 2024-2026 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 .*/
+
+//Config Imports
+const package = require('../../package.json');
+
+//Local Imports
+const loggerUtils = require('./loggerUtils');
+const sessionUtils = require('./sessionUtils');
+const statModel = require('../schemas/statSchema');
+const { userModel } = require('../schemas/user/userSchema');
+const channelModel = require('../schemas/channel/channelSchema');
+const archiveModel = require('../schemas/channel/media/archivedMediaSchema');
+const { channelPlaylistModel } = require('../schemas/channel/media/playlist/playlistSchema');
+const { userPlaylistModel } = require('../schemas/channel/media/playlist/playlistSchema');
+const archivedMedia = require('../app/channel/media/archivedMedia');
+
+/**
+ * Checks server for recent upgrades upon boot
+ */
+module.exports.upgradeCheck = async function(){
+ //Get the server statistics document from Database
+ const stats = await statModel.getStats();
+
+ //Throw this flag if an upgrade is initiated to automagically and silently initiate all upgrades after it
+ let upgradeStarted = false;
+
+ //If we're upgrading from Panama Red (or older)
+ if(stats.version == "<0.2.0"){
+ //Initiate upgrade procedure from Panama Red
+ await upgradePanamaRed();
+
+ //Throw upgrade started flag
+ upgradeStarted = true;
+ }
+
+ //If we upgraded
+ if(upgradeStarted){
+ //Update the statistics document to reflect the new version
+ stats.version = package.version;
+ //Write the statistics document to the DB
+ await stats.save();
+ }
+
+ loggerUtils.consoleShout(`Database Upgrading to ${package.canopyDisplayVersion} Complete!`);
+}
+
+/**
+ * Upgrades server database from Panama Red
+ * @param {Boolean} quiet - tells upgrade logic to STFU!
+ */
+async function upgradePanamaRed(quiet = false){
+ //If we're bein' loud
+ if(!quiet){
+ upgradeWarn("0.1-Alpha (Panama Red)");
+ }
+
+ //Warn of impending session death
+ loggerUtils.consoleWarn("Killing all active sessions!");
+ //Nuke existing sessions since the new version requires the user _id in there
+ sessionUtils.killAllSessions();
+
+ //Pull all channels into RAM
+ const channels = await channelModel.find();
+ //Pull all users into RAM
+ const users = await userModel.find();
+
+ //For every channel on the server
+ for(channel of channels){
+ //Create variables to track upgraded objects
+ let archived = 0;
+ let playlists = 0;
+
+ //log the migration
+ loggerUtils.consoleWarn(`Upgrading Channel '${channel.name}'..`);
+
+ //Iterate through the given channels media archive
+ for(entry of channel.media.archived){
+ //Re-hydrate archived media object
+ const mediaObject = archivedMedia.fromQueuedMedia(entry.rehydrate())
+
+ //Tattoo channel name into media object
+ mediaObject.channel = channel.name;
+
+ //Add media object to the site-wide media archive
+ await archiveModel.create(mediaObject);
+
+ //We could just pull the archvied array count, but this feels more honest XP
+ archived++;
+ }
+
+ //Iterate through the given channels playlists
+ for(playlist of channel.media.playlists){
+ //Create channel playlist in site-wide collection
+ await channelPlaylistModel.create({
+ name: playlist.name,
+ media: playlist.media,
+ defaultTitles: playlist.defaultTitles,
+ channel: channel._id
+ })
+
+ //We could just pull the playlists array count, but this feels more honest XP
+ playlists++;
+ }
+
+ //If we did anything
+ if(playlists > 0 || archived > 0){
+ //Clear out archived media from channel document
+ channel.media.archived = [];
+
+ //Clear out playlists from channel document
+ channel.media.playlists = [];
+
+ //Save updated channel document
+ await channel.save();
+
+ //log the migration
+ loggerUtils.consoleWarn(`Channel '${channel.name}; Upgrade Complete! Optimized ${playlists} Playlist(s), and ${archived} Media Object(s)!`);
+ }
+ }
+
+ //For each registered user on the server
+ for(user of users){
+ //Create variable to track upgraded playlist count
+ let playlists = 0;
+
+ //Iterate through users playlists
+ for(playlist of user.playlists){
+ //Create channel playlist in site-wide collection
+ await userPlaylistModel.create({
+ name: playlist.name,
+ media: playlist.media,
+ defaultTitles: playlist.defaultTitles,
+ user: user._id
+ })
+
+ //We could just pull the playlists array count, but this feels more honest XP
+ playlists++;
+ }
+
+ //If we did anything
+ if(playlists > 0){
+ //Clear out playlists from channel document
+ user.playlists = [];
+
+ //Save updated channel document
+ await user.save();
+
+ //log the migration
+ loggerUtils.consoleWarn(`User '${user.user}' Upgrade Complete! Optimized ${playlists} Playlist(s)!`);
+ }
+ }
+}
+
+/**
+ * Warns server admin of impending Database upgrades
+ * @param {String} versionString - Version we're upgrading from
+ */
+function upgradeWarn(versionString){
+ //Rise and Shout the ~~AlphaSmart's~~ upgrade's out!
+ //https://eeggs.com/images/items/2942.full.jpg
+ loggerUtils.consoleShout("Server Upgrade Detected!");
+ loggerUtils.consoleShout(`Upgrading Database from ${versionString} to ${package.canopyDisplayVersion}`);
+}
\ No newline at end of file