diff --git a/defaultFlair.json b/defaultFlair.json
new file mode 100644
index 0000000..6f6e608
--- /dev/null
+++ b/defaultFlair.json
@@ -0,0 +1,10 @@
+[
+ {
+ "name": "glitter",
+ "rank": "gold"
+ },
+ {
+ "name": "negativeGlitter",
+ "ranke": "gold"
+ }
+]
\ No newline at end of file
diff --git a/src/app/channel/channelManager.js b/src/app/channel/channelManager.js
index 00805ab..9cf2273 100644
--- a/src/app/channel/channelManager.js
+++ b/src/app/channel/channelManager.js
@@ -16,6 +16,8 @@ along with this program. If not, see .*/
//Local Imports
const channelModel = require('../../schemas/channelSchema');
+const flairModel = require('../../schemas/flairSchema');
+const userModel = require('../../schemas/userSchema');
const activeChannel = require('./activeChannel');
const chatHandler = require('./chatHandler');
@@ -26,8 +28,17 @@ module.exports.handleConnection = async function(io, socket){
//Prevent logged out connections and authenticate socket
if(socket.request.session.user != null){
try{
+ //Find the user in the Database since the session won't store enough data to fulfill our needs :P
+ const userDB = await userModel.findOne({user: socket.request.session.user.user});
+
//Set socket user and channel values
- socket.user = socket.request.session.user;
+ socket.user = {
+ id: userDB.id,
+ user: userDB.user,
+ rank: userDB.rank,
+ flair: userDB.flair
+ };
+
socket.chanName = socket.handshake.headers.referer.split('/c/')[1];
//Check if channel exists
diff --git a/src/app/channel/chatHandler.js b/src/app/channel/chatHandler.js
index e8f1734..fe23bec 100644
--- a/src/app/channel/chatHandler.js
+++ b/src/app/channel/chatHandler.js
@@ -16,9 +16,11 @@ along with this program. If not, see .*/
//NPM Imports
const validator = require('validator');//No express here, so regular validator it is!
+const loggerUtils = require('../../utils/loggerUtils');
+const userModel = require('../../schemas/userSchema');
module.exports.defineListeners = function(io, socket){
- socket.on("chat-message", (data) => {
+ socket.on("chatMessage", (data) => {
//Trim and Sanatize for XSS
const msg = validator.trim(validator.escape(data.msg));
//make sure high is an int
@@ -34,6 +36,22 @@ module.exports.defineListeners = function(io, socket){
return;
}
- io.in(socket.chan).emit("chat-message", {user: socket.user.user, msg, high});
+ io.in(socket.chan).emit("chatMessage", {user: socket.user.user, flair: socket.user.flair, high, msg});
+ });
+
+ socket.on("setFlair", async (data) => {
+ const userDB = await userModel.findOne({user: socket.user.user});
+
+ if(userDB){
+ try{
+ //We can take this data raw since our schema checks it against existing flairs, and mongoose sanatizes queries
+ userDB.flair = data.flair;
+ await userDB.save();
+ }catch(err){
+ return loggerUtils.socketExceptionHandler(socket, err);
+ }
+ }
+
+
});
}
\ No newline at end of file
diff --git a/src/schemas/flairSchema.js b/src/schemas/flairSchema.js
new file mode 100644
index 0000000..ec43073
--- /dev/null
+++ b/src/schemas/flairSchema.js
@@ -0,0 +1,62 @@
+/*Canopy - The next generation of stoner streaming software
+Copyright (C) 2024 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 .*/
+
+//NPM Imports
+const {mongoose} = require('mongoose');
+
+//Local Imports
+const permissionModel = require("./permissionSchema");
+const defaultFlair = require("../../defaultFlair.json");
+
+const flairSchema = new mongoose.Schema({
+ name:{
+ type: mongoose.SchemaTypes.String,
+ required: true
+ },
+ rank: {
+ type: mongoose.SchemaTypes.String,
+ enum: permissionModel.rankEnum,
+ default: "user",
+ required: true
+ }
+});
+
+flairSchema.statics.loadDefaults = async function(){
+ //For each entry in the defaultFlair.json file
+ defaultFlair.forEach(async (flair) => {
+ try{
+ //Look for flair matching the one from our file
+
+ const foundFlair = await this.findOne({name: flair.name});
+
+ //if the flair doesn't exist
+ if(!foundFlair){
+ const flairDB = await this.create(flair);
+ console.log(`Loading default flair '${flair.name} into DB from defaultFlair.json`);
+ }
+
+ }catch(err){
+ if(flair != null){
+ console.log(`Error loading flair '${flair.name}':`);
+ }else{
+ console.log("Error, null flair:");
+ }
+ console.log(err);
+ }
+ });
+}
+
+module.exports = mongoose.model("flair", flairSchema);
\ No newline at end of file
diff --git a/src/schemas/userSchema.js b/src/schemas/userSchema.js
index 11608fb..4dc56f6 100644
--- a/src/schemas/userSchema.js
+++ b/src/schemas/userSchema.js
@@ -19,8 +19,9 @@ const {mongoose} = require('mongoose');
//local imports
const server = require('../server.js');
-const statSchema = require('./statSchema.js');
-const permissionSchema = require('./permissionSchema.js');
+const statModel = require('./statSchema.js');
+const flairModel = require('./flairSchema.js');
+const permissionModel = require('./permissionSchema.js');
const hashUtil = require('../utils/hashUtils');
@@ -48,7 +49,7 @@ const userSchema = new mongoose.Schema({
rank: {
type: mongoose.SchemaTypes.String,
required: true,
- enum: permissionSchema.rankEnum,
+ enum: permissionModel.rankEnum,
default: "user"
},
tokes: {
@@ -75,15 +76,39 @@ const userSchema = new mongoose.Schema({
type: mongoose.SchemaTypes.String,
required: true,
default: "Signature not set!"
- }
+ },
+ flair: {
+ type: mongoose.SchemaTypes.String,
+ required: false,
+ default: ""
+ }
});
//This is one of those places where you really DON'T want to use an arrow function over an anonymous one!
userSchema.pre('save', async function (next){
+
+ //If the password was changed
if(this.isModified("pass")){
+ //Hash that sunnovabitch, no questions asked.
this.pass = hashUtil.hashPassword(this.pass);
}
+ //If the flair was changed
+ if(this.isModified("flair")){
+ //If we're not disabling flair
+ if(this.flair != ""){
+ //Look for the flair that was set
+ const foundFlair = await flairModel.findOne({name: this.flair});
+
+ //If new flair value doesn't corrispond to an existing flair
+ if(!foundFlair){
+ //Throw a shit fit. Do not pass go. Do not collect $200.
+ throw new Error("Invalid flair!");
+ }
+ }
+ }
+
+ //All is good, continue on saving.
next();
});
@@ -97,7 +122,7 @@ userSchema.statics.register = async function(userObj){
if(userDB){
throw new Error("User name/email already taken!");
}else{
- const id = await statSchema.incrementUserCount();
+ const id = await statModel.incrementUserCount();
const newUser = await this.create({id, user, pass, email});
}
}else{
diff --git a/src/server.js b/src/server.js
index cc3d9e8..d9515c7 100644
--- a/src/server.js
+++ b/src/server.js
@@ -25,6 +25,7 @@ const mongoose = require('mongoose');
//Define Local Imports
const statModel = require('./schemas/statSchema');
+const flairModel = require('./schemas/flairSchema');
const channelManager = require('./app/channel/channelManager');
const indexRouter = require('./routers/indexRouter');
const registerRouter = require('./routers/registerRouter');
@@ -105,6 +106,9 @@ app.use(express.static(path.join(__dirname, '../www')));
//Increment launch counter
statModel.incrementLaunchCount();
+//Load flairs
+flairModel.loadDefaults();
+
//Hand over general-namespace socket.io connections to the channel manager
io.on("connection", (socket) => {channelManager.handleConnection(io, socket)} );
diff --git a/src/utils/loggerUtils.js b/src/utils/loggerUtils.js
index 5a3f65b..86fd372 100644
--- a/src/utils/loggerUtils.js
+++ b/src/utils/loggerUtils.js
@@ -19,4 +19,9 @@ module.exports.exceptionHandler = function(res, err){
//if not yell at the browser for fucking up, and tell it what it did wrong.
res.status(400);
return res.send({errors: [{type: "Caught Exception", msg: err.message, date: new Date()}]});
+}
+
+module.exports.socketExceptionHandler = function(socket, err){
+ //if not yell at the browser for fucking up, and tell it what it did wrong.
+ return socket.emit("error", {errors: [{type: "Caught Exception", msg: err.message, date: new Date()}]});
}
\ No newline at end of file
diff --git a/www/js/channel/channel.js b/www/js/channel/channel.js
index 5aa4ec5..5622942 100644
--- a/www/js/channel/channel.js
+++ b/www/js/channel/channel.js
@@ -42,6 +42,10 @@ class channel{
this.socket.on("connect", () => {
document.title = `${this.channelName} - Connected`
});
+
+ this.socket.on("error", (data) => {
+ console.log(data);
+ });
}
}
diff --git a/www/js/channel/chat.js b/www/js/channel/chat.js
index 153a46c..19cdda2 100644
--- a/www/js/channel/chat.js
+++ b/www/js/channel/chat.js
@@ -64,7 +64,7 @@ class chatBox{
}
defineListeners(){
- this.client.socket.on("chat-message", (data) => {
+ this.client.socket.on("chatMessage", (data) => {
this.displayChat(data);
});
}
@@ -147,7 +147,7 @@ class chatBox{
async send(event){
if((!event || !event.key || event.key == "Enter") && this.chatPrompt.value){
- this.client.socket.emit("chat-message",{msg: this.chatPrompt.value, high: this.highLevel.value});
+ this.client.socket.emit("chatMessage",{msg: this.chatPrompt.value, high: this.highLevel.value});
this.chatPrompt.value = "";
}
}