diff --git a/Server/controllers/nodesController.js b/Server/controllers/nodesController.js index 45d26e8..c1999ed 100644 --- a/Server/controllers/nodesController.js +++ b/Server/controllers/nodesController.js @@ -2,15 +2,61 @@ const { DebugBuilder } = require("../utilities/debugBuilder.js"); const log = new DebugBuilder("server", "nodesController"); // Utilities -const {getAllNodes, addNewNode, updateNodeInfo, getNodeInfoFromId, getOnlineNodes } = require("../utilities/mysqlHandler"); +const { getAllNodes, addNewNode, updateNodeInfo, getNodeInfoFromId, getOnlineNodes } = require("../utilities/mysqlHandler"); const utils = require("../utilities/utils"); const { sendHttpRequest, requestOptions } = require("../utilities/httpRequests.js"); const { nodeObject } = require("../utilities/recordHelper.js"); -const { joinServerWrapper } = require("../commands/join"); -const { leaveServerWrapper } = require("../commands/leave"); const refreshInterval = process.env.NODE_MONITOR_REFRESH_INTERVAL ?? 1200000; +/** + * Check in with a singular node, mark it offline if it's offline and + * + * @param {*} node The node Object to check in with + */ +async function checkInWithNode(node) { + const reqOptions = new requestOptions("/client/requestCheckIn", "GET", node.ip, node.port) + sendHttpRequest(reqOptions, "", (responseObj) => { + if (responseObj) { + log.DEBUG("Response from: ", node.name, responseObj); + const onlineNode = new nodeObject({ _online: false, _id: node.id }); + log.DEBUG("Node update object: ", onlineNode); + updateNodeInfo(onlineNode, (sqlResponse) => { + if (!sqlResponse) this.log.ERROR("No response from SQL object"); + + log.DEBUG("Updated node: ", sqlResponse); + return true + }) + } + else { + log.DEBUG("No response from node, assuming it's offline"); + const offlineNode = new nodeObject({ _online: false, _id: node.id }); + log.DEBUG("Offline node update object: ", offlineNode); + updateNodeInfo(offlineNode, (sqlResponse) => { + if (!sqlResponse) this.log.ERROR("No response from SQL object"); + + log.DEBUG("Updated offline node: ", sqlResponse); + return false + }) + } + }) +} +exports.checkInWithNode = checkInWithNode; + +/** + * Check in with all online nodes and mark any nodes that are actually offline +*/ +async function checkInWithOnlineNodes() { + getOnlineNodes((nodes) => { + log.DEBUG("Online Nodes: ", nodes); + for (const node of nodes) { + checkInWithNode(node); + } + return; + }); +} +exports.checkInWithOnlineNodes = checkInWithOnlineNodes; + /** * * @param {*} req Default express req from router @@ -28,7 +74,7 @@ exports.listAllNodes = async (req, res) => { * Add a new node to the storage * @param {*} req Default express req from router * @param {*} res Defualt express res from router - */ + */ exports.newNode = async (req, res) => { if (!req.body.name) return res.status(400).json("No name specified for new node"); @@ -45,7 +91,7 @@ exports.newNode = async (req, res) => { addNewNode(newNode, (newNodeObject) => { // Send back a success if the user has been added and the ID for the client to keep track of - res.status(202).json({"nodeId": newNodeObject.id}); + res.status(202).json({ "nodeId": newNodeObject.id }); }) } catch (err) { @@ -62,7 +108,7 @@ exports.newNode = async (req, res) => { * * @param {*} req Default express req from router * @param {*} res Defualt express res from router - */ + */ exports.getNodeInfo = async (req, res) => { if (!req.query.id) return res.status(400).json("No id specified"); getNodeInfoFromId(req.query.id, (nodeInfo) => { @@ -75,7 +121,7 @@ exports.getNodeInfo = async (req, res) => { * @param {*} req Default express req from router * @param {*} res Defualt express res from router */ -exports.nodeCheckIn = async (req, res) => { +exports.nodeCheckIn = async (req, res) => { if (!req.body.id) return res.status(400).json("No id specified"); getNodeInfoFromId(req.body.id, (nodeInfo) => { let checkInObject = {}; @@ -87,7 +133,7 @@ exports.nodeCheckIn = async (req, res) => { if (req.body.name && req.body.name != nodeInfo.name) { checkInObject._name = req.body.name; isObjectUpdated = true; - } + } if (req.body.ip && req.body.ip != nodeInfo.ip) { checkInObject._ip = req.body.ip; @@ -115,9 +161,9 @@ exports.nodeCheckIn = async (req, res) => { } // If no changes are made tell the client - if (!isObjectUpdated) return res.status(200).json("No keys updated"); + if (!isObjectUpdated) return res.status(200).json("No keys updated"); - log.INFO("Updating the following keys for ID: ", req.body.id, checkInObject); + log.INFO("Updating the following keys for ID: ", req.body.id, checkInObject); checkInObject._id = req.body.id; checkInObject = new nodeObject(checkInObject); @@ -125,40 +171,28 @@ exports.nodeCheckIn = async (req, res) => { if (!nodeInfo) { log.WARN("No existing node found with this ID, adding node: ", checkInObject); addNewNode(checkInObject, (newNode) => { - return res.status(201).json({"updatedKeys": newNode}); + return res.status(201).json({ "updatedKeys": newNode }); }); } - else { + else { updateNodeInfo(checkInObject, () => { - return res.status(202).json({"updatedKeys": checkInObject}); + return res.status(202).json({ "updatedKeys": checkInObject }); }); - } + } }); } /** - * Request the node to join the specified server/channel and listen to the specified resource + * Requests a specific node to check in with the server, if it's online * * @param {*} req Default express req from router * @param {*} res Defualt express res from router - * @param req.body.clientId The client ID to join discord with (NOT dev portal ID, right click user -> Copy ID) - * @param req.body.channelId The Channel ID to join in Discord - * @param req.body.presetName The preset name to listen to in Discord */ -exports.requestNodeJoinServer = async (req, res) => { - if (!req.body.clientId || !req.body.channelId || !req.body.presetName) return res.status(400).json("Missing information in request, requires clientId, channelId, presetName"); - await joinServerWrapper(req.body.presetName, req.body.channelId, req.body.clientId); -} - -/** - * - * @param {*} req Default express req from router - * @param {*} res Defualt express res from router - * @param {*} req.body.clientId The client ID to request to leave the server - */ -exports.requestNodeLeaveServer = async (req, res) => { - if (!req.body.ClientId) return res.status(400).json("Missing client ID in request"); - await leaveServerWrapper({clientId: req.body.ClientId}); +exports.requestNodeCheckIn = async (req, res) => { + if (!req.params.nodeId) return res.status(400).json("No Node ID supplied in request"); + const node = await getNodeInfoFromId(req.params.nodeId); + if (!node) return res.status(400).json("No Node with the ID given"); + checkInWithNode(node); } /** @@ -172,49 +206,21 @@ exports.nodeMonitorService = class nodeMonitorService { /** * Start the node monitor service in the background */ - async start(){ - // Wait for the a portion of the refresh period before checking in with the nodes, so the rest of the bot can start - await new Promise(resolve => setTimeout(resolve, refreshInterval/10)); + async start() { + // Wait for the a portion of the refresh period before checking in with the nodes, so the rest of the bot can start + await new Promise(resolve => setTimeout(resolve, refreshInterval / 10)); log.INFO("Starting Node Monitor Service"); // Check in before starting the infinite loop - await this.checkInWithOnlineNodes(); - - - while(true){ + await checkInWithOnlineNodes(); + + + while (true) { // Wait for the refresh interval, then wait for the posts to return, then wait a quarter of the refresh interval to make sure everything is cleared up await new Promise(resolve => setTimeout(resolve, refreshInterval)); - await this.checkInWithOnlineNodes(); + await checkInWithOnlineNodes(); await new Promise(resolve => setTimeout(resolve, refreshInterval / 4)); continue; - } - } - - /** - * Check in with all online nodes and mark any nodes that are actually offline - */ - async checkInWithOnlineNodes(){ - getOnlineNodes((nodes) => { - this.log.DEBUG("Online Nodes: ", nodes); - for (const node of nodes) { - const reqOptions = new requestOptions("/client/requestCheckIn", "GET", node.ip, node.port) - sendHttpRequest(reqOptions, "", (responseObj) => { - if (responseObj) { - this.log.DEBUG("Response from: ", node.name, responseObj); - } - else { - this.log.DEBUG("No response from node, assuming it's offline"); - const offlineNode = new nodeObject({ _online: 0, _id: node.id }); - this.log.DEBUG("Offline node update object: ", offlineNode); - updateNodeInfo(offlineNode, (sqlResponse) => { - if (!sqlResponse) this.log.ERROR("No response from SQL object"); - - this.log.DEBUG("Updated offline node: ", sqlResponse); - }) - } - }) - } - return; - }); + } } } diff --git a/Server/public/res/css/main.css b/Server/public/res/css/main.css index d057e2a..6384148 100644 --- a/Server/public/res/css/main.css +++ b/Server/public/res/css/main.css @@ -139,4 +139,38 @@ a { /* Global Section */ .sidebar-container { min-height: 94.2vh; -} \ No newline at end of file +} + +/* User table section */ + +.label { + border-radius: 3px; + font-size: 1.1em; + font-weight: 600; +} + +.user-list tbody td .user-subhead { + font-size: 1em; + font-style: italic; +} + +.table thead tr th { + text-transform: uppercase; + font-size: 0.875em; +} + +.table thead tr th { + border-bottom: 2px solid #e7ebee; +} + +.table tbody tr td:first-child { + font-size: 1.125em; + font-weight: 300; +} + +.table tbody tr td { + font-size: 0.875em; + vertical-align: middle; + border-top: 1px solid #e7ebee; + padding: 12px 8px; +} diff --git a/Server/public/res/js/node.js b/Server/public/res/js/node.js new file mode 100644 index 0000000..a1530f4 --- /dev/null +++ b/Server/public/res/js/node.js @@ -0,0 +1,50 @@ +function addFrequencyInput(system){ + // Create new input + var icon = document.createElement('i'); + icon.classList.add('bi'); + icon.classList.add('bi-x-circle'); + icon.classList.add('text-black'); + + var remove = document.createElement('a'); + remove.classList.add('align-middle'); + remove.classList.add('float-left'); + remove.href = '#' + remove.appendChild(icon); + + var childColRemoveIcon = document.createElement('div'); + childColRemoveIcon.classList.add('col-2'); + childColRemoveIcon.appendChild(remove); + + var input = document.createElement('input'); + input.classList.add('form-control'); + input.id = 'nodeFreq'; + input.type = 'text'; + + var childColInput = document.createElement('div'); + childColInput.classList.add('col-10'); + childColInput.appendChild(input); + + var childRow = document.createElement('div'); + childRow.classList.add("row"); + childRow.classList.add("px-1"); + childRow.appendChild(childColInput); + childRow.appendChild(childColRemoveIcon); + + var colParent = document.createElement('div'); + colParent.classList.add("col-md-6"); + colParent.classList.add("mb-1"); + colParent.appendChild(childRow); + + document.getElementById(`frequencyRow_${system.replaceAll(" ", "_")}`).appendChild(colParent); +} + +function checkInByNodeId(nodeId){ + const Http = new XMLHttpRequest(); + const url='/nodes/'+nodeId; + Http.open("GET", url); + Http.send(); + + Http.onreadystatechange = (e) => { + console.log(Http.responseText) + } +} \ No newline at end of file diff --git a/Server/routes/nodes.js b/Server/routes/nodes.js index b90e8db..773bc45 100644 --- a/Server/routes/nodes.js +++ b/Server/routes/nodes.js @@ -28,8 +28,7 @@ router.get('/nodeInfo', nodesController.getNodeInfo); // Client checkin with the server to update information router.post('/nodeCheckIn', nodesController.nodeCheckIn); -// TODO Need to authenticate this request -// Request a particular client to join a particular channel listening to a particular preset -router.post('/joinServer', nodesController.requestNodeJoinServer); +// Request a node to check in with the server +router.get('/:nodeId', nodesController.requestNodeCheckIn); module.exports = router; diff --git a/Server/utilities/recordHelper.js b/Server/utilities/recordHelper.js index 5464374..eb26ae3 100644 --- a/Server/utilities/recordHelper.js +++ b/Server/utilities/recordHelper.js @@ -118,8 +118,7 @@ class nodeObject { this.ip = _ip; this.port = _port; this.location = _location; - this.nearbySystems = _nearbySystems; - if (this.nearbySystems) this.presets = Object.keys(_nearbySystems); + this.nearbySystems = _nearbySystems; this.online = _online; } } diff --git a/Server/views/node.ejs b/Server/views/node.ejs index 941e326..05cd447 100644 --- a/Server/views/node.ejs +++ b/Server/views/node.ejs @@ -1,41 +1,154 @@ <%- include('partials/htmlHead.ejs') %> -
-
- Account Details - <% if(node.online){%> Online - <% } else {%> Offline - <% } %> +
+
+
+

