You've already forked fm-dx-webserver
mirror of
https://github.com/KubaPro010/fm-dx-webserver.git
synced 2026-02-26 22:13:53 +01:00
some changes again
This commit is contained in:
2
index.js
2
index.js
@@ -3,7 +3,7 @@ require('./server/index.js');
|
||||
/**
|
||||
* FM-DX Webserver
|
||||
*
|
||||
* Github repo: https://github.com/NoobishSVK/fm-dx-webserver
|
||||
* Github repo: https://github.com/KubaPro010/fm-dx-webserver
|
||||
* Server files: /server
|
||||
* Client files (web): /web
|
||||
* Plugin files: /plugins
|
||||
|
||||
@@ -63,17 +63,12 @@ function createChatServer(storage) {
|
||||
delete messageData.ip;
|
||||
delete messageData.time;
|
||||
|
||||
if (messageData.nickname != null) {
|
||||
messageData.nickname = helpers.escapeHtml(String(messageData.nickname));
|
||||
}
|
||||
if (messageData.nickname != null) messageData.nickname = helpers.escapeHtml(String(messageData.nickname));
|
||||
|
||||
messageData.ip = clientIp;
|
||||
|
||||
const now = new Date();
|
||||
messageData.time =
|
||||
String(now.getHours()).padStart(2, '0') +
|
||||
":" +
|
||||
String(now.getMinutes()).padStart(2, '0');
|
||||
messageData.time = String(now.getHours()).padStart(2, '0') + ":" + String(now.getMinutes()).padStart(2, '0');
|
||||
|
||||
if (serverConfig.webserver.banlist?.includes(clientIp)) return;
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
const RDSDecoder = require("./rds.js");
|
||||
const { serverConfig } = require('./server_config');
|
||||
|
||||
const { fetchTx } = require('./tx_search.js');
|
||||
const fetchTx = require('./tx_search.js');
|
||||
const updateInterval = 75;
|
||||
|
||||
// Initialize the data object
|
||||
|
||||
@@ -15,7 +15,7 @@ const tunerProfiles = require('./tuner_profiles');
|
||||
const { logInfo, logs } = require('./console');
|
||||
const dataHandler = require('./datahandler');
|
||||
const fmdxList = require('./fmdx_list');
|
||||
const { allPluginConfigs } = require('./plugins');
|
||||
const allPluginConfigs = require('./plugins');
|
||||
|
||||
// Endpoints
|
||||
router.get('/', (req, res) => {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
const fs = require('fs');
|
||||
const http = require('http');
|
||||
const https = require('https');
|
||||
const net = require('net');
|
||||
@@ -5,7 +6,7 @@ const crypto = require('crypto');
|
||||
const dataHandler = require('./datahandler');
|
||||
const storage = require('./storage');
|
||||
const consoleCmd = require('./console');
|
||||
const { serverConfig, configExists, configSave } = require('./server_config');
|
||||
const { serverConfig, configSave } = require('./server_config');
|
||||
|
||||
function parseMarkdown(parsed) {
|
||||
parsed = parsed.replace(/<\/?[^>]+(>|$)/g, '');
|
||||
@@ -93,9 +94,7 @@ let bannedASCache = { data: null, timestamp: 0 };
|
||||
|
||||
function fetchBannedAS(callback) {
|
||||
const now = Date.now();
|
||||
if (bannedASCache.data && now - bannedASCache.timestamp < 10 * 60 * 1000) {
|
||||
return callback(null, bannedASCache.data);
|
||||
}
|
||||
if (bannedASCache.data && now - bannedASCache.timestamp < 10 * 60 * 1000) return callback(null, bannedASCache.data);
|
||||
|
||||
const req = https.get("https://fmdx.org/banned_as.json", { family: 4 }, (banResponse) => {
|
||||
let banData = "";
|
||||
@@ -152,9 +151,7 @@ function processConnection(clientIp, locationInfo, currentUsers, ws, callback) {
|
||||
}
|
||||
|
||||
const userLocation =
|
||||
locationInfo.country === undefined
|
||||
? "Unknown"
|
||||
: `${locationInfo.city}, ${locationInfo.regionName}, ${locationInfo.countryCode}`;
|
||||
locationInfo.country === undefined ? "Unknown" : `${locationInfo.city}, ${locationInfo.regionName}, ${locationInfo.countryCode}`;
|
||||
|
||||
storage.connectedUsers.push({
|
||||
ip: clientIp,
|
||||
@@ -289,9 +286,7 @@ function antispamProtection(message, clientIp, ws, userCommands, lastWarn, userC
|
||||
lastMessageTime = now;
|
||||
|
||||
// Initialize command history for rate-limiting checks
|
||||
if (!userCommands[command]) {
|
||||
userCommands[command] = [];
|
||||
}
|
||||
if (!userCommands[command]) userCommands[command] = [];
|
||||
|
||||
// Record the current timestamp for this command
|
||||
userCommands[command].push(now);
|
||||
@@ -313,15 +308,45 @@ function antispamProtection(message, clientIp, ws, userCommands, lastWarn, userC
|
||||
}
|
||||
|
||||
const escapeHtml = (unsafe) => {
|
||||
return unsafe
|
||||
.replace(/&/g, "&")
|
||||
.replace(/</g, "<")
|
||||
.replace(/>/g, ">")
|
||||
.replace(/"/g, """)
|
||||
.replace(/'/g, "'");
|
||||
return unsafe.replace(/&/g, "&")
|
||||
.replace(/</g, "<").replace(/>/g, ">")
|
||||
.replace(/"/g, """).replace(/'/g, "'");
|
||||
};
|
||||
|
||||
// Start plugins with delay
|
||||
function startPluginsWithDelay(plugins, delay) {
|
||||
plugins.forEach((pluginPath, index) => {
|
||||
setTimeout(() => {
|
||||
const pluginName = path.basename(pluginPath, '.js'); // Extract plugin name from path
|
||||
logInfo(`-----------------------------------------------------------------`);
|
||||
logInfo(`Plugin ${pluginName} loaded successfully!`);
|
||||
require(pluginPath);
|
||||
}, delay * index);
|
||||
});
|
||||
|
||||
// Add final log line after all plugins are loaded
|
||||
setTimeout(() => {
|
||||
logInfo(`-----------------------------------------------------------------`);
|
||||
}, delay * plugins.length);
|
||||
}
|
||||
|
||||
// Function to find server files based on the plugins listed in config
|
||||
function findServerFiles(plugins) {
|
||||
let results = [];
|
||||
plugins.forEach(plugin => {
|
||||
// Remove .js extension if present
|
||||
if (plugin.endsWith('.js')) plugin = plugin.slice(0, -3);
|
||||
|
||||
const pluginPath = path.join(__dirname, '..', 'plugins', `${plugin}_server.js`);
|
||||
if (fs.existsSync(pluginPath) && fs.statSync(pluginPath).isFile()) results.push(pluginPath);
|
||||
});
|
||||
return results;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
authenticateWithXdrd, parseMarkdown, handleConnect, removeMarkdown, formatUptime, resolveDataBuffer, kickClient, checkIPv6Support, checkLatency, antispamProtection, escapeHtml
|
||||
authenticateWithXdrd, parseMarkdown, handleConnect,
|
||||
removeMarkdown, formatUptime, resolveDataBuffer,
|
||||
kickClient, checkIPv6Support, checkLatency,
|
||||
antispamProtection, escapeHtml, findServerFiles,
|
||||
startPluginsWithDelay
|
||||
}
|
||||
@@ -1,4 +1,3 @@
|
||||
// Library imports
|
||||
const express = require('express');
|
||||
const endpoints = require('./endpoints');
|
||||
const session = require('express-session');
|
||||
@@ -8,21 +7,16 @@ const readline = require('readline');
|
||||
const app = express();
|
||||
const httpServer = http.createServer(app);
|
||||
const WebSocket = require('ws');
|
||||
const wss = new WebSocket.Server({ noServer: true, perMessageDeflate: true });
|
||||
const rdsWss = new WebSocket.Server({ noServer: true });
|
||||
const pluginsWss = new WebSocket.Server({ noServer: true, perMessageDeflate: true });
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const net = require('net');
|
||||
const client = new net.Socket();
|
||||
const { SerialPort } = require('serialport');
|
||||
const tunnel = require('./tunnel');
|
||||
const { createChatServer } = require('./chat');
|
||||
const { createAudioServer } = require('./stream/ws.js');
|
||||
const figlet = require('figlet');
|
||||
|
||||
// File imports
|
||||
const helpers = require('./helpers');
|
||||
const { findServerFiles, startPluginsWithDelay } = helpers;
|
||||
|
||||
const dataHandler = require('./datahandler');
|
||||
const fmdxList = require('./fmdx_list');
|
||||
const { logError, logInfo, logWarn } = require('./console');
|
||||
@@ -31,35 +25,10 @@ const { serverConfig, configExists } = require('./server_config');
|
||||
const pluginsApi = require('./plugins_api');
|
||||
const pjson = require('../package.json');
|
||||
|
||||
// Function to find server files based on the plugins listed in config
|
||||
function findServerFiles(plugins) {
|
||||
let results = [];
|
||||
plugins.forEach(plugin => {
|
||||
// Remove .js extension if present
|
||||
if (plugin.endsWith('.js')) plugin = plugin.slice(0, -3);
|
||||
|
||||
const pluginPath = path.join(__dirname, '..', 'plugins', `${plugin}_server.js`);
|
||||
if (fs.existsSync(pluginPath) && fs.statSync(pluginPath).isFile()) results.push(pluginPath);
|
||||
});
|
||||
return results;
|
||||
}
|
||||
|
||||
// Start plugins with delay
|
||||
function startPluginsWithDelay(plugins, delay) {
|
||||
plugins.forEach((pluginPath, index) => {
|
||||
setTimeout(() => {
|
||||
const pluginName = path.basename(pluginPath, '.js'); // Extract plugin name from path
|
||||
logInfo(`-----------------------------------------------------------------`);
|
||||
logInfo(`Plugin ${pluginName} loaded successfully!`);
|
||||
require(pluginPath);
|
||||
}, delay * index);
|
||||
});
|
||||
|
||||
// Add final log line after all plugins are loaded
|
||||
setTimeout(() => {
|
||||
logInfo(`-----------------------------------------------------------------`);
|
||||
}, delay * plugins.length);
|
||||
}
|
||||
const client = new net.Socket();
|
||||
const wss = new WebSocket.Server({ noServer: true, perMessageDeflate: true });
|
||||
const rdsWss = new WebSocket.Server({ noServer: true });
|
||||
const pluginsWss = new WebSocket.Server({ noServer: true, perMessageDeflate: true });
|
||||
|
||||
// Get all plugins from config and find corresponding server files
|
||||
const plugins = findServerFiles(serverConfig.plugins);
|
||||
@@ -76,22 +45,12 @@ const terminalWidth = readline.createInterface({
|
||||
output: process.stdout
|
||||
}).output.columns;
|
||||
|
||||
|
||||
figlet("FM-DX Webserver", function (err, data) {
|
||||
if (err) {
|
||||
console.log("Something went wrong...");
|
||||
console.dir(err);
|
||||
return;
|
||||
}
|
||||
console.log('\x1b[32m' + data);
|
||||
});
|
||||
console.log('\x1b[32m' + figlet.textSync("FM-DX Webserver"));
|
||||
console.log('\x1b[32m\x1b[2mby Noobish @ \x1b[4mFMDX.org\x1b[0m');
|
||||
console.log("v" + pjson.version)
|
||||
console.log('\x1b[90m' + '─'.repeat(terminalWidth - 1) + '\x1b[0m');
|
||||
|
||||
const chatWss = createChatServer(storage);
|
||||
const audioWss = createAudioServer();
|
||||
// Start ffmpeg
|
||||
const audioWss = require('./stream/ws.js');
|
||||
require('./stream/index');
|
||||
require('./plugins');
|
||||
|
||||
@@ -107,6 +66,7 @@ const sessionMiddleware = session({
|
||||
});
|
||||
app.use(sessionMiddleware);
|
||||
app.use(bodyParser.json());
|
||||
const chatWss = createChatServer(storage);
|
||||
|
||||
connectToXdrd();
|
||||
connectToSerial();
|
||||
@@ -237,9 +197,7 @@ client.on('data', (data) => {
|
||||
const { xdrd } = serverConfig;
|
||||
|
||||
helpers.resolveDataBuffer(data, wss, rdsWss);
|
||||
if (authFlags.authMsg == true && authFlags.messageCount > 1) {
|
||||
return;
|
||||
}
|
||||
if (authFlags.authMsg == true && authFlags.messageCount > 1) return;
|
||||
|
||||
authFlags.messageCount++;
|
||||
const receivedData = data.toString();
|
||||
|
||||
@@ -93,6 +93,4 @@ function createLinks() {
|
||||
const allPluginConfigs = collectPluginConfigs();
|
||||
createLinks();
|
||||
|
||||
module.exports = {
|
||||
allPluginConfigs
|
||||
};
|
||||
module.exports = allPluginConfigs;
|
||||
|
||||
@@ -93,8 +93,8 @@ class RDSDecoder {
|
||||
|
||||
this.ps[idx * 2] = String.fromCharCode(blockD >> 8);
|
||||
this.ps[idx * 2 + 1] = String.fromCharCode(blockD & 0xFF);
|
||||
this.ps_errors[idx * 2] = error;
|
||||
this.ps_errors[idx * 2 + 1] = error;
|
||||
this.ps_errors[idx * 2] = Math.ceil(d_error * (10/3));
|
||||
this.ps_errors[idx * 2 + 1] = Math.ceil(d_error * (10/3));
|
||||
|
||||
this.data.ps = this.ps.join('');
|
||||
this.data.ps_errors = this.ps_errors.join(',');
|
||||
@@ -124,15 +124,15 @@ class RDSDecoder {
|
||||
if(c_error < 2 && multiplier !== 2) {
|
||||
this.rt1[idx * multiplier] = String.fromCharCode(blockC >> 8);
|
||||
this.rt1[idx * multiplier + 1] = String.fromCharCode(blockC & 0xFF);
|
||||
this.rt1_errors[idx * multiplier] = error;
|
||||
this.rt1_errors[idx * multiplier + 1] = error;
|
||||
this.rt1_errors[idx * multiplier] = Math.ceil(c_error * (10/3));
|
||||
this.rt1_errors[idx * multiplier + 1] = Math.ceil(c_error * (10/3));
|
||||
}
|
||||
if(d_error < 2) {
|
||||
var offset = (multiplier == 2) ? 0 : 2;
|
||||
this.rt1[idx * multiplier + offset] = String.fromCharCode(blockD >> 8);
|
||||
this.rt1[idx * multiplier + offset + 1] = String.fromCharCode(blockD & 0xFF);
|
||||
this.rt1_errors[idx * multiplier + offset] = error;
|
||||
this.rt1_errors[idx * multiplier + offset + 1] = error;
|
||||
this.rt1_errors[idx * multiplier + offset] = Math.ceil(d_error * (10/3));
|
||||
this.rt1_errors[idx * multiplier + offset + 1] = Math.ceil(d_error * (10/3));
|
||||
}
|
||||
|
||||
var i = this.rt1.indexOf("\r")
|
||||
@@ -155,15 +155,15 @@ class RDSDecoder {
|
||||
if(c_error !== 3 && multiplier !== 2) {
|
||||
this.rt0[idx * multiplier] = String.fromCharCode(blockC >> 8);
|
||||
this.rt0[idx * multiplier + 1] = String.fromCharCode(blockC & 0xFF);
|
||||
this.rt0_errors[idx * multiplier] = error;
|
||||
this.rt0_errors[idx * multiplier + 1] = error;
|
||||
this.rt0_errors[idx * multiplier] = Math.ceil(c_error * (10/3));
|
||||
this.rt0_errors[idx * multiplier + 1] = Math.ceil(c_error * (10/3));
|
||||
}
|
||||
if(d_error !== 3) {
|
||||
var offset = (multiplier == 2) ? 0 : 2;
|
||||
this.rt0[idx * multiplier + offset] = String.fromCharCode(blockD >> 8);
|
||||
this.rt0[idx * multiplier + offset + 1] = String.fromCharCode(blockD & 0xFF);
|
||||
this.rt0_errors[idx * multiplier + offset] = error;
|
||||
this.rt0_errors[idx * multiplier + offset + 1] = error;
|
||||
this.rt0_errors[idx * multiplier + offset] = Math.ceil(d_error * (10/3));
|
||||
this.rt0_errors[idx * multiplier + offset + 1] = Math.ceil(d_error * (10/3));
|
||||
}
|
||||
|
||||
var i = this.rt0.indexOf("\r");
|
||||
|
||||
@@ -21,7 +21,7 @@ checkFFmpeg().then((ffmpegPath) => {
|
||||
logInfo(`${consoleLogTitle} Using ${ffmpegPath === 'ffmpeg' ? 'system-installed FFmpeg' : 'ffmpeg-static'}`);
|
||||
logInfo(`${consoleLogTitle} Starting audio stream on device: \x1b[35m${serverConfig.audio.audioDevice}\x1b[0m`);
|
||||
|
||||
const sampleRate = Number(serverConfig.audio.sampleRate || 44100) + Number(serverConfig.audio.samplerateOffset || 0);
|
||||
const sampleRate = Number(serverConfig.audio.sampleRate || 44100) + Number(serverConfig.audio.samplerateOffset || 0); // Maybe even do 32 khz, we do not need higher than 15 khz precision
|
||||
|
||||
const channels = Number(serverConfig.audio.audioChannels || 2);
|
||||
|
||||
@@ -139,4 +139,4 @@ checkFFmpeg().then((ffmpegPath) => {
|
||||
logError(`${consoleLogTitle} Error: ${err.message}`);
|
||||
});
|
||||
|
||||
module.exports.audio_pipe = audio_pipe;
|
||||
module.exports = audio_pipe;
|
||||
@@ -1,9 +1,8 @@
|
||||
const WebSocket = require('ws');
|
||||
const { serverConfig } = require('../server_config');
|
||||
const { audio_pipe } = require('./index.js');
|
||||
const audio_pipe = require('./index.js');
|
||||
|
||||
function createAudioServer() {
|
||||
const audioWss = new WebSocket.Server({ noServer: true });
|
||||
const audioWss = new WebSocket.Server({ noServer: true, skipUTF8Validation: true });
|
||||
|
||||
audioWss.on('connection', (ws, request) => {
|
||||
const clientIp = request.headers['x-forwarded-for'] || request.socket.remoteAddress;
|
||||
@@ -26,7 +25,4 @@ function createAudioServer() {
|
||||
});
|
||||
});
|
||||
|
||||
return audioWss;
|
||||
}
|
||||
|
||||
module.exports = { createAudioServer };
|
||||
module.exports = audioWss;
|
||||
@@ -80,9 +80,7 @@ async function buildTxDatabase() {
|
||||
consoleCmd.logInfo('Fetching transmitter database...');
|
||||
const response = await fetch(`https://maps.fmdx.org/api?qth=${serverConfig.identification.lat},${serverConfig.identification.lon}`, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Accept': 'application/json'
|
||||
}
|
||||
headers: {'Accept': 'application/json'}
|
||||
});
|
||||
if (!response.ok) throw new Error(`HTTP error! Status: ${response.status}`);
|
||||
localDb = await response.json();
|
||||
@@ -169,9 +167,7 @@ function getStateForCoordinates(lat, lon) {
|
||||
|
||||
for (const feature of usStatesGeoJson.features) {
|
||||
const boundingBox = getStateBoundingBox(feature.geometry.coordinates);
|
||||
if (isCityInState(lat, lon, boundingBox)) {
|
||||
return feature.properties.name; // Return the state's name if city is inside bounding box
|
||||
}
|
||||
if (isCityInState(lat, lon, boundingBox)) return feature.properties.name; // Return the state's name if city is inside bounding box
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -208,22 +204,16 @@ function validPsCompare(rdsPs, stationPs) {
|
||||
for (let i = 0; i < standardizedRdsPs.length; i++) {
|
||||
// Skip this position if the character in standardizedRdsPs is an underscore.
|
||||
if (standardizedRdsPs[i] === '_') continue;
|
||||
if (token[i] === standardizedRdsPs[i]) {
|
||||
matchCount++;
|
||||
}
|
||||
}
|
||||
if (matchCount >= minMatchLen) {
|
||||
return true;
|
||||
if (token[i] === standardizedRdsPs[i]) matchCount++;
|
||||
}
|
||||
if (matchCount >= minMatchLen) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function evaluateStation(station, esMode) {
|
||||
let weightDistance = station.distanceKm;
|
||||
if (esMode && station.distanceKm > 700) {
|
||||
weightDistance = Math.abs(station.distanceKm - 1500) + 200;
|
||||
}
|
||||
if (esMode && station.distanceKm > 700) weightDistance = Math.abs(station.distanceKm - 1500) + 200;
|
||||
let erp = station.erp && station.erp > 0 ? station.erp : 1;
|
||||
let extraWeight = erp > weightedErp && station.distanceKm <= weightDistance ? 0.3 : 0;
|
||||
let score = 0;
|
||||
@@ -394,6 +384,4 @@ function deg2rad(deg) {
|
||||
return deg * (Math.PI / 180);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
fetchTx
|
||||
};
|
||||
module.exports = fetchTx;
|
||||
Reference in New Issue
Block a user