From 71c90119aff058603843829907faea66326938a1 Mon Sep 17 00:00:00 2001 From: Amateur Audio Dude <168192910+AmateurAudioDude@users.noreply.github.com> Date: Sun, 20 Apr 2025 21:17:06 +1000 Subject: [PATCH 1/6] add rt_flag to api --- server/datahandler.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/server/datahandler.js b/server/datahandler.js index eac6735..2d02494 100644 --- a/server/datahandler.js +++ b/server/datahandler.js @@ -139,6 +139,7 @@ const callbacks = { dataToSend.rt1 = decode_unicode(rt); dataToSend.rt1_errors = decode_errors(rt); } + dataToSend.rt_flag = flag; }, 'callback_rt*'), ptyn: koffi.register((rds, flag) => ( @@ -222,6 +223,7 @@ var dataToSend = { af: [], rt0: '', rt1: '', + rt_flag: '', ims: 0, eq: 0, ant: 0, From fbcdda29412927ab80303a6cfe647112f3df928d Mon Sep 17 00:00:00 2001 From: Amateur Audio Dude <168192910+AmateurAudioDude@users.noreply.github.com> Date: Sun, 20 Apr 2025 21:19:07 +1000 Subject: [PATCH 2/6] reset ps_errors api on frequency change --- server/datahandler.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/server/datahandler.js b/server/datahandler.js index 2d02494..894e153 100644 --- a/server/datahandler.js +++ b/server/datahandler.js @@ -466,7 +466,13 @@ function showOnlineUsers(currentUsers) { initialData.users = currentUsers; } +let prevFreq = initialData.freq || '87.500'; function processSignal(receivedData, st, stForced) { + if (initialData.freq !== prevFreq) { + prevFreq = initialData.freq; + dataToSend.ps_errors = ''; + } + const modifiedData = receivedData.substring(2); const parsedValue = parseFloat(modifiedData); dataToSend.st = st; From b23211f408dbefb8d269471b1effba5ddb52fa04 Mon Sep 17 00:00:00 2001 From: Amateur Audio Dude <168192910+AmateurAudioDude@users.noreply.github.com> Date: Sun, 20 Apr 2025 21:20:31 +1000 Subject: [PATCH 3/6] optimised serialport code using export --- server/datahandler.js | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/server/datahandler.js b/server/datahandler.js index 894e153..9e320ab 100644 --- a/server/datahandler.js +++ b/server/datahandler.js @@ -433,15 +433,19 @@ function handleData(wss, receivedData, rdsWss) { } // Serialport retry code when port is open but communication is lost (additional code in index.js) -isSerialportAlive = true; -lastFrequencyAlive = '87.500'; +let state = { + isSerialportAlive: true, + isSerialportRetrying: false, + lastFrequencyAlive: '87.500' +}; + setInterval(() => { - lastFrequencyAlive = initialData.freq; + state.lastFrequencyAlive = initialData.freq; const serialportElapsedTime = process.hrtime(serialportUpdateTime)[0]; // Activate serialport retry if handleData has not been executed for over 8 seconds - if (checkSerialport && (serialportElapsedTime > 8) && !isSerialportRetrying && serverConfig.xdrd.wirelessConnection === false) { - isSerialportAlive = false; - isSerialportRetrying = true; + if (checkSerialport && (serialportElapsedTime > 8) && !state.isSerialportRetrying && serverConfig.xdrd.wirelessConnection === false) { + state.isSerialportAlive = false; + state.isSerialportRetrying = true; } }, 2000); @@ -498,5 +502,5 @@ function processSignal(receivedData, st, stForced) { } module.exports = { - handleData, showOnlineUsers, dataToSend, initialData, resetToDefault + handleData, showOnlineUsers, dataToSend, initialData, resetToDefault, state }; From 4c71b736a6ef08b78da79688e88782e6f73e2cc6 Mon Sep 17 00:00:00 2001 From: Amateur Audio Dude <168192910+AmateurAudioDude@users.noreply.github.com> Date: Sun, 20 Apr 2025 21:23:18 +1000 Subject: [PATCH 4/6] optimised serialport code --- server/index.js | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/server/index.js b/server/index.js index 16ac695..f58ee2f 100644 --- a/server/index.js +++ b/server/index.js @@ -18,7 +18,7 @@ const path = require('path'); const net = require('net'); const client = new net.Socket(); const { SerialPort } = require('serialport'); -const tunnel = require('./tunnel') +const tunnel = require('./tunnel'); // File imports const helpers = require('./helpers'); @@ -118,12 +118,12 @@ connectToSerial(); tunnel.connect(); // Serialport retry code when port is open but communication is lost (additional code in datahandler.js) -isSerialportRetrying = false; +dataHandler.state.isSerialportRetrying = false; setInterval(() => { - if (!isSerialportAlive && serverConfig.xdrd.wirelessConnection === false) { - isSerialportAlive = true; - isSerialportRetrying = true; + if (!dataHandler.state.isSerialportAlive && serverConfig.xdrd.wirelessConnection === false) { + dataHandler.state.isSerialportAlive = true; + dataHandler.state.isSerialportRetrying = true; if (serialport && serialport.isOpen) { logWarn('Communication lost from ' + serverConfig.xdrd.comPort + ', force closing serialport.'); setTimeout(() => { @@ -163,7 +163,7 @@ if (serverConfig.xdrd.wirelessConnection === false) { } logInfo('Using COM device: ' + serverConfig.xdrd.comPort); - isSerialportAlive = true; + dataHandler.state.isSerialportAlive = true; setTimeout(() => { serialport.write('x\n'); }, 3000); @@ -177,12 +177,12 @@ if (serverConfig.xdrd.wirelessConnection === false) { serialport.write('T' + Math.round(serverConfig.defaultFreq * 1000) + '\n'); dataHandler.initialData.freq = Number(serverConfig.defaultFreq).toFixed(3); dataHandler.dataToSend.freq = Number(serverConfig.defaultFreq).toFixed(3); - } else if (lastFrequencyAlive && isSerialportRetrying) { // Serialport retry code when port is open but communication is lost - serialport.write('T' + (lastFrequencyAlive * 1000) + '\n'); + } else if (dataHandler.state.lastFrequencyAlive && dataHandler.state.isSerialportRetrying) { // Serialport retry code when port is open but communication is lost + serialport.write('T' + (dataHandler.state.lastFrequencyAlive * 1000) + '\n'); } else { serialport.write('T87500\n'); } - isSerialportRetrying = false; + dataHandler.state.isSerialportRetrying = false; serialport.write('A0\n'); serialport.write('F-1\n'); @@ -207,7 +207,7 @@ if (serverConfig.xdrd.wirelessConnection === false) { serialport.on('close', () => { logWarn('Disconnected from ' + serverConfig.xdrd.comPort + '. Attempting to reconnect.'); setTimeout(() => { - isSerialportRetrying = true; + dataHandler.state.isSerialportRetrying = true; connectToSerial(); }, 5000); }); From ae1be13e8246a6e310480ca85d0d39105370dc8f Mon Sep 17 00:00:00 2001 From: Amateur Audio Dude <168192910+AmateurAudioDude@users.noreply.github.com> Date: Sun, 20 Apr 2025 21:24:29 +1000 Subject: [PATCH 5/6] exclude localhost from 'dangerous command' warning --- server/index.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/server/index.js b/server/index.js index f58ee2f..989def8 100644 --- a/server/index.js +++ b/server/index.js @@ -421,10 +421,12 @@ wss.on('connection', (ws, request) => { ws.on('message', (message) => { const command = helpers.antispamProtection(message, clientIp, ws, userCommands, lastWarn, userCommandHistory, '18', 'text'); - if (((command.startsWith('X') || command.startsWith('Y')) && !request.session.isAdminAuthenticated) || - ((command.startsWith('F') || command.startsWith('W')) && serverConfig.bwSwitch === false)) { - logWarn(`User \x1b[90m${clientIp}\x1b[0m attempted to send a potentially dangerous command. You may consider blocking this user.`); - return; + if (!clientIp.includes("127.0.0.1")) { + if (((command.startsWith('X') || command.startsWith('Y')) && !request.session.isAdminAuthenticated) || + ((command.startsWith('F') || command.startsWith('W')) && serverConfig.bwSwitch === false)) { + logWarn(`User \x1b[90m${clientIp}\x1b[0m attempted to send a potentially dangerous command: ${command.slice(0, 64)}.`); + return; + } } if (command.includes("\'")) { From 24c2c893ea418df212afcad6a0fb05d3a81434ea Mon Sep 17 00:00:00 2001 From: Amateur Audio Dude <168192910+AmateurAudioDude@users.noreply.github.com> Date: Sun, 20 Apr 2025 21:25:58 +1000 Subject: [PATCH 6/6] fix EADDRINUSE (if caused by webpage on launch) --- server/index.js | 31 +++++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/server/index.js b/server/index.js index 989def8..6fd0f3a 100644 --- a/server/index.js +++ b/server/index.js @@ -682,6 +682,26 @@ pluginsWss.on('connection', (ws, request) => { }); }); +function isPortOpen(host, port, timeout = 1000) { + return new Promise((resolve) => { + const socket = new net.Socket(); + + const onError = () => { + socket.destroy(); + resolve(false); + }; + + socket.setTimeout(timeout); + socket.once('error', onError); + socket.once('timeout', onError); + + socket.connect(port, host, () => { + socket.end(); + resolve(true); + }); + }); +} + // Websocket register for /text, /audio and /chat paths httpServer.on('upgrade', (request, socket, head) => { if (request.url === '/text') { @@ -691,8 +711,15 @@ httpServer.on('upgrade', (request, socket, head) => { }); }); } else if (request.url === '/audio') { - proxy.ws(request, socket, head); - } else if (request.url === '/chat') { + isPortOpen('localhost', (Number(serverConfig.webserver.webserverPort) + 10)).then((open) => { + if (open) { + proxy.ws(request, socket, head); + } else { + logWarn(`Audio stream port ${(Number(serverConfig.webserver.webserverPort) + 10)} not yet open — skipping proxy connection.`); + socket.end(); // close socket so client isn't left hanging + } + }); +} else if (request.url === '/chat') { sessionMiddleware(request, {}, () => { chatWss.handleUpgrade(request, socket, head, (ws) => { chatWss.emit('connection', ws, request);