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
admin dashboard, bugfixes, cleanup
This commit is contained in:
4
.gitignore
vendored
4
.gitignore
vendored
@@ -1,3 +1,3 @@
|
||||
node_modules/
|
||||
/example.js
|
||||
/config.json
|
||||
/*.json
|
||||
/ffmpeg.exe
|
||||
61
index.js
61
index.js
@@ -281,9 +281,6 @@ app.get('/static_data', (req, res) => {
|
||||
});
|
||||
|
||||
app.get('/server_time', (req, res) => {
|
||||
/*const serverTime = new Date().toISOString(); // Get server time in ISO format
|
||||
const serverTimezoneOffset = new Date().getTimezoneOffset(); // Get server timezone offset in minutes*/
|
||||
|
||||
const serverTime = new Date(); // Get current server time
|
||||
const serverTimeUTC = new Date(serverTime.getTime() - (serverTime.getTimezoneOffset() * 60000)); // Adjust server time to UTC
|
||||
res.json({
|
||||
@@ -577,8 +574,6 @@ app.get('/getDevices', (req, res) => {
|
||||
/**
|
||||
* WEBSOCKET BLOCK
|
||||
*/
|
||||
let lastDisconnectTime = null;
|
||||
|
||||
wss.on('connection', (ws, request) => {
|
||||
const clientIp = request.headers['x-forwarded-for'] || request.connection.remoteAddress;
|
||||
currentUsers++;
|
||||
@@ -618,40 +613,57 @@ wss.on('connection', (ws, request) => {
|
||||
});
|
||||
|
||||
ws.on('message', (message) => {
|
||||
logDebug('Command received from \x1b[90m' + clientIp + '\x1b[0m:', message.toString());
|
||||
command = message.toString();
|
||||
const command = message.toString();
|
||||
logDebug(`Command received from \x1b[90m${clientIp}\x1b[0m: ${command}`);
|
||||
|
||||
if (command.startsWith('X')) {
|
||||
logWarn('Remote tuner shutdown attempted by \x1b[90m' + clientIp + '\x1b[0m. You may consider blocking this user.');
|
||||
logWarn(`Remote tuner shutdown attempted by \x1b[90m${clientIp}\x1b[0m. You may consider blocking this user.`);
|
||||
return;
|
||||
}
|
||||
|
||||
if(command.includes('\'')) {
|
||||
if (command.includes("'")) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (command.startsWith('w') && request.session.isAdminAuthenticated) {
|
||||
switch (command) {
|
||||
case 'wL1':
|
||||
serverConfig.lockToAdmin = true;
|
||||
break;
|
||||
case 'wL0':
|
||||
serverConfig.lockToAdmin = false;
|
||||
break;
|
||||
case 'wT0':
|
||||
serverConfig.publicTuner = true;
|
||||
break;
|
||||
case 'wT1':
|
||||
serverConfig.publicTuner = false;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (command.startsWith('T')) {
|
||||
let tuneFreq = Number(command.slice(1)) / 1000;
|
||||
const tuneFreq = Number(command.slice(1)) / 1000;
|
||||
const { tuningLimit, tuningLowerLimit, tuningUpperLimit } = serverConfig.webserver;
|
||||
|
||||
if(serverConfig.webserver.tuningLimit === true && (tuneFreq < serverConfig.webserver.tuningLowerLimit || tuneFreq > serverConfig.webserver.tuningUpperLimit) || isNaN(tuneFreq)) {
|
||||
if (tuningLimit && (tuneFreq < tuningLowerLimit || tuneFreq > tuningUpperLimit) || isNaN(tuneFreq)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if((serverConfig.publicTuner === true) || (request.session && request.session.isTuneAuthenticated === true && serverConfig.xdrd.wirelessConnection)) {
|
||||
const { isAdminAuthenticated, isTuneAuthenticated } = request.session || {};
|
||||
const { wirelessConnection } = serverConfig.xdrd;
|
||||
|
||||
if(serverConfig.lockToAdmin === true) {
|
||||
if(request.session && request.session.isAdminAuthenticated === true) {
|
||||
serverConfig.xdrd.wirelessConnection === true ? client.write(command + "\n") : serialport.write(command + "\n");
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
serverConfig.xdrd.wirelessConnection === true ? client.write(command + "\n") : serialport.write(command + "\n");
|
||||
}
|
||||
if ((serverConfig.publicTuner || (isTuneAuthenticated && wirelessConnection)) &&
|
||||
(!serverConfig.lockToAdmin || isAdminAuthenticated)) {
|
||||
const output = serverConfig.xdrd.wirelessConnection ? client : serialport;
|
||||
output.write(`${command}\n`);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
ws.on('close', (code, reason) => {
|
||||
currentUsers--;
|
||||
dataHandler.showOnlineUsers(currentUsers);
|
||||
@@ -683,7 +695,6 @@ wss.on('connection', (ws, request) => {
|
||||
});
|
||||
|
||||
// CHAT WEBSOCKET BLOCK
|
||||
// Assuming chatWss is your WebSocket server instance
|
||||
// Initialize an array to store chat messages
|
||||
let chatHistory = [];
|
||||
|
||||
@@ -713,22 +724,20 @@ chatWss.on('connection', (ws, request) => {
|
||||
messageData.time = `${hours}:${minutes}`; // Adding current time to the message object in hours:minutes format
|
||||
|
||||
if (serverConfig.webserver.banlist?.includes(clientIp)) {
|
||||
return; // Do not proceed further if banned
|
||||
return;
|
||||
}
|
||||
|
||||
if(request.session.isAdminAuthenticated === true) {
|
||||
messageData.admin = true;
|
||||
}
|
||||
|
||||
// Limit message length to 255 characters
|
||||
if (messageData.message.length > 255) {
|
||||
messageData.message = messageData.message.substring(0, 255);
|
||||
}
|
||||
|
||||
// Add the new message to chat history and keep only the latest 50 messages
|
||||
chatHistory.push(messageData);
|
||||
if (chatHistory.length > 50) {
|
||||
chatHistory.shift(); // Remove the oldest message if the history exceeds 50 messages
|
||||
chatHistory.shift();
|
||||
}
|
||||
|
||||
const modifiedMessage = JSON.stringify(messageData);
|
||||
|
||||
186
package-lock.json
generated
186
package-lock.json
generated
@@ -1,29 +1,28 @@
|
||||
{
|
||||
"name": "fm-dx-webserver",
|
||||
"version": "1.1.3",
|
||||
"version": "1.1.5",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "fm-dx-webserver",
|
||||
"version": "1.1.3",
|
||||
"version": "1.1.5",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@mapbox/node-pre-gyp": "^1.0.11",
|
||||
"body-parser": "^1.20.2",
|
||||
"command-exists-promise": "^2.0.2",
|
||||
"ejs": "^3.1.9",
|
||||
"express": "4.18.2",
|
||||
"express-session": "^1.18.0",
|
||||
"http": "^0.0.1-security",
|
||||
"http-proxy": "^1.18.1",
|
||||
"@mapbox/node-pre-gyp": "1.0.11",
|
||||
"body-parser": "1.20.2",
|
||||
"command-exists-promise": "2.0.2",
|
||||
"ejs": "3.1.9",
|
||||
"express": "4.18.3",
|
||||
"express-session": "1.18.0",
|
||||
"http": "0.0.1-security",
|
||||
"http-proxy": "1.18.1",
|
||||
"https": "1.0.0",
|
||||
"koffi": "2.7.2",
|
||||
"net": "1.0.2",
|
||||
"serialport": "^12.0.0",
|
||||
"websocket": "1.0.34",
|
||||
"wrtc": "^0.4.7",
|
||||
"ws": "^8.14.2"
|
||||
"serialport": "12.0.0",
|
||||
"wrtc": "0.4.7",
|
||||
"ws": "8.14.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@mapbox/node-pre-gyp": {
|
||||
@@ -447,6 +446,8 @@
|
||||
"resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-4.0.8.tgz",
|
||||
"integrity": "sha512-4T53u4PdgsXqKaIctwF8ifXlRTTmEPJ8iEPWFdGZvcf7sbwYo6FKFEX9eNNAnzFZ7EzJAQ3CJeOtCRA4rDp7Pw==",
|
||||
"hasInstallScript": true,
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"node-gyp-build": "^4.3.0"
|
||||
},
|
||||
@@ -572,15 +573,6 @@
|
||||
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
|
||||
"integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ=="
|
||||
},
|
||||
"node_modules/d": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz",
|
||||
"integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==",
|
||||
"dependencies": {
|
||||
"es5-ext": "^0.10.50",
|
||||
"type": "^1.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/debug": {
|
||||
"version": "2.6.9",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
|
||||
@@ -674,39 +666,6 @@
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/es5-ext": {
|
||||
"version": "0.10.62",
|
||||
"resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.62.tgz",
|
||||
"integrity": "sha512-BHLqn0klhEpnOKSrzn/Xsz2UIW8j+cGmo9JLzr8BiUapV8hPL9+FliFqjwr9ngW7jWdnxv6eO+/LqyhJVqgrjA==",
|
||||
"hasInstallScript": true,
|
||||
"dependencies": {
|
||||
"es6-iterator": "^2.0.3",
|
||||
"es6-symbol": "^3.1.3",
|
||||
"next-tick": "^1.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10"
|
||||
}
|
||||
},
|
||||
"node_modules/es6-iterator": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz",
|
||||
"integrity": "sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==",
|
||||
"dependencies": {
|
||||
"d": "1",
|
||||
"es5-ext": "^0.10.35",
|
||||
"es6-symbol": "^3.1.1"
|
||||
}
|
||||
},
|
||||
"node_modules/es6-symbol": {
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz",
|
||||
"integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==",
|
||||
"dependencies": {
|
||||
"d": "^1.0.1",
|
||||
"ext": "^1.1.2"
|
||||
}
|
||||
},
|
||||
"node_modules/escape-html": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
|
||||
@@ -726,13 +685,13 @@
|
||||
"integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw=="
|
||||
},
|
||||
"node_modules/express": {
|
||||
"version": "4.18.2",
|
||||
"resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz",
|
||||
"integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==",
|
||||
"version": "4.18.3",
|
||||
"resolved": "https://registry.npmjs.org/express/-/express-4.18.3.tgz",
|
||||
"integrity": "sha512-6VyCijWQ+9O7WuVMTRBTl+cjNNIzD5cY5mQ1WM8r/LEkI2u8EYpOotESNwzNlyCn3g+dmjKYI6BmNneSr/FSRw==",
|
||||
"dependencies": {
|
||||
"accepts": "~1.3.8",
|
||||
"array-flatten": "1.1.1",
|
||||
"body-parser": "1.20.1",
|
||||
"body-parser": "1.20.2",
|
||||
"content-disposition": "0.5.4",
|
||||
"content-type": "~1.0.4",
|
||||
"cookie": "0.5.0",
|
||||
@@ -797,56 +756,6 @@
|
||||
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.7.tgz",
|
||||
"integrity": "sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA=="
|
||||
},
|
||||
"node_modules/express/node_modules/body-parser": {
|
||||
"version": "1.20.1",
|
||||
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz",
|
||||
"integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==",
|
||||
"dependencies": {
|
||||
"bytes": "3.1.2",
|
||||
"content-type": "~1.0.4",
|
||||
"debug": "2.6.9",
|
||||
"depd": "2.0.0",
|
||||
"destroy": "1.2.0",
|
||||
"http-errors": "2.0.0",
|
||||
"iconv-lite": "0.4.24",
|
||||
"on-finished": "2.4.1",
|
||||
"qs": "6.11.0",
|
||||
"raw-body": "2.5.1",
|
||||
"type-is": "~1.6.18",
|
||||
"unpipe": "1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8",
|
||||
"npm": "1.2.8000 || >= 1.4.16"
|
||||
}
|
||||
},
|
||||
"node_modules/express/node_modules/raw-body": {
|
||||
"version": "2.5.1",
|
||||
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz",
|
||||
"integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==",
|
||||
"dependencies": {
|
||||
"bytes": "3.1.2",
|
||||
"http-errors": "2.0.0",
|
||||
"iconv-lite": "0.4.24",
|
||||
"unpipe": "1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/ext": {
|
||||
"version": "1.7.0",
|
||||
"resolved": "https://registry.npmjs.org/ext/-/ext-1.7.0.tgz",
|
||||
"integrity": "sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==",
|
||||
"dependencies": {
|
||||
"type": "^2.7.2"
|
||||
}
|
||||
},
|
||||
"node_modules/ext/node_modules/type": {
|
||||
"version": "2.7.2",
|
||||
"resolved": "https://registry.npmjs.org/type/-/type-2.7.2.tgz",
|
||||
"integrity": "sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw=="
|
||||
},
|
||||
"node_modules/filelist": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz",
|
||||
@@ -1193,11 +1102,6 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/is-typedarray": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
|
||||
"integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA=="
|
||||
},
|
||||
"node_modules/jake": {
|
||||
"version": "10.8.7",
|
||||
"resolved": "https://registry.npmjs.org/jake/-/jake-10.8.7.tgz",
|
||||
@@ -1376,11 +1280,6 @@
|
||||
"resolved": "https://registry.npmjs.org/net/-/net-1.0.2.tgz",
|
||||
"integrity": "sha512-kbhcj2SVVR4caaVnGLJKmlk2+f+oLkjqdKeQlmUtz6nGzOpbcobwVIeSURNgraV/v3tlmGIX82OcPCl0K6RbHQ=="
|
||||
},
|
||||
"node_modules/next-tick": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz",
|
||||
"integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ=="
|
||||
},
|
||||
"node_modules/node-addon-api": {
|
||||
"version": "7.0.0",
|
||||
"resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.0.0.tgz",
|
||||
@@ -1409,6 +1308,8 @@
|
||||
"version": "4.6.1",
|
||||
"resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.6.1.tgz",
|
||||
"integrity": "sha512-24vnklJmyRS8ViBNI8KbtK/r/DmXQMRiOMXTNz2nrTnAYUwjmEEbnnpB/+kt+yWRv73bPsSPRFddrcIbAxSiMQ==",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"node-gyp-build": "bin.js",
|
||||
"node-gyp-build-optional": "optional.js",
|
||||
@@ -1617,9 +1518,9 @@
|
||||
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
|
||||
},
|
||||
"node_modules/semver": {
|
||||
"version": "7.5.4",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz",
|
||||
"integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==",
|
||||
"version": "7.6.0",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz",
|
||||
"integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==",
|
||||
"dependencies": {
|
||||
"lru-cache": "^6.0.0"
|
||||
},
|
||||
@@ -1842,11 +1743,6 @@
|
||||
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
|
||||
"integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="
|
||||
},
|
||||
"node_modules/type": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz",
|
||||
"integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg=="
|
||||
},
|
||||
"node_modules/type-is": {
|
||||
"version": "1.6.18",
|
||||
"resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
|
||||
@@ -1859,14 +1755,6 @@
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/typedarray-to-buffer": {
|
||||
"version": "3.1.5",
|
||||
"resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz",
|
||||
"integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==",
|
||||
"dependencies": {
|
||||
"is-typedarray": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/uid-safe": {
|
||||
"version": "2.1.5",
|
||||
"resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.5.tgz",
|
||||
@@ -1891,6 +1779,8 @@
|
||||
"resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.10.tgz",
|
||||
"integrity": "sha512-Z6czzLq4u8fPOyx7TU6X3dvUZVvoJmxSQ+IcrlmagKhilxlhZgxPK6C5Jqbkw1IDUmFTM+cz9QDnnLTwDz/2gQ==",
|
||||
"hasInstallScript": true,
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"node-gyp-build": "^4.3.0"
|
||||
},
|
||||
@@ -1925,22 +1815,6 @@
|
||||
"integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/websocket": {
|
||||
"version": "1.0.34",
|
||||
"resolved": "https://registry.npmjs.org/websocket/-/websocket-1.0.34.tgz",
|
||||
"integrity": "sha512-PRDso2sGwF6kM75QykIesBijKSVceR6jL2G8NGYyq2XrItNC2P5/qL5XeR056GhA+Ly7JMFvJb9I312mJfmqnQ==",
|
||||
"dependencies": {
|
||||
"bufferutil": "^4.0.1",
|
||||
"debug": "^2.2.0",
|
||||
"es5-ext": "^0.10.50",
|
||||
"typedarray-to-buffer": "^3.1.5",
|
||||
"utf-8-validate": "^5.0.2",
|
||||
"yaeti": "^0.0.6"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/whatwg-url": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
|
||||
@@ -2006,14 +1880,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/yaeti": {
|
||||
"version": "0.0.6",
|
||||
"resolved": "https://registry.npmjs.org/yaeti/-/yaeti-0.0.6.tgz",
|
||||
"integrity": "sha512-MvQa//+KcZCUkBTIC9blM+CU9J2GzuTytsOUwf2lidtvkx/6gnEp1QvJv34t9vdjhFmha/mUiNDbN0D0mJWdug==",
|
||||
"engines": {
|
||||
"node": ">=0.10.32"
|
||||
}
|
||||
},
|
||||
"node_modules/yallist": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
|
||||
|
||||
25
package.json
25
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "fm-dx-webserver",
|
||||
"version": "1.1.4",
|
||||
"version": "1.1.5",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
@@ -12,20 +12,19 @@
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@mapbox/node-pre-gyp": "^1.0.11",
|
||||
"body-parser": "^1.20.2",
|
||||
"command-exists-promise": "^2.0.2",
|
||||
"ejs": "^3.1.9",
|
||||
"express": "4.18.2",
|
||||
"express-session": "^1.18.0",
|
||||
"http": "^0.0.1-security",
|
||||
"http-proxy": "^1.18.1",
|
||||
"@mapbox/node-pre-gyp": "1.0.11",
|
||||
"body-parser": "1.20.2",
|
||||
"command-exists-promise": "2.0.2",
|
||||
"ejs": "3.1.9",
|
||||
"express": "4.18.3",
|
||||
"express-session": "1.18.0",
|
||||
"http": "0.0.1-security",
|
||||
"http-proxy": "1.18.1",
|
||||
"https": "1.0.0",
|
||||
"koffi": "2.7.2",
|
||||
"net": "1.0.2",
|
||||
"serialport": "^12.0.0",
|
||||
"websocket": "1.0.34",
|
||||
"wrtc": "^0.4.7",
|
||||
"ws": "^8.14.2"
|
||||
"serialport": "12.0.0",
|
||||
"wrtc": "0.4.7",
|
||||
"ws": "8.14.2"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ function enableAudioStream() {
|
||||
ffmpegCommand = `${flags} -f dshow -audio_buffer_size 50 -i audio="${serverConfig.audio.audioDevice}" ${codec} ${output} pipe:1 | node stream/3las.server.js -port ${serverConfig.webserver.webserverPort + 10} -samplerate 48000 -channels ${serverConfig.audio.audioChannels}`;
|
||||
} else {
|
||||
// Linux
|
||||
ffmpegCommand = `${flags} -f alsa -i "${serverConfig.audio.audioDevice}" ${codec} ${output} pipe:1 | node stream/3las.server.js -port ${serverConfig.webserver.webserverPort + 10} -samplerate 48000 -channels ${serverConfig.audio.audioChannels}`;
|
||||
ffmpegCommand = `${flags} -f alsa -i "${serverConfig.audio.softwareMode && serverConfig.audio.softwareMode == true ? 'plug' : ''}${serverConfig.audio.audioDevice}" ${codec} ${output} pipe:1 | node stream/3las.server.js -port ${serverConfig.webserver.webserverPort + 10} -samplerate 48000 -channels ${serverConfig.audio.audioChannels}`;
|
||||
}
|
||||
|
||||
consoleCmd.logInfo("Using audio device: " + serverConfig.audio.audioDevice);
|
||||
|
||||
@@ -50,7 +50,7 @@ h4 {
|
||||
border-radius: 30px;
|
||||
padding: 5px 25px;
|
||||
z-index: 1000;
|
||||
opacity:var(--color-main);
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s ease-in-out;
|
||||
}
|
||||
|
||||
@@ -248,6 +248,42 @@ label {
|
||||
opacity: 0; /* Make the overlay invisible */
|
||||
}
|
||||
|
||||
.admin-quick-dashboard {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: -96px;
|
||||
width: 96px;
|
||||
height: 286px;
|
||||
background-color: var(--color-2);
|
||||
margin: auto 0;
|
||||
border-radius: 30px;
|
||||
padding-top: 15px;
|
||||
}
|
||||
|
||||
.admin-quick-dashboard .icon {
|
||||
width: 72px;
|
||||
height: 72px;
|
||||
margin: 10px auto;
|
||||
color: var(--color-1);
|
||||
background-color: var(--color-3);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
font-size: 24px;
|
||||
border-radius: 10px;
|
||||
transition: 0.3s ease-in-out background-color;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.admin-quick-dashboard .icon.active {
|
||||
background-color: var(--color-4);
|
||||
}
|
||||
|
||||
.admin-quick-dashboard .icon:hover {
|
||||
background-color: var(--color-5);
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
canvas, #flags-container {
|
||||
display: none;
|
||||
|
||||
@@ -52,8 +52,10 @@
|
||||
<audio id="audioTag"></audio>
|
||||
<div id="wrapper">
|
||||
<div class="panel-100 no-bg tuner-info">
|
||||
<h1 id="tuner-name"><%= tunerName %> <% if (!publicTuner) { %><i class="fa-solid fa-key pointer" title="Only people with tune password can tune."></i>
|
||||
<% } else if (tunerLock) { %><i class="fa-solid fa-lock pointer" title="Tuner is currently locked to admin."></i><% } %>
|
||||
<h1 id="tuner-name"><%= tunerName %>
|
||||
<% if (!publicTuner) { %><i class="fa-solid fa-key pointer tooltip" data-tooltip="Only people with tune password can tune."></i>
|
||||
<% } if (tunerLock) { %><i class="fa-solid fa-lock pointer tooltip" data-tooltip="Tuner is currently locked to admin."></i>
|
||||
<% } %>
|
||||
</h1>
|
||||
<p id="tuner-desc">
|
||||
<%- tunerDesc %>
|
||||
@@ -74,7 +76,7 @@
|
||||
<div class="panel-10 no-bg h-100 m-0 m-right-20 hide-phone" style="width: 100px;margin-right: 20px !important;">
|
||||
<button class="playbutton" aria-label="Play / Stop Button"><i class="fa-solid fa-play fa-lg"></i></button>
|
||||
</div>
|
||||
<div class="panel-100 m-0 hover-brighten flex-center" id="ps-container" style="height: 90px;" data-tooltip="Clicking on the RDS PS will copy the RDS info into the clipboard.">
|
||||
<div class="panel-100 m-0 hover-brighten flex-center tooltip" id="ps-container" style="height: 90px;" data-tooltip="Clicking on the RDS PS will copy the RDS info into the clipboard.">
|
||||
<span class="text-big" id="data-ps"></span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -91,7 +93,7 @@
|
||||
</div>
|
||||
<span id="stereo-container" class="pointer" style="position: relative;">
|
||||
<span style="margin-left: 20px;" class="data-st">ST</span>
|
||||
<span class="overlay" data-tooltip="Stereo / Mono toggle. <br><strong>Click to toggle."></span>
|
||||
<span class="overlay tooltip" data-tooltip="Stereo / Mono toggle. <br><strong>Click to toggle."></span>
|
||||
</span>
|
||||
<span style="margin-left: 15px;" class="data-ms">MS</span>
|
||||
</h3>
|
||||
@@ -99,7 +101,7 @@
|
||||
</div>
|
||||
|
||||
<div class="flex-container">
|
||||
<div class="panel-33 hover-brighten" id="pi-code-container" data-tooltip="Clicking on the PI code will show the current station on a map.">
|
||||
<div class="panel-33 hover-brighten tooltip" id="pi-code-container" data-tooltip="Clicking on the PI code will show the current station on a map.">
|
||||
<h2>PI CODE</h2>
|
||||
<span id="data-pi" class="text-big text-uppercase"></span>
|
||||
</div>
|
||||
@@ -144,12 +146,12 @@
|
||||
<% } %>
|
||||
|
||||
<div class="panel-100 no-bg h-100 m-0 button-eq">
|
||||
<% if (device == 'tef') { %><button id="data-eq" style="border-radius: 30px 0px 0px 30px;" aria-label="EQ Filter" data-tooltip="<strong>The cEQ filter can reduce bandwidth below 56 KHz.</strong><br><br>Useful for weak stations next to strong ones,<br>although it may pick up more interference."><span class="text-bold">cEQ</span></button><% } %>
|
||||
<% if (device == 'xdr') { %><button id="data-eq" style="border-radius: 30px 0px 0px 30px;" aria-label="RF+ Filter" data-tooltip="<strong>The RF+ filter increases gain by 5dB</strong>"><span class="text-bold">RF+</span></button><% } %>
|
||||
<% if (device == 'tef') { %><button id="data-eq" style="border-radius: 30px 0px 0px 30px;" class="tooltip" aria-label="EQ Filter" data-tooltip="<strong>The cEQ filter can reduce bandwidth below 56 KHz.</strong><br><br>Useful for weak stations next to strong ones,<br>although it may pick up more interference."><span class="text-bold">cEQ</span></button><% } %>
|
||||
<% if (device == 'xdr') { %><button id="data-eq" style="border-radius: 30px 0px 0px 30px;" class="tooltip" aria-label="RF+ Filter" data-tooltip="<strong>The RF+ filter increases gain by 5dB</strong>"><span class="text-bold">RF+</span></button><% } %>
|
||||
</div>
|
||||
<div class="panel-100 no-bg h-100 m-0 button-ims">
|
||||
<% if (device == 'tef') { %><button id="data-ims" style="border-radius: 0px 30px 30px 0px;" aria-label="iMS + Filter" data-tooltip="<strong>The iMS filter reduces multipath audio artifacts.</strong><br><br>It's recommended to leave it on most of the time."><span class="text-bold">iMS</span></button><% } %>
|
||||
<% if (device == 'xdr') { %><button id="data-ims" style="border-radius: 0px 30px 30px 0px;" aria-label="IF+ Filter" data-tooltip="<strong>The IF+ filter increases gain by 6dB</strong>"><span class="text-bold">IF+</span></button><% } %>
|
||||
<% if (device == 'tef') { %><button id="data-ims" style="border-radius: 0px 30px 30px 0px;" class="tooltip" aria-label="iMS + Filter" data-tooltip="<strong>The iMS filter reduces multipath audio artifacts.</strong><br><br>It's recommended to leave it on most of the time."><span class="text-bold">iMS</span></button><% } %>
|
||||
<% if (device == 'xdr') { %><button id="data-ims" style="border-radius: 0px 30px 30px 0px;" class="tooltip" aria-label="IF+ Filter" data-tooltip="<strong>The IF+ filter increases gain by 6dB</strong>"><span class="text-bold">IF+</span></button><% } %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -173,7 +175,7 @@
|
||||
<hr class="hide-desktop">
|
||||
</div>
|
||||
|
||||
<div class="panel-33 hover-brighten" data-tooltip="This panel contains the current TX info when RDS is loaded.<br><strong>Clicking on this panel copies the info into the clipboard.</strong>">
|
||||
<div class="panel-33 hover-brighten tooltip" data-tooltip="This panel contains the current TX info when RDS is loaded.<br><strong>Clicking on this panel copies the info into the clipboard.</strong>">
|
||||
<div id="data-station-container">
|
||||
<h2 style="margin-top: 0;" class="mb-0">
|
||||
<span id="data-station-name"></span>
|
||||
@@ -228,6 +230,20 @@
|
||||
<% } %>
|
||||
<button id="users-online-container" class="hide-phone" aria-label="Online users"><i class="fa-solid fa-user"></i> <span class="users-online"></span></button>
|
||||
|
||||
<% if (isAdminAuthenticated) { %>
|
||||
<div class="admin-quick-dashboard hide-phone">
|
||||
<div class="icon tooltip <% if (tunerLock) { %>active<% } %>" id="dashboard-lock-admin" onClick="toggleAdminLock()" data-tooltip="Toggle admin lock<br>Lasts until restart">
|
||||
<i class="fa-solid fa-lock"></i>
|
||||
</div>
|
||||
<div class="icon tooltip <% if (!publicTuner) { %>active<% } %>" id="dashboard-lock-tune" onClick="togglePasswordLock()" data-tooltip="Toggle password lock<br>Lasts until restart">
|
||||
<i class="fa-solid fa-key"></i>
|
||||
</div>
|
||||
<div class="icon tooltip" data-tooltip="Go to admin menu" onClick="window.open('./setup', '_blank').focus();">
|
||||
<i class="fa-solid fa-user"></i>
|
||||
</div>
|
||||
</div>
|
||||
<% } %>
|
||||
|
||||
<div id="myModal" class="modal">
|
||||
<div class="modal-panel">
|
||||
<div class="flex-container flex-phone" style="height: calc(100% - 100px)">
|
||||
@@ -285,7 +301,7 @@
|
||||
|
||||
<% if (isAdminAuthenticated) { %>
|
||||
<p>You are logged in as an adminstrator.<br>
|
||||
<a href="./setup">Setup</a> • <a class="logout-link" href="#">Logout</a>
|
||||
<a href="./setup" target="_blank">Setup</a> • <a class="logout-link" href="#">Logout</a>
|
||||
</p>
|
||||
<% } else if (isTuneAuthenticated) { %>
|
||||
<p>You are logged in and can control the receiver.<br><a class="logout-link" href="#">Logout</a></p>
|
||||
@@ -360,9 +376,5 @@
|
||||
</div>
|
||||
|
||||
<script src="js/webserver.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.4.1/html2canvas.min.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/screenshot-capture/2.0.0/screenshot-capture.min.js"></script>
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -45,6 +45,8 @@ function submitData() {
|
||||
return $(this).text() === $('#device-type').val();
|
||||
}).data('value') || "tef");
|
||||
|
||||
const softwareMode = $('#audio-software-mode').is("checked") || false;
|
||||
|
||||
const tunerName = $('#webserver-name').val() || 'FM Tuner';
|
||||
const tunerDesc = $('#webserver-desc').val() || 'Default FM tuner description';
|
||||
const broadcastTuner = $("#broadcast-tuner").is(":checked");
|
||||
@@ -84,6 +86,7 @@ function submitData() {
|
||||
audioDevice,
|
||||
audioChannels,
|
||||
audioBitrate,
|
||||
softwareMode,
|
||||
},
|
||||
identification: {
|
||||
tunerName,
|
||||
@@ -202,6 +205,8 @@ function submitData() {
|
||||
$("#audio-quality").val(selectedQuality.text());
|
||||
}
|
||||
|
||||
$('#audio-software-switch').prop("checked", data.audio.softwareMode || false);
|
||||
|
||||
$('#webserver-name').val(data.identification.tunerName);
|
||||
$('#webserver-desc').val(data.identification.tunerDesc);
|
||||
$("#broadcast-tuner").prop("checked", data.identification.broadcastTuner);
|
||||
|
||||
@@ -28,6 +28,21 @@ const usa_programmes = [
|
||||
$(document).ready(function () {
|
||||
var canvas = $('#signal-canvas')[0];
|
||||
|
||||
var $panel = $('.admin-quick-dashboard');
|
||||
var panelWidth = $panel.outerWidth();
|
||||
|
||||
$(document).mousemove(function(e) {
|
||||
var mouseX = e.pageX;
|
||||
var panelLeft = parseInt($panel.css('left'));
|
||||
|
||||
if (mouseX <= 10 || (panelLeft === 4 && mouseX <= 100)) {
|
||||
$panel.css('left', '4px');
|
||||
} else {
|
||||
$panel.css('left', -panelWidth);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
canvas.width = canvas.parentElement.clientWidth;
|
||||
canvas.height = canvas.parentElement.clientHeight;
|
||||
|
||||
@@ -49,9 +64,9 @@ $(document).ready(function () {
|
||||
const textInput = $('#commandinput');
|
||||
|
||||
textInput.on('change', function (event) {
|
||||
const inputValue = textInput.val();
|
||||
const inputValue = Number(textInput.val());
|
||||
// Check if the user agent contains 'iPhone'
|
||||
if (/iPhone/i.test(navigator.userAgent) && socket.readyState === WebSocket.OPEN) {
|
||||
if (/iPhone/i.test(navigator.userAgent)) {
|
||||
socket.send("T" + (inputValue * 1000));
|
||||
// Clear the input field if needed
|
||||
textInput.val('');
|
||||
@@ -385,25 +400,6 @@ function checkKey(e) {
|
||||
tuneTo(Number(currentFreq));
|
||||
break;
|
||||
case 83: // Screenshot (S key)
|
||||
screenshotCapture.capture().then(function (dataUrl) {
|
||||
// Create an input element to hold the data URL temporarily
|
||||
var aux = $('<input>').attr({
|
||||
type: 'text',
|
||||
value: dataUrl
|
||||
});
|
||||
|
||||
// Append the input element to the body, select its contents, and copy them to the clipboard
|
||||
$('body').append(aux);
|
||||
aux.select();
|
||||
document.execCommand('copy');
|
||||
aux.remove();
|
||||
|
||||
// Alert the user that the screenshot has been copied to the clipboard
|
||||
alert('Screenshot copied to clipboard!');
|
||||
}).catch(function (error) {
|
||||
console.error('Error capturing screenshot:', error);
|
||||
});
|
||||
|
||||
break;
|
||||
case 38:
|
||||
socket.send("T" + (Math.round(currentFreq*1000) + ((currentFreq > 30) ? 10 : 1)));
|
||||
@@ -758,19 +754,51 @@ function toggleForcedStereo() {
|
||||
socket.send(message);
|
||||
}
|
||||
|
||||
function toggleAdminLock() {
|
||||
let $adminLockButton = $('#dashboard-lock-admin');
|
||||
|
||||
if($adminLockButton.hasClass('active')) {
|
||||
socket.send('wL0');
|
||||
$adminLockButton.removeClass('active');
|
||||
} else {
|
||||
socket.send('wL1');
|
||||
$adminLockButton.addClass('active');
|
||||
}
|
||||
}
|
||||
|
||||
function togglePasswordLock() {
|
||||
let $passwordLockButton = $('#dashboard-lock-tune');
|
||||
|
||||
if($passwordLockButton.hasClass('active')) {
|
||||
socket.send('wT0');
|
||||
$passwordLockButton.removeClass('active');
|
||||
} else {
|
||||
socket.send('wT1');
|
||||
$passwordLockButton.addClass('active');
|
||||
}
|
||||
}
|
||||
|
||||
function initTooltips() {
|
||||
$('[data-tooltip]').hover(function(e){
|
||||
$('.tooltip').hover(function(e){
|
||||
var tooltipText = $(this).data('tooltip');
|
||||
|
||||
// Add a delay of 500 milliseconds before creating and appending the tooltip
|
||||
$(this).data('timeout', setTimeout(() => {
|
||||
var tooltip = $('<div class="tooltiptext"></div>').html(tooltipText);
|
||||
$('body').append(tooltip);
|
||||
|
||||
var posX = e.pageX;
|
||||
var posY = e.pageY;
|
||||
|
||||
var tooltipWidth = tooltip.outerWidth();
|
||||
var tooltipHeight = tooltip.outerHeight();
|
||||
var posX = e.pageX - tooltipWidth / 2;
|
||||
var posY = e.pageY - tooltipHeight - 10;
|
||||
|
||||
tooltip.css({ top: posY, left: posX, opacity: 0.9 });
|
||||
posX -= tooltipWidth / 2;
|
||||
posY -= tooltipHeight + 10;
|
||||
tooltip.css({ top: posY, left: posX, opacity: 1 }); // Set opacity to 1
|
||||
}, 500));
|
||||
}, function() {
|
||||
// Clear the timeout if the mouse leaves before the delay completes
|
||||
clearTimeout($(this).data('timeout'));
|
||||
$('.tooltiptext').remove();
|
||||
}).mousemove(function(e){
|
||||
var tooltipWidth = $('.tooltiptext').outerWidth();
|
||||
|
||||
@@ -3,7 +3,7 @@ var day = currentDate.getDate();
|
||||
var month = currentDate.getMonth() + 1; // Months are zero-indexed, so add 1
|
||||
var year = currentDate.getFullYear();
|
||||
var formattedDate = day + '/' + month + '/' + year;
|
||||
var currentVersion = 'v1.1.4 [' + formattedDate + ']';
|
||||
var currentVersion = 'v1.1.5 [' + formattedDate + ']';
|
||||
|
||||
|
||||
/**
|
||||
|
||||
@@ -237,6 +237,11 @@
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group checkbox bottom-20">
|
||||
<input type="checkbox" id="audio-software-switch">
|
||||
<label for="audio-software-switch">ALSA software mode (plughw) - LINUX ONLY</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -352,6 +357,7 @@
|
||||
<li class="option" data-value="tef">TEF668x / TEA685x</li>
|
||||
<li class="option" data-value="xdr">XDR (F1HD / S10HDiP)</li>
|
||||
<li class="option" data-value="sdr">SDR (RTL-SDR / AirSpy)</li>
|
||||
<li class="option" data-value="sdr">Other</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -61,6 +61,7 @@
|
||||
<li class="option" data-value="tef">TEF668x / TEA685x</li>
|
||||
<li class="option" data-value="xdr">XDR (F1HD / S10HDiP)</li>
|
||||
<li class="option" data-value="sdr">SDR (RTL-SDR / AirSpy)</li>
|
||||
<li class="option" data-value="other">Other</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user