Files
DRB-CnC/Server/controllers/nodesController.js
2023-07-22 03:57:50 -04:00

221 lines
8.3 KiB
JavaScript

// Debug
const { DebugBuilder } = require("../utilities/debugBuilder.js");
const log = new DebugBuilder("server", "nodesController");
// Utilities
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;
/**
*
* @param {*} req Default express req from router
* @param {*} res Defualt express res from router
*/
exports.listAllNodes = async (req, res) => {
getAllNodes((allNodes) => {
res.status(200).json({
"nodes_online": allNodes
});
});
}
/**
* 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");
try {
// Try to add the new user with defaults if missing options
const newNode = new nodeObject({
_name: req.body.name,
_ip: req.body.ip ?? null,
_port: req.body.port ?? null,
_location: req.body.location ?? null,
_nearbySystems: req.body.nearbySystems ?? null,
_online: (req.body.online == "true" || req.body.online == "True") ? true : false
});
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});
})
}
catch (err) {
// Catch any errors
if (err === "No name provided") {
return res.sendStatus(400);
}
else log.ERROR(err)
return res.sendStatus(500);
}
}
/** Get the known info for the node specified
*
* @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) => {
res.status(200).json(nodeInfo);
})
}
/** Updates the information received from the client based on ID
*
* @param {*} req Default express req from router
* @param {*} res Defualt express res from router
*/
exports.nodeCheckIn = async (req, res) => {
if (!req.body.id) return res.status(400).json("No id specified");
getNodeInfoFromId(req.body.id, (nodeInfo) => {
let checkInObject = {};
// Convert the online status to a boolean to be worked with
log.DEBUG("REQ Body: ", req.body);
var isObjectUpdated = false;
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;
isObjectUpdated = true;
}
if (req.body.port && req.body.port != nodeInfo.port) {
checkInObject._port = req.body.port;
isObjectUpdated = true;
}
if (req.body.location && req.body.location != nodeInfo.location) {
checkInObject._location = req.body.location;
isObjectUpdated = true;
}
if (req.body.nearbySystems && JSON.stringify(req.body.nearbySystems) !== JSON.stringify(nodeInfo.nearbySystems)) {
checkInObject._nearbySystems = req.body.nearbySystems;
isObjectUpdated = true;
}
if (req.body.online != nodeInfo.online || req.body.online && (req.body.online === "true") != nodeInfo.online) {
checkInObject._online = req.body.online;
isObjectUpdated = true;
}
// If no changes are made tell the client
if (!isObjectUpdated) return res.status(200).json("No keys updated");
log.INFO("Updating the following keys for ID: ", req.body.id, checkInObject);
checkInObject._id = req.body.id;
checkInObject = new nodeObject(checkInObject);
if (!nodeInfo) {
log.WARN("No existing node found with this ID, adding node: ", checkInObject);
addNewNode(checkInObject, (newNode) => {
return res.status(201).json({"updatedKeys": newNode});
});
}
else {
updateNodeInfo(checkInObject, () => {
return res.status(202).json({"updatedKeys": checkInObject});
});
}
});
}
/**
* Request the node to join the specified server/channel and listen to the specified resource
*
* @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});
}
/**
* The node monitor service, this will periodically check in on the online nodes to make sure they are still online
*/
exports.nodeMonitorService = class nodeMonitorService {
constructor() {
this.log = new DebugBuilder("server", "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));
log.INFO("Starting Node Monitor Service");
// Check in before starting the infinite loop
await this.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 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;
});
}
}