From 3d08ee65d41a65abeed3dbeee94518adc0ebf591 Mon Sep 17 00:00:00 2001 From: Luke Berndt Date: Fri, 3 Jan 2025 11:10:11 -0500 Subject: [PATCH] tested --- backend/index.js | 159 +++++++++++++--------- prod.env.example | 2 +- utils/websocket_example/.gitignore | 1 + utils/websocket_example/index.js | 59 ++++++++ utils/websocket_example/package-lock.json | 116 ++++++++++++++++ utils/websocket_example/package.json | 14 ++ 6 files changed, 288 insertions(+), 63 deletions(-) create mode 100644 utils/websocket_example/.gitignore create mode 100644 utils/websocket_example/index.js create mode 100644 utils/websocket_example/package-lock.json create mode 100644 utils/websocket_example/package.json diff --git a/backend/index.js b/backend/index.js index 31f9730..080d023 100644 --- a/backend/index.js +++ b/backend/index.js @@ -60,20 +60,20 @@ if ((typeof mongo_user !== 'undefined') && (typeof mongo_password !== 'undefined const connect = async () => { - // Demonstrate the readyState and on event emitters - console.log(mongoose.connection.readyState); //logs 0 - mongoose.connection.on('connecting', () => { - console.log('Mongoose is connecting') - }); - mongoose.connection.on('connected', () => { - console.log('Mongoose is connected'); - }); - mongoose.connection.on('disconnecting', () => { - console.log('Mongoose is disconnecting'); - }); - - await mongoose.connect(mongoUrl, { useNewUrlParser: true, useUnifiedTopology: true, maxPoolSize: 50 }); - console.log("All Done"); + // Demonstrate the readyState and on event emitters + console.log(mongoose.connection.readyState); //logs 0 + mongoose.connection.on('connecting', () => { + console.log('Mongoose is connecting') + }); + mongoose.connection.on('connected', () => { + console.log('Mongoose is connected'); + }); + mongoose.connection.on('disconnecting', () => { + console.log('Mongoose is disconnecting'); + }); + + await mongoose.connect(mongoUrl, { useNewUrlParser: true, useUnifiedTopology: true, maxPoolSize: 50 }); + console.log("All Done"); } connect(); @@ -124,7 +124,7 @@ app.get('/:shortName/calls/newer', calls.get_newer_calls); app.get('/:shortName/calls/older', calls.get_older_calls); app.get('/:shortName/calls/date', calls.get_date_calls); app.get('/:shortName/calls/:time/older', calls.get_iphone_calls); -app.get('/:shortName/calls', calls.get_calls); +app.get('/:shortName/calls', calls.get_calls); /*------ UPLOADS ---------- upload.single('call'), uploads.upload,*/ @@ -213,40 +213,37 @@ function notify_clients(call) { var client = clients[key]; if (client.active) { if ((client.firehose == true) || (client.shortName == call.shortName.toLowerCase())) { - // if client is not filtering for stars, or if the client is filtering and the call has stars - if (!client.filterStarred || call.star) { - if (client.filterCode == "") { - sent++; - client.socket.emit("new message", JSON.stringify(call)); - } else if (client.filterType == "unit") { - var codeArray = client.filterCode.split(','); - var success = false; - for (var j = 0; j < codeArray.length; ++j) { - for (var k = 0; k < call.srcList.length; k++) { - if (codeArray[j] == call.srcList[k]) { - sent++; - client.socket.emit("new message", JSON.stringify(call)); - success = true; - break; - } - } - if (success) { + // if client + if ((client.firehose == true) || (client.filterType == "all")) { + sent++; + client.socket.emit("new message", JSON.stringify(call)); + } else if (client.filterType == "unit") { + var codeArray = client.filterCode.split(','); + var success = false; + for (var j = 0; j < codeArray.length; ++j) { + for (var k = 0; k < call.srcList.length; k++) { + if (codeArray[j] == call.srcList[k]) { + sent++; + client.socket.emit("new message", JSON.stringify(call)); + success = true; break; } } - - - } else { - var codeArray = client.talkgroupNums; - for (var j = 0; j < codeArray.length; ++j) { - if (codeArray[j] == call.talkgroupNum) { - client.socket.emit("new message", JSON.stringify(call)); - sent++ - break; - } + if (success) { + break; + } + } + } else { + var codeArray = client.talkgroupNums; + for (var j = 0; j < codeArray.length; ++j) { + if (codeArray[j] == call.talkgroupNum) { + client.socket.emit("new message", JSON.stringify(call)); + sent++ + break; } } } + } } } @@ -257,11 +254,24 @@ function notify_clients(call) { } } +function parseHeader(header) { + for (const directive of header.split(",")[0].split(";")) { + if (directive.startsWith("for=")) { + return directive.substring(4); + } + } +} + + io.sockets.on('connection', function (client) { + const header_ipAddress = parseHeader(client.handshake.headers["forwarded"] || ""); + const cf_ipAddress = client.handshake.headers["cf-connecting-ip"]; + const x_ipAddress = client.handshake.headers["x-forwarded-for"].split(",")[0]; + const ipAddress = cf_ipAddress || x_ipAddress || header_ipAddress; clients[client.id] = { socket: client, active: false }; clients[client.id].timestamp = new Date(); client.on('start', async function (data) { - if ((typeof clients[client.id] !== "undefined") && data.shortName) { + if (typeof clients[client.id] !== "undefined") { clients[client.id].active = true; clients[client.id].shortName = data.shortName.toLowerCase(); clients[client.id].filterCode = String(data.filterCode); @@ -271,19 +281,41 @@ io.sockets.on('connection', function (client) { clients[client.id].talkgroupNums = []; clients[client.id].timestamp = new Date(); clients[client.id].firehose = false; + clients[client.id].ipAddress = ipAddress; + // Validate the filterCode and filterType if (typeof clients[client.id].filterCode != "string") { - console.error("Error - Socket - Invalid filterCode: " + data.filterCode + " ShortName: " + data.shortName.toLowerCase()); + console.error("Error - Socket - Invalid filterCode: " + data.filterCode + " ShortName: " + data.shortName.toLowerCase() + " ClientID: " + client.id + " IP: " + ipAddress); delete clients[client.id]; return; } - if ((data.filterType == "firehose") && firehose_key && (typeof data.filterCode == "string") && (data.filterCode == firehose_key)) { - clients[client.id].firehose = true; - - } else if ((data.filterType == "group") && (typeof data.filterCode == "string") && (data.filterCode.indexOf(',') == -1)) { - if (!ObjectId.isValid(data.filterCode)) { - console.error("Error - Socket - Invalid Group ID: " + data.filterCode); + if (typeof clients[client.id].filterType != "string") { + console.error("Error - Socket - Invalid filterType: " + data.filterType + " ShortName: " + data.shortName.toLowerCase() + " ClientID: " + client.id + " IP: " + ipAddress); + delete clients[client.id]; + return; + } + + if ((data.filterType != "firehose") && (!data.shortName || (typeof data.shortName != "string"))) { // If it is not firehose, then it must have a shortName + console.error("Error - Socket - Invalid ShortName: " + data.shortName + " ClientID: " + client.id + " IP: " + ipAddress); + delete clients[client.id]; + return; + } + + + // Check if the filterType is firehose and the filterCode is the firehose key + if (data.filterType == "firehose") { + if (firehose_key && (typeof data.filterCode == "string") && (data.filterCode == firehose_key)) { + console.log("Enabling Firehose mode for Client: " + client.id + " IP: " + ipAddress); + clients[client.id].firehose = true; + } else { + console.error("Error - Socket - Invalid Firehose Key: " + data.filterCode + " ClientID: " + client.id + " Filter Name: " + client.filterName + " IP: " + ipAddress); + delete clients[client.id]; + return; + } + } else if ((data.filterType == "group")) { // Handle filterType == "group" + if ((typeof data.filterCode != "string") || (data.filterCode.indexOf(',') != -1) || !ObjectId.isValid(data.filterCode)) { + console.error("Error - Socket - Invalid Group ID: " + data.filterCode + " ShortName: " + data.shortName.toLowerCase() + " ClientID: " + client.id + " IP: " + ipAddress); delete clients[client.id]; return; } @@ -291,29 +323,32 @@ io.sockets.on('connection', function (client) { const group = await Group.findOne({ 'shortName': data.shortName.toLowerCase(), '_id': ObjectId.createFromHexString(data.filterCode) }); if (typeof clients[client.id] === "undefined") { - console.error("How can it be undefined here!!"); - console.error(client.id); + console.error("Error - Socket - Client not found ShortName: " + data.shortName + " ClientID: " + client.id + " IP: " + ipAddress); return; } if (group) { clients[client.id].talkgroupNums = group.talkgroups; } else { - console.error("Error - Socket: Invalid group " + data.filterCode + " Shortname: " + data.shortName + " ClientID: " + client.id); + console.error("Error - Socket - Invalid group " + data.filterCode + " Shortname: " + data.shortName + " ClientID: " + client.id + " IP: " + ipAddress); delete clients[client.id]; return; } - + // Handle filterType == "talkgroup" } else if ((data.filterType == "talkgroup") && Array.isArray(data.filterCode)) { clients[client.id].talkgroupNums = data.filterCode; + } else if ((data.filterType == "all") && (data.filterCode == "")) { + // Handle filterType == "all" + clients[client.id].talkgroupNums = []; } else { - // This is for ALL filters - //console.error("Invalid Socket Filter - Type: " + data.filterType + " Code: " + data.filterCode); + console.error("Error - Socket - Invalid Filter Type: " + data.filterType + " Filter Code: " + data.filterCode + " Filter Name: " + data.filterName + " ShortName: " + data.shortName.toLowerCase() + " ClientID: " + client.id + " IP: " + ipAddress); + delete clients[client.id]; + return; } - //console.log("[" + data.shortName.toLowerCase() + "] WebSocket Updating - Client: " + client.id + " code set to: " + data.filterCode + " type set to: " + data.filterType + " TGS: " + clients[client.id].talkgroupNums); } else { - console.error("Error - Socket.io [Start] either client not found or no Short Name"); + console.error("Error - Socket.io [Start] either client not found, shortName:" + data.shortName + " ClientID: " + client.id + " IP: " + ipAddress); } }); + client.on('stop', function (data) { if (clients[client.id]) { clients[client.id].active = false; @@ -329,15 +364,15 @@ io.sockets.on('connection', function (client) { stats.init_stats(); // This will run at 3am each day. // IF you don't set the minute to 0, it will run every minute while it is still 3, so at 3:01, 3:02... etc -schedule.scheduleJob('0 3 * * *', function() { +schedule.scheduleJob('0 3 * * *', function () { db.cleanOldCalls(); }); -schedule.scheduleJob('15 3 * * *', function() { +schedule.scheduleJob('15 3 * * *', function () { db.cleanOldEvents(); }); -schedule.scheduleJob('30 3 * * *', function() { +schedule.scheduleJob('30 3 * * *', function () { db.cleanOldPodcasts(); }); diff --git a/prod.env.example b/prod.env.example index 24ddb02..5203982 100644 --- a/prod.env.example +++ b/prod.env.example @@ -3,7 +3,7 @@ export MAILJET_SECRET="FILL IN HERE" export REACT_APP_SITE_NAME=OpenMHz export REACT_APP_ADMIN_EMAIL="admin@email.com" export DOMAIN_NAME="openmhz.com" - +export FIREHOSE_KEY="firehose_key" export S3_PROFILE='default' export S3_REGION='us-west-001' export S3_ENDPOINT='https://s3.us-west-1.wasabisys.com' diff --git a/utils/websocket_example/.gitignore b/utils/websocket_example/.gitignore new file mode 100644 index 0000000..40b878d --- /dev/null +++ b/utils/websocket_example/.gitignore @@ -0,0 +1 @@ +node_modules/ \ No newline at end of file diff --git a/utils/websocket_example/index.js b/utils/websocket_example/index.js new file mode 100644 index 0000000..6196a4c --- /dev/null +++ b/utils/websocket_example/index.js @@ -0,0 +1,59 @@ +// Install first: npm install socket.io-client +const io = require('socket.io-client'); +const serverUrl = 'http://api.openmhz.test'; // Update URL to match your server + +// Connect to the WebSocket server +const socket = io(serverUrl); // Update URL to match your server + + +// filterType can be "group", "talkgroup", "all", or "firehose" +// if filterType is "group" then filterCode is the group ID +// if filterType is "talkgroup" then filterCode is the talkgroup ID +// if filterType is "all" then filterCode is "" +// if filterType is "firehose" then filterCode is set to the FIREHOSE_KEY + +// shortName is the shortName for the System to monitor, unless it is in firehose mode, then it is set to "" +// filterStarred is ignored, so just set to false + + + +// Connection event handlers +socket.on('connect', () => { + console.log('Connected to server'); + socket.emit("start", { + filterCode: "", + filterType: "all", + filterName: "Test Client", + filterStarred: false, + shortName: "test" + }); +}); + +socket.on('disconnect', () => { + console.log('Disconnected from server'); +}); + +// Listen for incoming messages +socket.on('new message', (message) => { + message = JSON.parse(message); + const date = new Date(message.time); + console.log('\n=== Call Details ==='); + console.log(`Time: ${date.toLocaleString()}`); + console.log(`Talkgroup: ${message.talkgroupNum}`); + console.log(`System: ${message.shortName}`); + console.log(`Duration: ${message.len} seconds`); + console.log(`Frequency: ${(message.freq / 1000000).toFixed(3)} MHz`); + console.log(`Emergency: ${message.emergency ? 'Yes' : 'No'}`); + console.log('\nSource List:'); + message.srcList.forEach(src => { + console.log(`- Source ${src.src} at position ${src.pos}s`); + }); + console.log('\nAudio URL:', message.url); + console.log('==================\n'); +}); + +// Error handling +socket.on('connect_error', (error) => { + console.error('Connection error:', error); +}); + diff --git a/utils/websocket_example/package-lock.json b/utils/websocket_example/package-lock.json new file mode 100644 index 0000000..709abb6 --- /dev/null +++ b/utils/websocket_example/package-lock.json @@ -0,0 +1,116 @@ +{ + "name": "websocket_example", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "websocket_example", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "socket.io-client": "^4.8.1" + } + }, + "node_modules/@socket.io/component-emitter": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz", + "integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==" + }, + "node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/engine.io-client": { + "version": "6.6.2", + "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.6.2.tgz", + "integrity": "sha512-TAr+NKeoVTjEVW8P3iHguO1LO6RlUz9O5Y8o7EY0fU+gY1NYqas7NN3slpFtbXEsLMHk0h90fJMfKjRkQ0qUIw==", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1", + "engine.io-parser": "~5.2.1", + "ws": "~8.17.1", + "xmlhttprequest-ssl": "~2.1.1" + } + }, + "node_modules/engine.io-parser": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.3.tgz", + "integrity": "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "node_modules/socket.io-client": { + "version": "4.8.1", + "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.8.1.tgz", + "integrity": "sha512-hJVXfu3E28NmzGk8o1sHhN3om52tRvwYeidbj7xKy2eIIse5IoKX3USlS6Tqt3BHAtflLIkCQBkzVrEEfWUyYQ==", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.2", + "engine.io-client": "~6.6.1", + "socket.io-parser": "~4.2.4" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/socket.io-parser": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz", + "integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/ws": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", + "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/xmlhttprequest-ssl": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.1.2.tgz", + "integrity": "sha512-TEU+nJVUUnA4CYJFLvK5X9AOeH4KvDvhIfm0vV1GaQRtchnG0hgK5p8hw/xjv8cunWYCsiPCSDzObPyhEwq3KQ==", + "engines": { + "node": ">=0.4.0" + } + } + } +} diff --git a/utils/websocket_example/package.json b/utils/websocket_example/package.json new file mode 100644 index 0000000..52cd412 --- /dev/null +++ b/utils/websocket_example/package.json @@ -0,0 +1,14 @@ +{ + "name": "websocket_example", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "Luke Berndt", + "license": "ISC", + "dependencies": { + "socket.io-client": "^4.8.1" + } +}