channelManager now tracks all active connectedUser objects for a given user.
This commit is contained in:
parent
261dce7b29
commit
6222535c47
3
.gitignore
vendored
3
.gitignore
vendored
|
|
@ -1,6 +1,9 @@
|
||||||
node_modules/
|
node_modules/
|
||||||
log/crash/*
|
log/crash/*
|
||||||
|
!log/crash
|
||||||
www/doc/*/*
|
www/doc/*/*
|
||||||
|
!www/doc/client
|
||||||
|
!www/doc/server
|
||||||
package-lock.json
|
package-lock.json
|
||||||
config.json
|
config.json
|
||||||
config.json.old
|
config.json.old
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ Canopy - 0.3-INDEV - Hotfix 1
|
||||||
|
|
||||||
Canopy - /ˈkæ.nə.pi/:
|
Canopy - /ˈkæ.nə.pi/:
|
||||||
- The upper layer of foliage and branches of a forest, containing the majority of animal life.
|
- The upper layer of foliage and branches of a forest, containing the majority of animal life.
|
||||||
|
- An honest attempt at an freedom/privacy respecting, libre, and open-source refrence implementation of what a stoner streaming service can be.
|
||||||
|
|
||||||
Canopy is a community chat & synced video embedding web application, intended to replace fore.st as the server software for ourfore.st.
|
Canopy is a community chat & synced video embedding web application, intended to replace fore.st as the server software for ourfore.st.
|
||||||
This new codebase intends to solve the following issues with the current CyTube based software:
|
This new codebase intends to solve the following issues with the current CyTube based software:
|
||||||
|
|
@ -22,10 +23,11 @@ The Canopy codebase does not, nor will it ever contain:
|
||||||
- Cryptocurrency/Blockchain integration
|
- Cryptocurrency/Blockchain integration
|
||||||
- 'Analytics/Telemtry' spyware
|
- 'Analytics/Telemtry' spyware
|
||||||
- The use of video sources which require proprietary 'Digital ~~Rights Management~~ Ristricitons Malware' such as Widevine.
|
- The use of video sources which require proprietary 'Digital ~~Rights Management~~ Ristricitons Malware' such as Widevine.
|
||||||
|
- The use of large language models, stable diffusion, or generative AI in either development or function.
|
||||||
|
|
||||||
Thirdparty media providers may or may not contain all of the above atrocities :P (though browser-side DRM extensions will never be required), always use an ad-blocker!
|
Thirdparty media providers may or may not contain all of the above atrocities :P (though browser-side DRM extensions will never be required), always use an ad-blocker!
|
||||||
|
|
||||||
Our current goal is to create a cleaner, more modern, purpose-built codebase that has feature-parity with the current version of fore.st, while writing improvements where possible. Once this is accomplished, and ourfore.st has been migrated, work will continue to re-create features from TTN, while also building completely new ones as well.
|
Our current goal is to create a cleaner, more modern, purpose-built back-end that has feature-parity with the current version of fore.st, writing improvements where possible. Paired with this functionality, are a mix of engineering and artistic choices which attempt to re-create the power-user friendly UX of desktop sites from the early 2010's, with the 'aged like wine' looks that late oughts/early web 2.0 designs graced us with. Making sure that pageloads are low, and GPU use is non-existant along the way, to ensure everything is usable, even on low-end machines.
|
||||||
|
|
||||||
## License
|
## License
|
||||||
Canopy is written by the community, and provided under the GNU Affero General Public License v3 in order to prevent Canopy from being used in proprietary software or shitcoin scams.
|
Canopy is written by the community, and provided under the GNU Affero General Public License v3 in order to prevent Canopy from being used in proprietary software or shitcoin scams.
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "canopy-of",
|
"name": "canopy-of-indev",
|
||||||
"version": "0.3",
|
"version": "0.4",
|
||||||
"license": "AGPL-3.0-only",
|
"license": "AGPL-3.0-only",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"altcha": "^1.0.7",
|
"altcha": "^1.0.7",
|
||||||
|
|
|
||||||
|
|
@ -74,6 +74,7 @@ class activeChannel{
|
||||||
* @param {Mongoose.Document} userDB - User Document Passthrough to save on DB Access
|
* @param {Mongoose.Document} userDB - User Document Passthrough to save on DB Access
|
||||||
* @param {Mongoose.Document} chanDB - Channnel Document Passthrough to save on DB Access
|
* @param {Mongoose.Document} chanDB - Channnel Document Passthrough to save on DB Access
|
||||||
* @param {Socket} socket - Requesting Socket
|
* @param {Socket} socket - Requesting Socket
|
||||||
|
* @returns {activeUser} active user object generated by the new connection
|
||||||
*/
|
*/
|
||||||
async handleConnection(userDB, chanDB, socket){
|
async handleConnection(userDB, chanDB, socket){
|
||||||
//get current user object from the userlist
|
//get current user object from the userlist
|
||||||
|
|
@ -104,10 +105,13 @@ class activeChannel{
|
||||||
this.playlistHandler.defineListeners(socket);
|
this.playlistHandler.defineListeners(socket);
|
||||||
|
|
||||||
//Hand off the connection initiation to it's user object
|
//Hand off the connection initiation to it's user object
|
||||||
await userObj.handleConnection(userDB, chanDB, socket)
|
const activeUser = await userObj.handleConnection(userDB, chanDB, socket)
|
||||||
|
|
||||||
//Send out the userlist
|
//Send out the userlist
|
||||||
this.broadcastUserList(socket.chan);
|
this.broadcastUserList(socket.chan);
|
||||||
|
|
||||||
|
//Return active user connection object for use by the channelManager object
|
||||||
|
return activeUser;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -115,11 +119,11 @@ class activeChannel{
|
||||||
* @param {Socket} socket - Requesting Socket
|
* @param {Socket} socket - Requesting Socket
|
||||||
*/
|
*/
|
||||||
handleDisconnect(socket){
|
handleDisconnect(socket){
|
||||||
//If we have more than one active connection
|
|
||||||
if(this.userList.get(socket.user.user).sockets.length > 1){
|
|
||||||
//temporarily store userObj
|
//temporarily store userObj
|
||||||
var userObj = this.userList.get(socket.user.user);
|
let userObj = this.userList.get(socket.user.user);
|
||||||
|
|
||||||
|
//If we have more than one active connection
|
||||||
|
if(userObj.sockets.length > 1){
|
||||||
//Filter out disconnecting socket from socket list, and set as current socket list for user
|
//Filter out disconnecting socket from socket list, and set as current socket list for user
|
||||||
userObj.sockets = userObj.sockets.filter((id) => {
|
userObj.sockets = userObj.sockets.filter((id) => {
|
||||||
return id != socket.id;
|
return id != socket.id;
|
||||||
|
|
@ -127,7 +131,11 @@ class activeChannel{
|
||||||
|
|
||||||
//Update the userlist
|
//Update the userlist
|
||||||
this.userList.set(socket.user.user, userObj);
|
this.userList.set(socket.user.user, userObj);
|
||||||
|
//If this is the last one
|
||||||
}else{
|
}else{
|
||||||
|
//Tell the server to handle the disconnection of this user object
|
||||||
|
this.server.handleUserDisconnect(userObj);
|
||||||
|
|
||||||
//If this is the last connection for this user, remove them from the userlist
|
//If this is the last connection for this user, remove them from the userlist
|
||||||
this.userList.delete(socket.user.user);
|
this.userList.delete(socket.user.user);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,7 @@ const chatHandler = require('./chatHandler');
|
||||||
class channelManager{
|
class channelManager{
|
||||||
/**
|
/**
|
||||||
* Instantiates object containing global server-side channel conection management logic
|
* Instantiates object containing global server-side channel conection management logic
|
||||||
* @param {Server} io - Socket.io server instanced passed down from server.js
|
* @param {Socket.io} io - Socket.io server instanced passed down from server.js
|
||||||
*/
|
*/
|
||||||
constructor(io){
|
constructor(io){
|
||||||
/**
|
/**
|
||||||
|
|
@ -46,6 +46,11 @@ class channelManager{
|
||||||
*/
|
*/
|
||||||
this.activeChannels = new Map;
|
this.activeChannels = new Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Map containing all active users. This may be redundant, however it improves preformance for user-specific inter-channel functionality
|
||||||
|
*/
|
||||||
|
this.activeUsers = new Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Global Chat Handler Object
|
* Global Chat Handler Object
|
||||||
*/
|
*/
|
||||||
|
|
@ -88,13 +93,48 @@ class channelManager{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Connection accepted past this point
|
||||||
|
|
||||||
//Define listeners for inter-channel classes
|
//Define listeners for inter-channel classes
|
||||||
this.defineListeners(socket);
|
this.defineListeners(socket);
|
||||||
this.chatHandler.defineListeners(socket);
|
this.chatHandler.defineListeners(socket);
|
||||||
|
|
||||||
//Hand off the connection to it's given active channel object
|
//Hand off the connection to it's given active channel object
|
||||||
//Lil' hacky to pass chanDB like that, but why double up on DB calls?
|
//Lil' hacky to pass chanDB like that, but why double up on DB calls?
|
||||||
activeChan.handleConnection(userDB, chanDB, socket);
|
const activeUser = await activeChan.handleConnection(userDB, chanDB, socket);
|
||||||
|
|
||||||
|
//Pull status from server-wide activeUsers map
|
||||||
|
let status = this.activeUsers.get(activeUser.user);
|
||||||
|
|
||||||
|
//If this user isn't connected anywhere else
|
||||||
|
if(status == null){
|
||||||
|
//initiate the entry
|
||||||
|
this.activeUsers.set(activeUser.user, [activeUser]);
|
||||||
|
//otherwise
|
||||||
|
}else{
|
||||||
|
//Push user to array by default
|
||||||
|
let pushUser = true;
|
||||||
|
|
||||||
|
//For each active connection within the status map
|
||||||
|
for(let curUser of status){
|
||||||
|
//If we're already listing this active user (we're splitting a user connection amongst several sockets)
|
||||||
|
if(curUser.channel.name == activeUser.channel.name){
|
||||||
|
//don't need to push it again
|
||||||
|
pushUser = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//if the user is flagged as un-added
|
||||||
|
if(pushUser){
|
||||||
|
//Add their connection object into the status array we pulled
|
||||||
|
status.push(activeUser);
|
||||||
|
|
||||||
|
//Set status entry to updated array
|
||||||
|
this.activeUsers.set(activeUser.set, status);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}else{
|
}else{
|
||||||
//Toss out anon's
|
//Toss out anon's
|
||||||
socket.emit("kick", {type: "disconnected", reason: "You must log-in to join this channel!"});
|
socket.emit("kick", {type: "disconnected", reason: "You must log-in to join this channel!"});
|
||||||
|
|
@ -217,6 +257,34 @@ class channelManager{
|
||||||
activeChan.handleDisconnect(socket, reason);
|
activeChan.handleDisconnect(socket, reason);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles a disconnection event for a single active user within a given channel (when all sockets disconnect)
|
||||||
|
* @param {*} userObj
|
||||||
|
*/
|
||||||
|
handleUserDisconnect(userObj){
|
||||||
|
//Create array to hold
|
||||||
|
let stillConnected = [];
|
||||||
|
|
||||||
|
//Crawl through all known user connections
|
||||||
|
this.crawlConnections(userObj.user, (curUser)=>{
|
||||||
|
//If we have a matching username from a different channel
|
||||||
|
if(curUser.user == userObj.user && userObj.channel.name != curUser.channel.name){
|
||||||
|
//Keep current user
|
||||||
|
stillConnected.push(curUser);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
//If we have anyone left
|
||||||
|
if(stillConnected.length > 0){
|
||||||
|
//save the remainder to the status map, otherwise unset the value.
|
||||||
|
this.activeUsers.set(userObj.user, stillConnected);
|
||||||
|
//Otherwise
|
||||||
|
}else{
|
||||||
|
//Delete the user from the status map
|
||||||
|
this.activeUsers.delete(userObj.user);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Pulls user information by socket
|
* Pulls user information by socket
|
||||||
* @param {Socket} socket - Socket to check
|
* @param {Socket} socket - Socket to check
|
||||||
|
|
@ -257,30 +325,28 @@ class channelManager{
|
||||||
* @param {Function} cb - Callback function to run active connections of a given user against
|
* @param {Function} cb - Callback function to run active connections of a given user against
|
||||||
*/
|
*/
|
||||||
crawlConnections(user, cb){
|
crawlConnections(user, cb){
|
||||||
//For each channel
|
//Pull connection list from status map
|
||||||
this.activeChannels.forEach((channel) => {
|
const list = this.activeUsers.get(user);
|
||||||
//Check and see if the user is connected
|
|
||||||
const foundUser = channel.userList.get(user);
|
|
||||||
|
|
||||||
//If we found a user and this channel hasn't been added to the list
|
//If we have active connections
|
||||||
if(foundUser){
|
if(list != null){
|
||||||
cb(foundUser);
|
//For each connection
|
||||||
|
for(let user of list){
|
||||||
|
//Run the callback against it
|
||||||
|
cb(user);
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Iterates through connections by a given username, and runs them through a given callback function/method
|
* Iterates through connections by a given username, and runs them through a given callback function/method
|
||||||
|
* This function is deprecated. Instead use channelManager.activeUsers.get(user)
|
||||||
* @param {String} user - Username to crawl connections against
|
* @param {String} user - Username to crawl connections against
|
||||||
* @param {Function} cb - Callback function to run active connections of a given user against
|
* @param {Function} cb - Callback function to run active connections of a given user against
|
||||||
*/
|
*/
|
||||||
getConnections(user){
|
getConnections(user){
|
||||||
//Create a list to store our connections
|
const connections = this.activeUsers.get(user);
|
||||||
var connections = [];
|
|
||||||
|
|
||||||
//crawl through connections
|
|
||||||
//this.crawlConnections(user,(foundUser)=>{connections.push(foundUser)});
|
|
||||||
this.crawlConnections(user,(foundUser)=>{connections.push(foundUser)});
|
|
||||||
|
|
||||||
//return connects
|
//return connects
|
||||||
return connections;
|
return connections;
|
||||||
|
|
|
||||||
|
|
@ -91,6 +91,7 @@ class connectedUser{
|
||||||
* @param {Mongoose.Document} userDB - User Document Passthrough to save on DB Access
|
* @param {Mongoose.Document} userDB - User Document Passthrough to save on DB Access
|
||||||
* @param {Mongoose.Document} chanDB - Channnel Document Passthrough to save on DB Access
|
* @param {Mongoose.Document} chanDB - Channnel Document Passthrough to save on DB Access
|
||||||
* @param {Socket} socket - Requesting Socket
|
* @param {Socket} socket - Requesting Socket
|
||||||
|
* @returns {activeUser} active user object generated by the new connection
|
||||||
*/
|
*/
|
||||||
async handleConnection(userDB, chanDB, socket){
|
async handleConnection(userDB, chanDB, socket){
|
||||||
//send metadata to client
|
//send metadata to client
|
||||||
|
|
@ -115,6 +116,10 @@ class connectedUser{
|
||||||
//Tattoo hashed IP address to user account for seven days
|
//Tattoo hashed IP address to user account for seven days
|
||||||
await userDB.tattooIPRecord(socket.handshake.address);
|
await userDB.tattooIPRecord(socket.handshake.address);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//Return active user object for use by activeChannel and channelManager objects
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
52
src/app/channel/message.js
Normal file
52
src/app/channel/message.js
Normal file
|
|
@ -0,0 +1,52 @@
|
||||||
|
/*Canopy - The next generation of stoner streaming software
|
||||||
|
Copyright (C) 2024-2025 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 <https://www.gnu.org/licenses/>.*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class representing a single chat message
|
||||||
|
*/
|
||||||
|
class message{
|
||||||
|
/**
|
||||||
|
* Instantiates a chat message object
|
||||||
|
* @param {connectedUser} sender - User who sent the message
|
||||||
|
* @param {Array} recipients - Array of connected users who are supposed to receive the message
|
||||||
|
* @param {String} msg - Contents of the message, with links replaced with numbered file-seperator markers
|
||||||
|
* @param {Array} links - Array of URLs/Links included in the message.
|
||||||
|
*/
|
||||||
|
constructor(sender, recipients, msg, links){
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User who sent the message
|
||||||
|
*/
|
||||||
|
this.sender = sender;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Array of strings containing usernames to send message to
|
||||||
|
*/
|
||||||
|
this.recipients = recipients;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Contenst of the messages, with links replaced with numbered file-seperator markers
|
||||||
|
*/
|
||||||
|
this.msg = msg;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Array of URLs/Links included in the message.
|
||||||
|
*/
|
||||||
|
this.links = links;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = message;
|
||||||
Loading…
Reference in a new issue