1
0
mirror of https://github.com/KubaPro010/fm-dx-webserver.git synced 2026-02-26 22:13:53 +01:00

community tunnel, minor bugfixes

This commit is contained in:
Marek Farkaš
2025-11-30 22:49:00 +01:00
parent 7f6957e162
commit 531a0f23e6
10 changed files with 108 additions and 24 deletions

2
package-lock.json generated
View File

@@ -1,6 +1,6 @@
{ {
"name": "fm-dx-webserver", "name": "fm-dx-webserver",
"version": "1.3.11", "version": "1.3.12",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {

View File

@@ -1,6 +1,6 @@
{ {
"name": "fm-dx-webserver", "name": "fm-dx-webserver",
"version": "1.3.11", "version": "1.3.12",
"description": "FM DX Webserver", "description": "FM DX Webserver",
"main": "index.js", "main": "index.js",
"scripts": { "scripts": {

View File

@@ -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) => { router.get('/rds', (req, res) => {
res.send('Please connect using a WebSocket compatible app to obtain RDS stream.'); 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(); 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; module.exports = router;

View File

@@ -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) { function antispamProtection(message, clientIp, ws, userCommands, lastWarn, userCommandHistory, lengthCommands, endpointName) {
const command = message.toString(); const command = message.toString();
const now = Date.now(); const now = Date.now();
@@ -313,5 +330,5 @@ const escapeHtml = (unsafe) => {
module.exports = { module.exports = {
authenticateWithXdrd, parseMarkdown, handleConnect, removeMarkdown, formatUptime, resolveDataBuffer, kickClient, checkIPv6Support, antispamProtection, escapeHtml authenticateWithXdrd, parseMarkdown, handleConnect, removeMarkdown, formatUptime, resolveDataBuffer, kickClient, checkIPv6Support, checkLatency, antispamProtection, escapeHtml
} }

View File

@@ -94,10 +94,15 @@ let serverConfig = {
enabled: false, enabled: false,
username: "", username: "",
token: "", token: "",
region: "eu",
lowLatencyMode: false, lowLatencyMode: false,
subdomain: "", subdomain: "",
httpName: "", httpName: "",
httpPassword: "", httpPassword: "",
community: {
enabled: false,
host: ""
}
}, },
plugins: [], plugins: [],
device: 'tef', device: 'tef',

View File

@@ -41,6 +41,7 @@ async function connect() {
} }
const cfg = ejs.render(frpcConfigTemplate, { const cfg = ejs.render(frpcConfigTemplate, {
cfg: serverConfig.tunnel, cfg: serverConfig.tunnel,
host: serverConfig.tunnel.community.enabled ? serverConfig.tunnel.community.host : serverConfig.tunnel.region + ".fmtuner.org",
server: { server: {
port: serverConfig.webserver.webserverPort port: serverConfig.webserver.webserverPort
} }
@@ -84,7 +85,7 @@ async function connect() {
} }
const frpcConfigTemplate = ` const frpcConfigTemplate = `
serverAddr = "fmtuner.org" serverAddr = "<%= host %>"
serverPort = 7000 serverPort = 7000
loginFailExit = false loginFailExit = false
log.disablePrintColor = true log.disablePrintColor = true

View File

@@ -10,6 +10,9 @@ $(document).ready(function() {
showPanelFromHash(); showPanelFromHash();
initNav(); initNav();
initBanlist(); initBanlist();
checkTunnelServers();
setInterval(checkTunnelServers, 10000);
}); });
/** /**
@@ -253,3 +256,32 @@ async function loadConsoleLogs() {
}); });
$("#console-output").length ? $("#console-output").scrollTop($("#console-output")[0].scrollHeight) : null; $("#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');
}
});
}

View File

@@ -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) { function loadScript(src) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const script = document.createElement('script'); const script = document.createElement('script');
@@ -13,6 +9,7 @@ function loadScript(src) {
} }
async function loadScriptsInOrder() { async function loadScriptsInOrder() {
await loadScript('./js/ver.js');
await loadScript('./js/api.js'); await loadScript('./js/api.js');
await loadScript('./js/main.js'); await loadScript('./js/main.js');
await loadScript('./js/dropdown.js'); await loadScript('./js/dropdown.js');

View File

@@ -129,7 +129,7 @@
<div class="panel-100-real p-bottom-20"> <div class="panel-100-real p-bottom-20">
<h3>Quick settings</h3> <h3>Quick settings</h3>
<div class="flex-container flex-center" style="margin: 30px;"> <div class="flex-container flex-center" style="margin: 30px;">
<%- include('_components', {component: 'checkbox', cssClass: '', label: 'Public Tuner', id: 'publicTuner'}) %> <%- include('_components', {component: 'checkbox', cssClass: '', label: 'Unlocked Tuner', id: 'publicTuner'}) %>
<%- include('_components', {component: 'checkbox', cssClass: '', label: 'Admin lock', id: 'lockToAdmin'}) %><br> <%- include('_components', {component: 'checkbox', cssClass: '', label: 'Admin lock', id: 'lockToAdmin'}) %><br>
</div> </div>
@@ -483,7 +483,7 @@
<%- include('_components', {component: 'checkbox', cssClass: '', label: 'Broadcast to map', id: 'identification-broadcastTuner'}) %><br> <%- include('_components', {component: 'checkbox', cssClass: '', label: 'Broadcast to map', id: 'identification-broadcastTuner'}) %><br>
<%- include('_components', {component: 'text', cssClass: 'br-15', placeholder: 'Your e-mail or Discord...', label: 'Owner contact', id: 'identification-contact'}) %> <%- include('_components', {component: 'text', cssClass: 'br-15', placeholder: 'Your e-mail or Discord...', label: 'Owner contact', id: 'identification-contact'}) %>
<%- include('_components', {component: 'text', cssClass: 'br-15', label: 'Broadcast address (if using a proxy)', id: 'identification-proxyIp'}) %> <%- include('_components', {component: 'text', cssClass: 'br-15', label: 'Proxy address', id: 'identification-proxyIp'}) %>
<p>Check your tuner at <strong><a href="https://servers.fmdx.org" target="_blank" class="color-4">servers.fmdx.org</a></strong>.</p> <p>Check your tuner at <strong><a href="https://servers.fmdx.org" target="_blank" class="color-4">servers.fmdx.org</a></strong>.</p>
<p class="text-small text-gray">By activating the <strong>Broadcast to map</strong> option,<br>you agree to the <a href="https://fmdx.org/projects/webserver.php#rules" target="_blank">Terms of Service</a>.</p> <p class="text-small text-gray">By activating the <strong>Broadcast to map</strong> option,<br>you agree to the <a href="https://fmdx.org/projects/webserver.php#rules" target="_blank">Terms of Service</a>.</p>
@@ -700,20 +700,31 @@
<h3>Tunnel</h3> <h3>Tunnel</h3>
<p>When you become an <a href="https://buymeacoffee.com/fmdx" target="_blank"><strong>FMDX.org supporter</strong></a>, you can host your webserver without the need of a public IP address & port forwarding.<br> <p>When you become an <a href="https://buymeacoffee.com/fmdx" target="_blank"><strong>FMDX.org supporter</strong></a>, you can host your webserver without the need of a public IP address & port forwarding.<br>
When you become a supporter, you can message the Founders on Discord for your login details.</p> When you become a supporter, you can message the Founders on Discord for your login details.</p>
<h4>Main tunnel settings</h4>
<%- include('_components', {component: 'checkbox', cssClass: 'm-right-10', label: 'Enable tunnel', id: 'tunnel-enabled'}) %><br> <%- include('_components', {component: 'checkbox', cssClass: 'm-right-10', label: 'Enable tunnel', id: 'tunnel-enabled'}) %><br>
<%- include('_components', { component: 'dropdown', id: 'tunnel-server', inputId: 'tunnel-serverSelect', label: 'Official server region', cssClass: '', placeholder: 'Europe',
options: [
{ value: 'eu', label: 'Europe' },
{ value: 'us', label: 'Americas' },
{ value: 'sg', label: 'Asia & Oceania' },
]
}) %>
<%- include('_components', {component: 'text', cssClass: 'w-150 br-15', placeholder: '', label: 'Username', id: 'tunnel-username'}) %> <%- include('_components', {component: 'text', cssClass: 'w-150 br-15', placeholder: '', label: 'Username', id: 'tunnel-username'}) %>
<%- include('_components', {component: 'text', cssClass: 'w-250 br-15', password: true, placeholder: '', label: 'Token', id: 'tunnel-token'}) %> <%- include('_components', {component: 'text', cssClass: 'w-250 br-15', password: true, placeholder: '', label: 'Token', id: 'tunnel-token'}) %>
<%- include('_components', {component: 'text', cssClass: 'w-150 br-15', placeholder: '', label: 'Subdomain name', id: 'tunnel-subdomain'}) %>.fmtuner.org <%- include('_components', {component: 'text', cssClass: 'w-150 br-15', placeholder: '', label: 'Subdomain name', id: 'tunnel-subdomain'}) %><br>
<p>Enabling low latency mode may provide better experience, however it will also use more bandwidth.</p>
<%- include('_components', {component: 'checkbox', cssClass: 'm-right-10', label: 'Low latency mode', id: 'tunnel-lowLatencyMode'}) %><br> <%- include('_components', {component: 'checkbox', cssClass: 'm-right-10', label: 'Low latency mode', id: 'tunnel-lowLatencyMode'}) %><br>
<p>Enabling low latency mode may provide better experience, however it will also use more bandwidth.</p>
<h4 class="top-25">Community tunnel settings</h4>
<p>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.</p>
<%- include('_components', {component: 'checkbox', cssClass: 'm-right-10', label: 'Use a community tunnel', id: 'tunnel-community-enabled'}) %><br>
<%- include('_components', {component: 'text', cssClass: 'w-250 br-15', placeholder: '', label: 'Community Tunnel host (IP or hostname)', id: 'tunnel-community-host'}) %>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<script src="js/init.js"></script> <script src="js/ver.js"></script>
<script src="js/toast.js"></script> <script src="js/toast.js"></script>
<script src="js/settings.js"></script> <script src="js/settings.js"></script>
<script src="js/dropdown.js"></script> <script src="js/dropdown.js"></script>
@@ -722,5 +733,15 @@
<% enabledPlugins?.forEach(function(plugin) { %> <% enabledPlugins?.forEach(function(plugin) { %>
<script src="js/plugins/<%= plugin %>"></script> <script src="js/plugins/<%= plugin %>"></script>
<% }); %> <% }); %>
<script>
document.addEventListener('keydown', function(event) {
if (event.ctrlKey && event.key === 's') {
event.preventDefault();
if (event.repeat) return;
submitConfig();
}
});
</script>
</body> </body>
</html> </html>

View File

@@ -220,7 +220,6 @@
</div> </div>
</div> </div>
</div> </div>
<script src="js/init.js"></script>
<script src="js/settings.js"></script> <script src="js/settings.js"></script>
<script src="js/dropdown.js"></script> <script src="js/dropdown.js"></script>
<script src="js/toast.js"></script> <script src="js/toast.js"></script>