mirror of
https://github.com/IrosTheBeggar/mStream.git
synced 2025-10-27 07:31:02 +00:00
223 lines
5.7 KiB
JavaScript
223 lines
5.7 KiB
JavaScript
// Websocket Server
|
|
const WebSocketServer = require('ws').Server;
|
|
const url = require('url');
|
|
const winston = require('winston');
|
|
const jwt = require('jsonwebtoken');
|
|
|
|
// list of currently connected clients (users)
|
|
var clients = {};
|
|
// Any code in here will be limited in functionality
|
|
var guests = {};
|
|
|
|
// Map code to JWT
|
|
var codeTokenMap = {};
|
|
|
|
const allowedCommands = [
|
|
'next',
|
|
'previous',
|
|
'playPause',
|
|
'addSong',
|
|
'getPlaylist',
|
|
'removeSong',
|
|
];
|
|
const guestCommands = [
|
|
'addSong',
|
|
'getPlaylist'
|
|
];
|
|
|
|
|
|
// This part is run after the login code
|
|
exports.setup = function (mstream, server, program) {
|
|
var vcFunc = function (info, cb) {
|
|
try {
|
|
const code = url.parse(info.req.url, true).query.code;
|
|
info.req.code = code;
|
|
if (info.req.code && ((code in clients) || (code in guests))) {
|
|
cb(false, 403, 'Code In Use');
|
|
return;
|
|
}
|
|
} catch (err) {}
|
|
cb(true);
|
|
}
|
|
|
|
// If we are logging in
|
|
if (program.auth) {
|
|
vcFunc = function (info, cb) {
|
|
var token;
|
|
|
|
// Tokens are attached as a GET param
|
|
try {
|
|
token = url.parse(info.req.url, true).query.token;
|
|
} catch (err) {
|
|
cb(false, 401, 'Unauthorized');
|
|
return;
|
|
}
|
|
|
|
if (!token) {
|
|
cb(false, 401, 'Unauthorized');
|
|
}
|
|
else {
|
|
jwt.verify(token, program.secret, (err, decoded) => {
|
|
if (err) {
|
|
cb(false, 401, 'Unauthorized');
|
|
return;
|
|
}
|
|
|
|
try {
|
|
const code = url.parse(info.req.url, true).query.code;
|
|
info.req.code = code;
|
|
} catch (err) {}
|
|
|
|
if (info.req.code && ((info.req.code in clients) || (info.req.code in guests))) {
|
|
cb(false, 403, 'Code In Use');
|
|
return;
|
|
}
|
|
|
|
// We are going to create a new JWT specifically for this session
|
|
const sendData = {
|
|
username: decoded.username,
|
|
restrictedFunctions: ['/db/recursive-scan', '/saveplaylist', '/deleteplaylist', '/download'] // TODO: Should probably have more in here
|
|
}
|
|
|
|
info.req.jwt = jwt.sign(sendData, program.secret);
|
|
cb(true);
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
const wss = new WebSocketServer({ server: server, verifyClient: vcFunc });
|
|
wss.on('connection', (connection, req) => {
|
|
// Generate code and assure it doesn't exist
|
|
var code = createAccountNumber(10000);
|
|
var guestcode = createAccountNumber(10000);
|
|
if (req.code) {
|
|
code = req.code;
|
|
}
|
|
|
|
// Handle code failures
|
|
if (code === false || guestcode === false) {
|
|
connection.send(JSON.stringify({ error: 'Failed To Create Instance' }));
|
|
return;
|
|
}
|
|
|
|
winston.info(`Websocket Connection Accepted With Code: ${code}`);
|
|
|
|
// Add code to clients object
|
|
clients[code] = connection;
|
|
guests[guestcode] = code;
|
|
|
|
// create JWT
|
|
var token = false;
|
|
if (req.jwt) {
|
|
token = req.jwt;
|
|
codeTokenMap[code] = token;
|
|
codeTokenMap[guestcode] = token;
|
|
}
|
|
|
|
// Send Code
|
|
connection.send(JSON.stringify({ code: code, guestCode: guestcode, token: token }));
|
|
|
|
// user sent some message
|
|
connection.on('message', (message) => {
|
|
// Send client code back
|
|
connection.send(JSON.stringify({ code: code, guestCode: guestcode }));
|
|
});
|
|
|
|
|
|
// user disconnected
|
|
connection.on('close', (connection) => {
|
|
// Remove client from array
|
|
delete guests[guestcode];
|
|
delete clients[code];
|
|
|
|
if (codeTokenMap[code]) {
|
|
delete codeTokenMap[code];
|
|
delete codeTokenMap[guestcode];
|
|
}
|
|
});
|
|
});
|
|
|
|
|
|
// Function for creating account numbers
|
|
function createAccountNumber(limit = 100000) {
|
|
var n = 0;
|
|
while (true) {
|
|
code = Math.floor(Math.random() * (limit * 9)) + limit;
|
|
if (!(code in clients) && !(code in guests)) {
|
|
break;
|
|
}
|
|
if (n === 10) {
|
|
winston.error('Failed to create ID for jukebox.');
|
|
// FIXME: Try again with a larger number size
|
|
return false;
|
|
}
|
|
n++;
|
|
}
|
|
return code;
|
|
}
|
|
|
|
// Send codes to client
|
|
mstream.post('/jukebox/push-to-client', (req, res) => {
|
|
var clientCode = req.body.code;
|
|
const command = req.body.command;
|
|
|
|
// Check that code exists
|
|
if (!(clientCode in clients) && !(clientCode in guests)) {
|
|
res.status(500).json({ error: 'Client code not found' });
|
|
return;
|
|
}
|
|
|
|
// Make sure command is allowed
|
|
if (allowedCommands.indexOf(command) === -1) {
|
|
res.status(500).json({ error: 'Command Not Recognized' });
|
|
return;
|
|
}
|
|
|
|
if (clientCode in guests) {
|
|
// Check that command does not violate guest conditions
|
|
if (guestCommands.indexOf(command) === -1) {
|
|
res.status(500).json({ error: 'The command is not allowed for guests' });
|
|
return;
|
|
}
|
|
|
|
clientCode = guests[clientCode];
|
|
}
|
|
|
|
// Handle extra data for Add File Commands
|
|
var sendFile = '';
|
|
if (req.body.file) {
|
|
sendFile = req.body.file;
|
|
}
|
|
|
|
// Push commands to client
|
|
clients[clientCode].send(JSON.stringify({ command: command, file: sendFile }));
|
|
|
|
// Send confirmation back to user
|
|
res.json({ status: 'done' });
|
|
});
|
|
}
|
|
|
|
// This part is run before the login code
|
|
exports.setup2 = function (mstream, server, program) {
|
|
mstream.post('/jukebox/does-code-exist', function (req, res) {
|
|
const clientCode = req.body.code;
|
|
|
|
// Check that code exists
|
|
if (!(clientCode in clients) && !(clientCode in guests)) {
|
|
res.json({ status: false });
|
|
return;
|
|
}
|
|
|
|
// Get Token
|
|
var jwt = false;
|
|
if (codeTokenMap[clientCode]) {
|
|
jwt = codeTokenMap[clientCode];
|
|
}
|
|
|
|
var guestStatus = (clientCode in guests);
|
|
res.json({ status: true, guestStatus: guestStatus, token: jwt });
|
|
});
|
|
}
|