diff --git a/server/chat.js b/server/chat.js index a538adc..f46655f 100644 --- a/server/chat.js +++ b/server/chat.js @@ -3,12 +3,19 @@ const { serverConfig } = require('./server_config'); const { logChat } = require('./console'); const helpers = require('./helpers'); +function heartbeat() { // WebSocket heartbeat helper + this.isAlive = true; +} + function createChatServer(storage) { if (!serverConfig.webserver.chatEnabled) return null; const chatWss = new WebSocket.Server({ noServer: true }); chatWss.on('connection', (ws, request) => { + ws.isAlive = true; + ws.on('pong', heartbeat); + const clientIp = request.headers['x-forwarded-for'] || request.socket.remoteAddress; const userCommandHistory = {}; @@ -25,19 +32,18 @@ function createChatServer(storage) { ws.send(JSON.stringify(historyMessage)); }); - const ipMessage = { + ws.send(JSON.stringify({ type: 'clientIp', ip: clientIp, admin: request.session?.isAdminAuthenticated - }; + })); - ws.send(JSON.stringify(ipMessage)); const userCommands = {}; let lastWarn = { time: 0 }; ws.on('message', (message) => { - helpers.antispamProtection( + message = helpers.antispamProtection( message, clientIp, ws, @@ -45,9 +51,12 @@ function createChatServer(storage) { lastWarn, userCommandHistory, '5', - 'chat' + 'chat', + 512 ); + if(!message) return; + let messageData; try { @@ -57,8 +66,6 @@ function createChatServer(storage) { return; } - console.log("Chat message:", messageData); - delete messageData.admin; delete messageData.ip; delete messageData.time; @@ -90,6 +97,25 @@ function createChatServer(storage) { } }); }); + + ws.on('close', () => { + ws.isAlive = false; + }); + }); + + /** + * We will not always be receiving data, so some proxies may terminate the connection, this prevents it. + */ + const interval = setInterval(() => { + chatWss.clients.forEach((ws) => { + if (ws.isAlive === false) return ws.terminate(); + ws.isAlive = false; + ws.ping(); + }); + }, 30000); + + chatWss.on('close', () => { + clearInterval(interval); }); return chatWss; diff --git a/server/helpers.js b/server/helpers.js index 8c11b7f..681686a 100644 --- a/server/helpers.js +++ b/server/helpers.js @@ -250,12 +250,18 @@ function checkLatency(host, port = 80, timeout = 2000) { }); } -function antispamProtection(message, clientIp, ws, userCommands, lastWarn, userCommandHistory, lengthCommands, endpointName) { - const command = message.toString(); +function antispamProtection(message, clientIp, ws, userCommands, lastWarn, userCommandHistory, lengthCommands, endpointName, maxPayloadSize = 1024 * 1024) { + const rawCommand = message.toString(); + const command = rawCommand.replace(/[\r\n]+/g, ''); const now = Date.now(); const normalizedClientIp = clientIp?.replace(/^::ffff:/, ''); if (endpointName === 'text') consoleCmd.logDebug(`Command received from \x1b[90m${clientIp}\x1b[0m: ${command}`); + if (command.length > maxPayloadSize) { + consoleCmd.logWarn(`Command from \x1b[90m${normalizedClientIp}\x1b[0m on \x1b[90m/${endpointName}\x1b[0m exceeded maximum payload size (${parseInt(command.length / 1024)} KB / ${parseInt(maxPayloadSize / 1024)} KB).`); + return ""; + } + // Initialize user command history if not present if (!userCommandHistory[clientIp]) userCommandHistory[clientIp] = []; diff --git a/server/index.js b/server/index.js index 734c3f2..fd4a6df 100644 --- a/server/index.js +++ b/server/index.js @@ -356,7 +356,7 @@ wss.on('connection', (ws, request) => { let lastWarn = { time: 0 }; ws.on('message', (message) => { - const command = helpers.antispamProtection(message, clientIp, ws, userCommands, lastWarn, userCommandHistory, '18', 'text'); + const command = helpers.antispamProtection(message, clientIp, ws, userCommands, lastWarn, userCommandHistory, '18', 'text', 16 * 1024); if (!clientIp.includes("127.0.0.1")) { if (((command.startsWith('X') || command.startsWith('Y')) && !request.session.isAdminAuthenticated) || diff --git a/web/favicon.png b/web/favicon.png deleted file mode 100644 index 0e104c3..0000000 Binary files a/web/favicon.png and /dev/null differ diff --git a/web/favicon.svg b/web/favicon.svg new file mode 100644 index 0000000..71c67a0 --- /dev/null +++ b/web/favicon.svg @@ -0,0 +1,24 @@ + \ No newline at end of file diff --git a/web/index.ejs b/web/index.ejs index 14d0957..4a00c2e 100644 --- a/web/index.ejs +++ b/web/index.ejs @@ -15,7 +15,7 @@ - + diff --git a/web/js/settings.js b/web/js/settings.js index c647ba9..382e522 100644 --- a/web/js/settings.js +++ b/web/js/settings.js @@ -75,6 +75,27 @@ function getQueryParameter(name) { return urlParams.get(name); } +function updateFavicon(color) { + function rgbToHex(rgb) { + const result = rgb.match(/\d+/g); + return "#" + result.slice(0, 3).map(x =>(+x).toString(16).padStart(2, "0")).join(""); + } + + const hex = rgbToHex(color); + + const svg = ``; + + const base64 = btoa(svg); + + $('#favicon').attr( + 'href', + `data:image/svg+xml;base64,${base64}` + ); +} + function setTheme(themeName) { const themeColors = themes[themeName]; if (themeColors) { @@ -94,6 +115,7 @@ function setTheme(themeName) { $(':root').css('--color-text', themeColors[2]); $(':root').css('--color-text-2', textColor2); $('.wrapper-outer').css('background-color', backgroundColorWithOpacity); + updateFavicon(themeColors[1]); } } diff --git a/web/login.ejs b/web/login.ejs index edccb84..2a34086 100644 --- a/web/login.ejs +++ b/web/login.ejs @@ -8,7 +8,7 @@ - +
@@ -16,7 +16,7 @@You are currently not logged in as an administrator and therefore can't change the settings.
Please login below.