+ + Node Details + +

+
+
+
+
+
+ +
+
+ + +
+
+ + <% if(node.online){%> Online + <% } else {%> Offline + <% } %> +
+ + Join Server + + Leave Server + + Check-in with Node +
+
+ +
+
+ + +
+
+
+
+ + +
+
+ + +
+
+
+ + +
+

+ Nearby Systems +

+
+
+
+
+
+
+ + + + + + + + + + + <% for(const system in node.nearbySystems){ %> + + + + + + + <% // Update system modal %> + <%- include("partials/modifySystemModal.ejs", {'system': system, 'frequencies': node.nearbySystems[system].frequencies}) %> + <% } %> + +
System NameFrequenciesProtocol 
+ <%= system %> + + <% if(node.nearbySystems[system].frequencies.length> 1) { %> +
    + <% for(const frequency of + node.nearbySystems[system].frequencies) { %> +
  • + <%=frequency%> MHz +
  • + <% } %> +
+ <% } else { const + frequency=node.nearbySystems[system].frequencies[0] + %> + <%=frequency%> MHz + <% } %> +
+ + <%= node.nearbySystems[system].mode %> + + + "> + + + + + +
+
+
+
+
+
+ + + + +
+
+
-
-
-
- - -
-
-
- - -
-
-
-
- - + + <% // new System Modal %> + <%- include('partials/bodyEnd.ejs') %> + <%- include('partials/htmlFooter.ejs') %> \ No newline at end of file diff --git a/Server/views/partials/bodyEnd.ejs b/Server/views/partials/bodyEnd.ejs index 2a347cf..992066b 100644 --- a/Server/views/partials/bodyEnd.ejs +++ b/Server/views/partials/bodyEnd.ejs @@ -7,5 +7,6 @@ integrity="sha384-geWF76RCwLtnZ8qwWowPQNguL3RmwHVBC9FhGdlKrxdiJJigb/j/68SIy3Te4Bkz" crossorigin="anonymous"> - \ No newline at end of file + \ No newline at end of file diff --git a/Server/views/partials/modifySystemModal.ejs b/Server/views/partials/modifySystemModal.ejs new file mode 100644 index 0000000..d24e83f --- /dev/null +++ b/Server/views/partials/modifySystemModal.ejs @@ -0,0 +1,54 @@ + \ No newline at end of file diff --git a/Server/views/partials/navbar.ejs b/Server/views/partials/navbar.ejs index ccac10e..169185f 100644 --- a/Server/views/partials/navbar.ejs +++ b/Server/views/partials/navbar.ejs @@ -1,12 +1,13 @@