diff --git a/package-lock.json b/package-lock.json index 5eab2c7..2caec72 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "fm-dx-webserver", - "version": "1.3.11", + "version": "1.3.12", "lockfileVersion": 3, "requires": true, "packages": { diff --git a/package.json b/package.json index 3b3207e..989e5ef 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "fm-dx-webserver", - "version": "1.3.11", + "version": "1.3.12", "description": "FM DX Webserver", "main": "index.js", "scripts": { diff --git a/server/endpoints.js b/server/endpoints.js index 87eb480..ffdf365 100644 --- a/server/endpoints.js +++ b/server/endpoints.js @@ -155,14 +155,6 @@ router.get('/wizard', (req, res) => { }); -router.get('/rds', (req, res) => { - res.send('Please connect using a WebSocket compatible app to obtain RDS stream.'); -}); - -router.get('/rdsspy', (req, res) => { - res.send('Please connect using a WebSocket compatible app to obtain RDS stream.'); -}); - router.get('/rds', (req, res) => { res.send('Please connect using a WebSocket compatible app to obtain RDS stream.'); }); @@ -466,4 +458,24 @@ router.get('/log_fmlist', (req, res) => { request.end(); }); +router.get('/tunnelservers', async (req, res) => { + const servers = [ + { value: "eu", host: "eu.fmtuner.org", label: "Europe" }, + { value: "us", host: "us.fmtuner.org", label: "Americas" }, + { value: "sg", host: "sg.fmtuner.org", label: "Asia & Oceania" }, + ]; + + const results = await Promise.all( + servers.map(async s => { + const latency = await helpers.checkLatency(s.host); + return { + value: s.value, + label: `${s.label} (${latency ? latency + ' ms' : 'offline'})` // From my tests, the latency via HTTP ping is roughly 2x higher than regular ping + }; + }) + ); + + res.json(results); + }); + module.exports = router; diff --git a/server/helpers.js b/server/helpers.js index 9544023..2c8dd8c 100644 --- a/server/helpers.js +++ b/server/helpers.js @@ -240,6 +240,23 @@ function checkIPv6Support(callback) { }); } +function checkLatency(host) { + return new Promise(resolve => { + const start = Date.now(); + + const req = http.get({ host, timeout: 2000 }, res => { + res.resume(); // discard body + resolve(Date.now() - start); + }); + + req.on("error", () => resolve(null)); // server offline + req.on("timeout", () => { + req.destroy(); + resolve(null); + }); + }); +} + function antispamProtection(message, clientIp, ws, userCommands, lastWarn, userCommandHistory, lengthCommands, endpointName) { const command = message.toString(); const now = Date.now(); @@ -313,5 +330,5 @@ const escapeHtml = (unsafe) => { module.exports = { - authenticateWithXdrd, parseMarkdown, handleConnect, removeMarkdown, formatUptime, resolveDataBuffer, kickClient, checkIPv6Support, antispamProtection, escapeHtml + authenticateWithXdrd, parseMarkdown, handleConnect, removeMarkdown, formatUptime, resolveDataBuffer, kickClient, checkIPv6Support, checkLatency, antispamProtection, escapeHtml } \ No newline at end of file diff --git a/server/server_config.js b/server/server_config.js index f5d6a0e..62466bc 100644 --- a/server/server_config.js +++ b/server/server_config.js @@ -94,10 +94,15 @@ let serverConfig = { enabled: false, username: "", token: "", + region: "eu", lowLatencyMode: false, subdomain: "", httpName: "", httpPassword: "", + community: { + enabled: false, + host: "" + } }, plugins: [], device: 'tef', diff --git a/server/tunnel.js b/server/tunnel.js index d2341ab..74f00b6 100644 --- a/server/tunnel.js +++ b/server/tunnel.js @@ -41,6 +41,7 @@ async function connect() { } const cfg = ejs.render(frpcConfigTemplate, { cfg: serverConfig.tunnel, + host: serverConfig.tunnel.community.enabled ? serverConfig.tunnel.community.host : serverConfig.tunnel.region + ".fmtuner.org", server: { port: serverConfig.webserver.webserverPort } @@ -84,7 +85,7 @@ async function connect() { } const frpcConfigTemplate = ` -serverAddr = "fmtuner.org" +serverAddr = "<%= host %>" serverPort = 7000 loginFailExit = false log.disablePrintColor = true diff --git a/web/js/setup.js b/web/js/setup.js index 806b607..231106d 100644 --- a/web/js/setup.js +++ b/web/js/setup.js @@ -10,6 +10,9 @@ $(document).ready(function() { showPanelFromHash(); initNav(); initBanlist(); + + checkTunnelServers(); + setInterval(checkTunnelServers, 10000); }); /** @@ -253,3 +256,32 @@ async function loadConsoleLogs() { }); $("#console-output").length ? $("#console-output").scrollTop($("#console-output")[0].scrollHeight) : null; } + +function checkTunnelServers() { + $.ajax({ + url: '/tunnelservers', + method: 'GET', + success: function(servers) { + const $options = $('#tunnel-server ul.options'); + const $input = $('#tunnel-serverSelect'); + const selectedValue = $input.val(); // currently selected value (label or value?) + + servers.forEach(server => { + const $li = $options.find(`li[data-value="${server.value}"]`); + + if ($li.length) { + $li.text(server.label); + + // If this li is the currently selected one, update input text too + // Note: input.val() holds the label, so match by label is safer + if ($li.text() === selectedValue || server.value === selectedValue) { + $input.val(server.label); + } + } + }); + }, + error: function() { + console.error('Failed to load server latency data'); + } + }); +} diff --git a/web/js/webserver.js b/web/js/webserver.js index 951b086..2a210a1 100644 --- a/web/js/webserver.js +++ b/web/js/webserver.js @@ -1,7 +1,3 @@ -const versionDate = new Date('Sep 11, 2025 14:00:00'); -const currentVersion = `v1.3.11 [${versionDate.getDate()}/${versionDate.getMonth() + 1}/${versionDate.getFullYear()}]`; - - function loadScript(src) { return new Promise((resolve, reject) => { const script = document.createElement('script'); @@ -13,6 +9,7 @@ function loadScript(src) { } async function loadScriptsInOrder() { + await loadScript('./js/ver.js'); await loadScript('./js/api.js'); await loadScript('./js/main.js'); await loadScript('./js/dropdown.js'); diff --git a/web/setup.ejs b/web/setup.ejs index 00ee44e..dc7a5e0 100644 --- a/web/setup.ejs +++ b/web/setup.ejs @@ -129,7 +129,7 @@
Check your tuner at servers.fmdx.org.
By activating the Broadcast to map option,
you agree to the Terms of Service.
When you become an FMDX.org supporter, you can host your webserver without the need of a public IP address & port forwarding.
When you become a supporter, you can message the Founders on Discord for your login details.
Enabling low latency mode may provide better experience, however it will also use more bandwidth.
+ <%- include('_components', {component: 'text', cssClass: 'w-150 br-15', placeholder: '', label: 'Subdomain name', id: 'tunnel-subdomain'}) %>Enabling low latency mode may provide better experience, however it will also use more bandwidth.
+ +You can also self-host or ask other people to provide you a token. In this case, the server owner is responsible for any potential security issues.
+ <%- include('_components', {component: 'checkbox', cssClass: 'm-right-10', label: 'Use a community tunnel', id: 'tunnel-community-enabled'}) %>