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
community tunnel, minor bugfixes
This commit is contained in:
2
package-lock.json
generated
2
package-lock.json
generated
@@ -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": {
|
||||||
|
|||||||
@@ -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": {
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
@@ -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',
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|||||||
@@ -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');
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
Reference in New Issue
Block a user