mirror of
https://github.com/KubaPro010/fm-dx-webserver.git
synced 2026-07-31 17:29:19 +02:00
Compare commits
3
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
72bbd9fcbd
|
||
|
|
fb6a1d00f7
|
||
|
|
f17e845c1d
|
Generated
+13
-37
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "fm-dx-webserver",
|
"name": "fm-dx-webserver",
|
||||||
"version": "1.4.0",
|
"version": "1.4.0a",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "fm-dx-webserver",
|
"name": "fm-dx-webserver",
|
||||||
"version": "1.4.0",
|
"version": "1.4.0a",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@mapbox/node-pre-gyp": "2.0.3",
|
"@mapbox/node-pre-gyp": "2.0.3",
|
||||||
@@ -17,7 +17,6 @@
|
|||||||
"ffmpeg-static": "5.3.0",
|
"ffmpeg-static": "5.3.0",
|
||||||
"figlet": "^1.10.0",
|
"figlet": "^1.10.0",
|
||||||
"http": "0.0.1-security",
|
"http": "0.0.1-security",
|
||||||
"koffi": "2.7.2",
|
|
||||||
"net": "1.0.2",
|
"net": "1.0.2",
|
||||||
"serialport": "13.0.0",
|
"serialport": "13.0.0",
|
||||||
"ws": "8.19.0"
|
"ws": "8.19.0"
|
||||||
@@ -1168,13 +1167,6 @@
|
|||||||
"node": ">=10"
|
"node": ">=10"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/koffi": {
|
|
||||||
"version": "2.7.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/koffi/-/koffi-2.7.2.tgz",
|
|
||||||
"integrity": "sha512-AWcsEKETQuELxK0Wq/aXDkDiNFFY41TxZQSrKm2Nd6HO/KTHeohPOOIlh2OfQnBXJbRjx5etpWt8cbqMUZo2sg==",
|
|
||||||
"hasInstallScript": true,
|
|
||||||
"license": "MIT"
|
|
||||||
},
|
|
||||||
"node_modules/lru-cache": {
|
"node_modules/lru-cache": {
|
||||||
"version": "6.0.0",
|
"version": "6.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
|
||||||
@@ -1238,9 +1230,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/minimatch": {
|
"node_modules/minimatch": {
|
||||||
"version": "10.2.2",
|
"version": "10.2.4",
|
||||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.2.tgz",
|
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.4.tgz",
|
||||||
"integrity": "sha512-+G4CpNBxa5MprY+04MbgOw1v7So6n5JY166pFi9KfYwT78fxScCeSNQSNzp6dpPSW2rONOps6Ocam1wFhCgoVw==",
|
"integrity": "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==",
|
||||||
"license": "BlueOak-1.0.0",
|
"license": "BlueOak-1.0.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"brace-expansion": "^5.0.2"
|
"brace-expansion": "^5.0.2"
|
||||||
@@ -1262,9 +1254,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/minizlib": {
|
"node_modules/minizlib": {
|
||||||
"version": "3.0.2",
|
"version": "3.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.1.0.tgz",
|
||||||
"integrity": "sha512-oG62iEk+CYt5Xj2YqI5Xi9xWUeZhDI8jjQmC5oThVH5JGCTgIjr7ciJDzC7MBzYd//WvR1OTmP5Q38Q8ShQtVA==",
|
"integrity": "sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"minipass": "^7.1.2"
|
"minipass": "^7.1.2"
|
||||||
@@ -1273,21 +1265,6 @@
|
|||||||
"node": ">= 18"
|
"node": ">= 18"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/mkdirp": {
|
|
||||||
"version": "3.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz",
|
|
||||||
"integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==",
|
|
||||||
"license": "MIT",
|
|
||||||
"bin": {
|
|
||||||
"mkdirp": "dist/cjs/src/bin.js"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=10"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/sponsors/isaacs"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/ms": {
|
"node_modules/ms": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
||||||
@@ -1794,16 +1771,15 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/tar": {
|
"node_modules/tar": {
|
||||||
"version": "7.4.3",
|
"version": "7.5.12",
|
||||||
"resolved": "https://registry.npmjs.org/tar/-/tar-7.4.3.tgz",
|
"resolved": "https://registry.npmjs.org/tar/-/tar-7.5.12.tgz",
|
||||||
"integrity": "sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw==",
|
"integrity": "sha512-9TsuLcdhOn4XztcQqhNyq1KOwOOED/3k58JAvtULiYqbO8B/0IBAAIE1hj0Svmm58k27TmcigyDI0deMlgG3uw==",
|
||||||
"license": "ISC",
|
"license": "BlueOak-1.0.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@isaacs/fs-minipass": "^4.0.0",
|
"@isaacs/fs-minipass": "^4.0.0",
|
||||||
"chownr": "^3.0.0",
|
"chownr": "^3.0.0",
|
||||||
"minipass": "^7.1.2",
|
"minipass": "^7.1.2",
|
||||||
"minizlib": "^3.0.1",
|
"minizlib": "^3.1.0",
|
||||||
"mkdirp": "^3.0.1",
|
|
||||||
"yallist": "^5.0.0"
|
"yallist": "^5.0.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
|
|||||||
+1
-1
@@ -16,7 +16,7 @@ function createChatServer(storage) {
|
|||||||
ws.isAlive = true;
|
ws.isAlive = true;
|
||||||
ws.on('pong', heartbeat);
|
ws.on('pong', heartbeat);
|
||||||
|
|
||||||
const clientIp = request.headers['x-forwarded-for'] || request.socket.remoteAddress;
|
const clientIp = helpers.getIpAddress(request);
|
||||||
const userCommandHistory = {};
|
const userCommandHistory = {};
|
||||||
|
|
||||||
if (serverConfig.webserver.banlist?.includes(clientIp)) {
|
if (serverConfig.webserver.banlist?.includes(clientIp)) {
|
||||||
|
|||||||
+3
-1
@@ -21,6 +21,7 @@ const MESSAGE_PREFIX = {
|
|||||||
FFMPEG: "\x1b[36m[FFMPEG]\x1b[0m",
|
FFMPEG: "\x1b[36m[FFMPEG]\x1b[0m",
|
||||||
INFO: "\x1b[32m[INFO]\x1b[0m",
|
INFO: "\x1b[32m[INFO]\x1b[0m",
|
||||||
WARN: "\x1b[33m[WARN]\x1b[0m",
|
WARN: "\x1b[33m[WARN]\x1b[0m",
|
||||||
|
SECURITY: "\x1b[36m[SECURITY]\x1b[0m",
|
||||||
};
|
};
|
||||||
|
|
||||||
const getCurrentTime = () => {
|
const getCurrentTime = () => {
|
||||||
@@ -44,6 +45,7 @@ const logMessage = (type, messages) => {
|
|||||||
if(type !== 'FFMPEG') appendLogToBuffer(logMessage);
|
if(type !== 'FFMPEG') appendLogToBuffer(logMessage);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const logSecurity = (...messages) => logMessage('SECURITY', messages);
|
||||||
const logDebug = (...messages) => logMessage('DEBUG', messages);
|
const logDebug = (...messages) => logMessage('DEBUG', messages);
|
||||||
const logChat = (message) => logMessage('CHAT', [`${message.nickname} (${message.ip}) sent a chat message: ${message.message}`]);
|
const logChat = (message) => logMessage('CHAT', [`${message.nickname} (${message.ip}) sent a chat message: ${message.message}`]);
|
||||||
const logError = (...messages) => logMessage('ERROR', messages);
|
const logError = (...messages) => logMessage('ERROR', messages);
|
||||||
@@ -87,4 +89,4 @@ process.on('exit', flushLogBuffer);
|
|||||||
process.on('SIGINT', gracefulExit);
|
process.on('SIGINT', gracefulExit);
|
||||||
process.on('SIGTERM', gracefulExit);
|
process.on('SIGTERM', gracefulExit);
|
||||||
|
|
||||||
module.exports = { logError, logDebug, logFfmpeg, logInfo, logWarn, logs, logChat };
|
module.exports = { logError, logDebug, logFfmpeg, logInfo, logWarn, logs, logChat, logSecurity };
|
||||||
@@ -106,8 +106,8 @@ function handleData(wss, receivedData, rdsWss) {
|
|||||||
case receivedLine.startsWith('T'): // Frequency
|
case receivedLine.startsWith('T'): // Frequency
|
||||||
modifiedData = receivedLine.substring(1).split(",")[0];
|
modifiedData = receivedLine.substring(1).split(",")[0];
|
||||||
|
|
||||||
rdsReset();
|
|
||||||
if((modifiedData / 1000).toFixed(3) == dataToSend.freq) return; // Prevent tune spamming using scrollwheel
|
if((modifiedData / 1000).toFixed(3) == dataToSend.freq) return; // Prevent tune spamming using scrollwheel
|
||||||
|
rdsReset();
|
||||||
|
|
||||||
parsedValue = parseFloat(modifiedData);
|
parsedValue = parseFloat(modifiedData);
|
||||||
|
|
||||||
@@ -202,6 +202,11 @@ function handleData(wss, receivedData, rdsWss) {
|
|||||||
rdsdec.decodeGroup(parseInt(a, 16), parseInt(b, 16), parseInt(c, 16), parseInt(d, 16), errors);
|
rdsdec.decodeGroup(parseInt(a, 16), parseInt(b, 16), parseInt(c, 16), parseInt(d, 16), errors);
|
||||||
legacyRdsPiBuffer = null;
|
legacyRdsPiBuffer = null;
|
||||||
break;
|
break;
|
||||||
|
case receivedLine.startsWith("!"):
|
||||||
|
wss.clients.forEach((client) => {
|
||||||
|
client.send(receivedLine.trim());
|
||||||
|
});
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+3
-3
@@ -19,10 +19,10 @@ const allPluginConfigs = require('./plugins');
|
|||||||
|
|
||||||
// Endpoints
|
// Endpoints
|
||||||
router.get('/', (req, res) => {
|
router.get('/', (req, res) => {
|
||||||
let requestIp = req.headers['x-forwarded-for'] || req.socket.remoteAddress;
|
let requestIp = helpers.getIpAddress(req);
|
||||||
|
|
||||||
const normalizedIp = requestIp?.replace(/^::ffff:/, '');
|
const normalizedIp = requestIp?.replace(/^::ffff:/, '');
|
||||||
const ipList = (normalizedIp || '').split(',').map(ip => ip.trim()).filter(Boolean); // in case there are multiple IPs (proxy), we need to check all of them
|
const ipList = (normalizedIp || '').split(',').map(ip => ip.trim()).filter(Boolean); // in case there are multiple IPs (proxy), we need to check all of them (No we don't)
|
||||||
|
|
||||||
const banEntry = serverConfig.webserver.banlist.find(banEntry => ipList.includes(banEntry[0]));
|
const banEntry = serverConfig.webserver.banlist.find(banEntry => ipList.includes(banEntry[0]));
|
||||||
|
|
||||||
@@ -385,7 +385,7 @@ router.get('/log_fmlist', (req, res) => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const clientIp = req.headers['x-forwarded-for'] || req.connection.remoteAddress;
|
const clientIp = helpers.getIpAddress(req);
|
||||||
const txId = dataHandler.dataToSend.txInfo.id; // Extract the ID
|
const txId = dataHandler.dataToSend.txInfo.id; // Extract the ID
|
||||||
|
|
||||||
if (!canLog(txId)) {
|
if (!canLog(txId)) {
|
||||||
|
|||||||
+30
-1
@@ -343,10 +343,39 @@ function findServerFiles(plugins) {
|
|||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function normalizeIp(ip) {
|
||||||
|
if(ip && ip.startsWith('::ffff:')) return ip.substring(7);
|
||||||
|
return ip;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isLocalhost(ip) {
|
||||||
|
const normalized = normalizeIp(ip);
|
||||||
|
return normalized === '127.0.0.1' || normalized === '::1';
|
||||||
|
}
|
||||||
|
|
||||||
|
function isTrustedProxy(ip) {
|
||||||
|
return serverConfig.trustedProxies.includes(normalizeIp(ip));
|
||||||
|
}
|
||||||
|
|
||||||
|
function getIpAddress(request) {
|
||||||
|
const remoteIpRaw = request.socket.remoteAddress;
|
||||||
|
const remoteIp = normalizeIp(remoteIpRaw);
|
||||||
|
const xff = request.headers['x-forwarded-for'];
|
||||||
|
|
||||||
|
if (xff && !isLocalhost(remoteIp) && !isTrustedProxy(remoteIp)) {
|
||||||
|
consoleCmd.logSecurity(`Untrusted proxy tried to set X-Forwarded-For: ${xff} (remote: ${remoteIpRaw})`);
|
||||||
|
return remoteIp;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (xff) return normalizeIp(xff.split(',')[0].trim());
|
||||||
|
|
||||||
|
return remoteIp;
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
authenticateWithXdrd, parseMarkdown, handleConnect,
|
authenticateWithXdrd, parseMarkdown, handleConnect,
|
||||||
removeMarkdown, formatUptime, resolveDataBuffer,
|
removeMarkdown, formatUptime, resolveDataBuffer,
|
||||||
kickClient, checkIPv6Support, checkLatency,
|
kickClient, checkIPv6Support, checkLatency,
|
||||||
antispamProtection, escapeHtml, findServerFiles,
|
antispamProtection, escapeHtml, findServerFiles,
|
||||||
startPluginsWithDelay
|
startPluginsWithDelay, getIpAddress
|
||||||
}
|
}
|
||||||
+3
-3
@@ -301,7 +301,7 @@ setInterval(() => {
|
|||||||
|
|
||||||
wss.on('connection', (ws, request) => {
|
wss.on('connection', (ws, request) => {
|
||||||
const output = serverConfig.xdrd.wirelessConnection ? client : serialport;
|
const output = serverConfig.xdrd.wirelessConnection ? client : serialport;
|
||||||
let clientIp = request.headers['x-forwarded-for'] || request.socket.remoteAddress;
|
let clientIp = helpers.getIpAddress(request);
|
||||||
const userCommandHistory = {};
|
const userCommandHistory = {};
|
||||||
const normalizedClientIp = clientIp?.replace(/^::ffff:/, '');
|
const normalizedClientIp = clientIp?.replace(/^::ffff:/, '');
|
||||||
|
|
||||||
@@ -486,7 +486,7 @@ wss.on('connection', (ws, request) => {
|
|||||||
|
|
||||||
// Additional web socket for using plugins
|
// Additional web socket for using plugins
|
||||||
pluginsWss.on('connection', (ws, request) => {
|
pluginsWss.on('connection', (ws, request) => {
|
||||||
const clientIp = request.headers['x-forwarded-for'] || request.socket.remoteAddress;
|
const clientIp = helpers.getIpAddress(request);
|
||||||
const userCommandHistory = {};
|
const userCommandHistory = {};
|
||||||
if (serverConfig.webserver.banlist?.includes(clientIp)) {
|
if (serverConfig.webserver.banlist?.includes(clientIp)) {
|
||||||
ws.close(1008, 'Banned IP');
|
ws.close(1008, 'Banned IP');
|
||||||
@@ -524,7 +524,7 @@ pluginsWss.on('connection', (ws, request) => {
|
|||||||
|
|
||||||
// Websocket register for /text, /audio and /chat paths
|
// Websocket register for /text, /audio and /chat paths
|
||||||
httpServer.on('upgrade', (request, socket, head) => {
|
httpServer.on('upgrade', (request, socket, head) => {
|
||||||
const clientIp = request.headers['x-forwarded-for'] || request.socket.remoteAddress;
|
const clientIp = helpers.getIpAddress(request);
|
||||||
if (serverConfig.webserver.banlist?.includes(clientIp)) {
|
if (serverConfig.webserver.banlist?.includes(clientIp)) {
|
||||||
socket.destroy();
|
socket.destroy();
|
||||||
return;
|
return;
|
||||||
|
|||||||
+12
-4
@@ -232,12 +232,20 @@ class RDSDecoder {
|
|||||||
|
|
||||||
if(d_error > 2) return; // Don't risk it
|
if(d_error > 2) return; // Don't risk it
|
||||||
|
|
||||||
|
|
||||||
const idx = blockB & 0x3;
|
const idx = blockB & 0x3;
|
||||||
|
|
||||||
|
const last_err = this.ps_errors[idx * 2];
|
||||||
|
const cur_err = Math.ceil(d_error * (10/3));
|
||||||
|
const character_a = decode_charset(blockD >> 8);
|
||||||
|
const character_b = decode_charset(blockD & 0xFF);
|
||||||
|
|
||||||
this.ps[idx * 2] = decode_charset(blockD >> 8);
|
if(last_err < cur_err && this.ps[idx * 2] == character_a && this.ps[idx * 2 + 1] == character_b) return;
|
||||||
this.ps[idx * 2 + 1] = decode_charset(blockD & 0xFF);
|
|
||||||
this.ps_errors[idx * 2] = Math.ceil(d_error * (10/3));
|
this.ps[idx * 2] = character_a;
|
||||||
this.ps_errors[idx * 2 + 1] = Math.ceil(d_error * (10/3));
|
this.ps[idx * 2 + 1] = character_b;
|
||||||
|
this.ps_errors[idx * 2] = cur_err;
|
||||||
|
this.ps_errors[idx * 2 + 1] = cur_err;
|
||||||
|
|
||||||
this.data.ps = this.ps.join('');
|
this.data.ps = this.ps.join('');
|
||||||
this.data.ps_errors = this.ps_errors.join(',');
|
this.data.ps_errors = this.ps_errors.join(',');
|
||||||
|
|||||||
@@ -105,7 +105,7 @@ var countries = [
|
|||||||
"Turkey",
|
"Turkey",
|
||||||
"Palestine",
|
"Palestine",
|
||||||
"Turkmenistan",
|
"Turkmenistan",
|
||||||
"Poland",
|
"Rzeczpospolita Polska",
|
||||||
"Uganda",
|
"Uganda",
|
||||||
"Portugal",
|
"Portugal",
|
||||||
"Ukraine",
|
"Ukraine",
|
||||||
@@ -539,7 +539,7 @@ const rdsEccE0E5Lut = [
|
|||||||
],
|
],
|
||||||
// E2
|
// E2
|
||||||
[
|
[
|
||||||
"Morocco", "Czechia", "Poland", "Vatican", "Slovakia",
|
"Morocco", "Czechia", "Rzeczpospolita Polska", "Vatican", "Slovakia",
|
||||||
"Syria", "Tunisia", "", "Liechtenstein", "Iceland",
|
"Syria", "Tunisia", "", "Liechtenstein", "Iceland",
|
||||||
"Monaco", "Lithuania", "Serbia", "Spain", "Norway"
|
"Monaco", "Lithuania", "Serbia", "Spain", "Norway"
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -126,7 +126,8 @@ let serverConfig = {
|
|||||||
stereoNoUsers: "0",
|
stereoNoUsers: "0",
|
||||||
antennaStartup: "0",
|
antennaStartup: "0",
|
||||||
antennaNoUsers: "0",
|
antennaNoUsers: "0",
|
||||||
antennaNoUsersDelay: false
|
antennaNoUsersDelay: false,
|
||||||
|
trustedProxies: []
|
||||||
};
|
};
|
||||||
|
|
||||||
// Function to add missing fields without overwriting existing values
|
// Function to add missing fields without overwriting existing values
|
||||||
|
|||||||
+2
-1
@@ -1,11 +1,12 @@
|
|||||||
const WebSocket = require('ws');
|
const WebSocket = require('ws');
|
||||||
const { serverConfig } = require('../server_config');
|
const { serverConfig } = require('../server_config');
|
||||||
const audio_pipe = require('./index.js');
|
const audio_pipe = require('./index.js');
|
||||||
|
const { getIpAddress } = require("../helpers.js")
|
||||||
|
|
||||||
const audioWss = new WebSocket.Server({ noServer: true, skipUTF8Validation: true });
|
const audioWss = new WebSocket.Server({ noServer: true, skipUTF8Validation: true });
|
||||||
|
|
||||||
audioWss.on('connection', (ws, request) => {
|
audioWss.on('connection', (ws, request) => {
|
||||||
const clientIp = request.headers['x-forwarded-for'] || request.socket.remoteAddress;
|
const clientIp = getIpAddress(request);
|
||||||
|
|
||||||
if (serverConfig.webserver.banlist?.includes(clientIp)) {
|
if (serverConfig.webserver.banlist?.includes(clientIp)) {
|
||||||
ws.close(1008, 'Banned IP');
|
ws.close(1008, 'Banned IP');
|
||||||
|
|||||||
+6
-4
@@ -26,7 +26,6 @@ const WebSocket = require('ws');
|
|||||||
// Get weighting values based on algorithm setting.
|
// Get weighting values based on algorithm setting.
|
||||||
// Defaults = algorithm 1
|
// Defaults = algorithm 1
|
||||||
let weightedErp = 10;
|
let weightedErp = 10;
|
||||||
let weightedDist = 400;
|
|
||||||
const algorithms = [
|
const algorithms = [
|
||||||
[10, 400],
|
[10, 400],
|
||||||
[30, 500],
|
[30, 500],
|
||||||
@@ -268,7 +267,11 @@ async function fetchTx(freq, piCode, rdsPs) {
|
|||||||
if (filteredLocations.length > 1) {
|
if (filteredLocations.length > 1) {
|
||||||
const extraFilteredLocations = filteredLocations.map(locData => ({
|
const extraFilteredLocations = filteredLocations.map(locData => ({
|
||||||
...locData,
|
...locData,
|
||||||
stations: locData.stations.filter(station => (station.ps?.toLowerCase() === rdsPs.replace(/ /g, '_').toLowerCase()))
|
stations: locData.stations.filter(station =>
|
||||||
|
station.ps?.toLowerCase().includes(
|
||||||
|
rdsPs.replace(/ /g, '_').toLowerCase()
|
||||||
|
) ?? false
|
||||||
|
)
|
||||||
})).filter(locData => locData.stations.length > 0);
|
})).filter(locData => locData.stations.length > 0);
|
||||||
|
|
||||||
if (extraFilteredLocations.length > 0) filteredLocations = extraFilteredLocations;
|
if (extraFilteredLocations.length > 0) filteredLocations = extraFilteredLocations;
|
||||||
@@ -370,8 +373,7 @@ function haversine(lat1, lon1, lat2, lon2) {
|
|||||||
|
|
||||||
const y = Math.sin(dLon) * Math.cos(deg2rad(lat2));
|
const y = Math.sin(dLon) * Math.cos(deg2rad(lat2));
|
||||||
const x = Math.cos(deg2rad(lat1)) * Math.sin(deg2rad(lat2)) - Math.sin(deg2rad(lat1)) * Math.cos(deg2rad(lat2)) * Math.cos(dLon);
|
const x = Math.cos(deg2rad(lat1)) * Math.sin(deg2rad(lat2)) - Math.sin(deg2rad(lat1)) * Math.cos(deg2rad(lat2)) * Math.cos(dLon);
|
||||||
const azimuth = Math.atan2(y, x);
|
const azimuthDegrees = (Math.atan2(y, x) * 180 / Math.PI + 360) % 360;
|
||||||
const azimuthDegrees = (azimuth * 180 / Math.PI + 360) % 360;
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
distanceKm: distance,
|
distanceKm: distance,
|
||||||
|
|||||||
+1
-1
@@ -21,7 +21,7 @@
|
|||||||
<br>
|
<br>
|
||||||
<i class="text-big fa-solid fa-exclamation-triangle color-4"></i>
|
<i class="text-big fa-solid fa-exclamation-triangle color-4"></i>
|
||||||
<p>
|
<p>
|
||||||
There's a possibility you were kicked by the system.<br>
|
The service has rejected you.<br>
|
||||||
Please try again later.</p>
|
Please try again later.</p>
|
||||||
|
|
||||||
<% if (reason) { %>
|
<% if (reason) { %>
|
||||||
|
|||||||
+1
-1
@@ -562,7 +562,7 @@
|
|||||||
<div class="hover-brighten br-0 bg-color-1" style="height: 50px;padding:12px;" onclick="window.open('https://buymeacoffee.com/noobish')">
|
<div class="hover-brighten br-0 bg-color-1" style="height: 50px;padding:12px;" onclick="window.open('https://buymeacoffee.com/noobish')">
|
||||||
<strong>Support</strong> the developer!
|
<strong>Support</strong> the developer!
|
||||||
</div>
|
</div>
|
||||||
<div class="hover-brighten br-0 bg-color-1" style="height: 50px;padding:12px;" onclick="window.open('https://discord.com/invite/ZAVNdS74mC')">
|
<div class="hover-brighten br-0 bg-color-1" style="height: 50px;padding:12px;" onclick="window.open('https://discord.gg/cY66nt6UhV')">
|
||||||
Join our <strong>FMDX.org Discord</strong> community!
|
Join our <strong>FMDX.org Discord</strong> community!
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
+2
-2
@@ -418,9 +418,9 @@ function handleWebSocketMessage(event) {
|
|||||||
console.log('Kick initiated.')
|
console.log('Kick initiated.')
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
window.location.href = '/403';
|
window.location.href = '/403';
|
||||||
}, 500);
|
}, 350);
|
||||||
return;
|
return;
|
||||||
}
|
} else if (event.data.startsWith("!")) sendToast('info', 'Info from server', event.data.slice(1), false, false)
|
||||||
|
|
||||||
parsedData = JSON.parse(event.data);
|
parsedData = JSON.parse(event.data);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user