Implement Node Monitor Service
- Check in with online nodes every n ms - Update nodes that do not reply - Added node object record helper - Updated mysql wrapper for updating node info to accept bool or number
This commit is contained in:
@@ -2,31 +2,42 @@
|
|||||||
const { DebugBuilder } = require("../utilities/debugBuilder.js");
|
const { DebugBuilder } = require("../utilities/debugBuilder.js");
|
||||||
const log = new DebugBuilder("server", "nodesController");
|
const log = new DebugBuilder("server", "nodesController");
|
||||||
// Utilities
|
// Utilities
|
||||||
const mysqlHander = require("../utilities/mysqlHandler");
|
const {getAllNodes, addNewNode, updateNodeInfo, getNodeInfoFromId, getOnlineNodes } = require("../utilities/mysqlHandler");
|
||||||
const utils = require("../utilities/utils");
|
const utils = require("../utilities/utils");
|
||||||
|
const { sendHttpRequest, requestOptions } = require("../utilities/httpRequests.js");
|
||||||
|
const { nodeObject } = require("../utilities/recordHelper.js");
|
||||||
|
|
||||||
|
const refreshInterval = process.env.NODE_MONITOR_REFRESH_INTERVAL ?? 1200000;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {*} req
|
||||||
|
* @param {*} res
|
||||||
|
*/
|
||||||
exports.listAllNodes = async (req, res) => {
|
exports.listAllNodes = async (req, res) => {
|
||||||
mysqlHander.getAllNodes((allNodes) => {
|
getAllNodes((allNodes) => {
|
||||||
res.status(200).json({
|
res.status(200).json({
|
||||||
"nodes_online": allNodes
|
"nodes_online": allNodes
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add a new node to the
|
// Add a new node to the storage
|
||||||
exports.newNode = async (req, res) => {
|
exports.newNode = async (req, res) => {
|
||||||
if (!req.body.name) return res.send(400)
|
if (!req.body.name) return res.send(400)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Try to add the new user with defaults if missing options
|
// Try to add the new user with defaults if missing options
|
||||||
mysqlHander.addNewNode({
|
const newNode = new nodeObject({
|
||||||
'name': req.body.name,
|
_name: req.body.name,
|
||||||
'ip': req.body.ip ?? null,
|
_ip: req.body.ip ?? null,
|
||||||
'port': req.body.port ?? null,
|
_port: req.body.port ?? null,
|
||||||
'location': req.body.location ?? null,
|
_location: req.body.location ?? null,
|
||||||
'nearbySystems': req.body.nearbySystems ?? null,
|
_nearbySystems: req.body.nearbySystems ?? null,
|
||||||
'online': req.body.online ?? 0
|
_online: req.body.online ?? 0
|
||||||
}, (queryResults) => {
|
});
|
||||||
|
|
||||||
|
addNewNode(newNode, (queryResults) => {
|
||||||
// Send back a success if the user has been added and the ID for the client to keep track of
|
// 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": queryResults.insertId});
|
res.status(202).json({"nodeId": queryResults.insertId});
|
||||||
})
|
})
|
||||||
@@ -44,7 +55,7 @@ exports.newNode = async (req, res) => {
|
|||||||
// Get the known info for the node specified
|
// Get the known info for the node specified
|
||||||
exports.getNodeInfo = async (req, res) => {
|
exports.getNodeInfo = async (req, res) => {
|
||||||
if (!req.query.id) return res.status(400).json("No id specified");
|
if (!req.query.id) return res.status(400).json("No id specified");
|
||||||
mysqlHander.getNodeInfoFromId(req.query.id, (nodeInfo) => {
|
getNodeInfoFromId(req.query.id, (nodeInfo) => {
|
||||||
res.status(200).json(nodeInfo);
|
res.status(200).json(nodeInfo);
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -52,29 +63,77 @@ exports.getNodeInfo = async (req, res) => {
|
|||||||
// Updates the information received from the client based on ID
|
// Updates the information received from the client based on ID
|
||||||
exports.nodeCheckIn = async (req, res) => {
|
exports.nodeCheckIn = async (req, res) => {
|
||||||
if (!req.body.id) return res.status(400).json("No id specified");
|
if (!req.body.id) return res.status(400).json("No id specified");
|
||||||
mysqlHander.getNodeInfoFromId(req.body.id, (nodeInfo) => {
|
getNodeInfoFromId(req.body.id, (nodeInfo) => {
|
||||||
let nodeObject = {};
|
let checkInObject = new nodeObject();
|
||||||
// Convert the DB systems buffer to a JSON object to be worked with
|
// Convert the DB systems buffer to a JSON object to be worked with
|
||||||
nodeInfo.nearbySystems = utils.BufferToJson(nodeInfo.nearbySystems)
|
nodeInfo.nearbySystems = utils.BufferToJson(nodeInfo.nearbySystems)
|
||||||
// Convert the online status to a boolean to be worked with
|
// Convert the online status to a boolean to be worked with
|
||||||
nodeInfo.online = nodeInfo.online !== 0;
|
nodeInfo.online = nodeInfo.online !== 0;
|
||||||
|
|
||||||
if (req.body.name && req.body.name !== nodeInfo.name) nodeObject.name = req.body.name
|
if (req.body.name && req.body.name !== nodeInfo.name) checkInObject.name = req.body.name
|
||||||
if (req.body.ip && req.body.ip !== nodeInfo.ip) nodeObject.ip = req.body.ip
|
if (req.body.ip && req.body.ip !== nodeInfo.ip) checkInObject.ip = req.body.ip
|
||||||
if (req.body.port && req.body.port !== nodeInfo.port) nodeObject.port = req.body.port
|
if (req.body.port && req.body.port !== nodeInfo.port) checkInObject.port = req.body.port
|
||||||
if (req.body.location && req.body.location !== nodeInfo.location) nodeObject.location = req.body.location
|
if (req.body.location && req.body.location !== nodeInfo.location) checkInObject.location = req.body.location
|
||||||
if (req.body.nearbySystems && JSON.stringify(req.body.nearbySystems) !== JSON.stringify(nodeInfo.nearbySystems)) nodeObject.nearbySystems = req.body.nearbySystems
|
if (req.body.nearbySystems && JSON.stringify(req.body.nearbySystems) !== JSON.stringify(nodeInfo.nearbySystems)) checkInObject.nearbySystems = req.body.nearbySystems
|
||||||
if (req.body.online && req.body.online !== nodeInfo.online) nodeObject.online = req.body.online
|
if (req.body.online && req.body.online !== nodeInfo.online) checkInObject.online = req.body.online
|
||||||
|
|
||||||
// If no changes are made tell the client
|
// If no changes are made tell the client
|
||||||
if (Object.keys(nodeObject).length === 0) return res.status(200).json("No keys updated");
|
if (Object.keys(checkInObject).length === 0) return res.status(200).json("No keys updated");
|
||||||
|
|
||||||
log.INFO("Updating the following keys for ID: ", req.body.id, nodeObject);
|
log.INFO("Updating the following keys for ID: ", req.body.id, checkInObject);
|
||||||
// Adding the ID key to the body so that the client can double-check their ID
|
// Adding the ID key to the body so that the client can double-check their ID
|
||||||
nodeObject.id = req.body.id;
|
checkInObject.id = req.body.id;
|
||||||
mysqlHander.updateNodeInfo(nodeObject, () => {
|
updateNodeInfo(checkInObject, () => {
|
||||||
return res.status(202).json({"updatedKeys": nodeObject});
|
return res.status(202).json({"updatedKeys": checkInObject});
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
exports.nodeMonitorService = class nodeMonitorService {
|
||||||
|
constructor() {
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async checkInWithOnlineNodes(){
|
||||||
|
getOnlineNodes((nodes) => {
|
||||||
|
log.DEBUG("Online Nodes: ", nodes);
|
||||||
|
for (const node of nodes) {
|
||||||
|
const reqOptions = new requestOptions("/client/requestCheckIn", "GET", node.ip, node.port)
|
||||||
|
const request = sendHttpRequest(reqOptions, "", (responseObj) => {
|
||||||
|
if (responseObj) {
|
||||||
|
log.DEBUG("Response from: ", node.name, responseObj);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
log.DEBUG("No response from node, assuming it's offline");
|
||||||
|
const offlineNode = new nodeObject({ _online: 0, _id: node.id });
|
||||||
|
log.DEBUG("Offline node update object: ", offlineNode);
|
||||||
|
updateNodeInfo(offlineNode, (sqlResponse) => {
|
||||||
|
if (!sqlResponse) log.ERROR("No response from SQL object");
|
||||||
|
|
||||||
|
log.DEBUG("Updated node: ", sqlResponse);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,20 +1,21 @@
|
|||||||
|
// Modules
|
||||||
var createError = require('http-errors');
|
var createError = require('http-errors');
|
||||||
var express = require('express');
|
var express = require('express');
|
||||||
var path = require('path');
|
var path = require('path');
|
||||||
var cookieParser = require('cookie-parser');
|
var cookieParser = require('cookie-parser');
|
||||||
var logger = require('morgan');
|
var logger = require('morgan');
|
||||||
var http = require('http');
|
var http = require('http');
|
||||||
|
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
require('dotenv').config();
|
require('dotenv').config();
|
||||||
|
// Utilities
|
||||||
const { RSSController } = require("./controllers/rssController");
|
const { RSSController } = require("./controllers/rssController");
|
||||||
const libUtils = require("./libUtils");
|
const libUtils = require("./libUtils");
|
||||||
const deployCommands = require("./utilities/deployCommands");
|
const deployCommands = require("./utilities/deployCommands");
|
||||||
|
const { nodeMonitorService } = require('./controllers/nodesController');
|
||||||
|
// Debug
|
||||||
const { DebugBuilder } = require("./utilities/debugBuilder");
|
const { DebugBuilder } = require("./utilities/debugBuilder");
|
||||||
const log = new DebugBuilder("server", "index");
|
const log = new DebugBuilder("server", "index");
|
||||||
|
|
||||||
//const Discord = require('discord.js');Client, Collection, Intents
|
|
||||||
const {
|
const {
|
||||||
Client,
|
Client,
|
||||||
Events,
|
Events,
|
||||||
@@ -23,7 +24,6 @@ const {
|
|||||||
MessageActionRow,
|
MessageActionRow,
|
||||||
MessageButton
|
MessageButton
|
||||||
} = require('discord.js');
|
} = require('discord.js');
|
||||||
//const client = new Discord.Client();
|
|
||||||
|
|
||||||
const client = new Client({
|
const client = new Client({
|
||||||
intents: [GatewayIntentBits.GuildMessages, GatewayIntentBits.Guilds]
|
intents: [GatewayIntentBits.GuildMessages, GatewayIntentBits.Guilds]
|
||||||
@@ -92,6 +92,14 @@ async function runHTTPServer() {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start the node monitoring service
|
||||||
|
*/
|
||||||
|
async function runNodeMonitorService(){
|
||||||
|
const monitor = new nodeMonitorService();
|
||||||
|
monitor.start();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Start the RSS background process
|
* Start the RSS background process
|
||||||
*/
|
*/
|
||||||
@@ -128,6 +136,9 @@ client.on('ready', () => {
|
|||||||
log.DEBUG(`Starting HTTP Server`);
|
log.DEBUG(`Starting HTTP Server`);
|
||||||
runHTTPServer();
|
runHTTPServer();
|
||||||
|
|
||||||
|
log.DEBUG("Starting Node Monitoring Service");
|
||||||
|
runNodeMonitorService();
|
||||||
|
|
||||||
log.DEBUG("Starting RSS watcher");
|
log.DEBUG("Starting RSS watcher");
|
||||||
runRssService();
|
runRssService();
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -39,30 +39,22 @@ exports.sendHttpRequest = function sendHttpRequest(requestOptions, data, callbac
|
|||||||
res.on('data', (data) => {
|
res.on('data', (data) => {
|
||||||
const responseObject = {
|
const responseObject = {
|
||||||
"statusCode": res.statusCode,
|
"statusCode": res.statusCode,
|
||||||
"body": data
|
"body": (requestOptions.method === "POST") ? JSON.parse(data) : data.toString()
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
|
||||||
responseObject.body = JSON.parse(responseObject.body)
|
|
||||||
}
|
|
||||||
catch (err) {
|
|
||||||
}
|
|
||||||
|
|
||||||
log.DEBUG("Response Object: ", responseObject);
|
log.DEBUG("Response Object: ", responseObject);
|
||||||
return callback(responseObject);
|
callback(responseObject);
|
||||||
})
|
})
|
||||||
}).on('error', err => {
|
}).on('error', err => {
|
||||||
log.ERROR('Error: ', err.message)
|
if (err.code === "ECONNREFUSED"){
|
||||||
|
// Bot refused connection, assumed offline
|
||||||
|
log.WARN("Connection Refused");
|
||||||
|
}
|
||||||
|
else log.ERROR('Error: ', err.message, err);
|
||||||
|
callback(undefined);
|
||||||
// TODO need to handle if the server is down
|
// TODO need to handle if the server is down
|
||||||
})
|
})
|
||||||
|
|
||||||
if (requestOptions.timeout) {
|
|
||||||
req.setTimeout(requestOptions.timeout, () => {
|
|
||||||
return callback(false);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write the data to the request and send it
|
// Write the data to the request and send it
|
||||||
req.write(data)
|
req.write(data);
|
||||||
req.end()
|
req.end();
|
||||||
}
|
}
|
||||||
@@ -68,6 +68,7 @@ exports.addNewNode = (nodeObject, callback) => {
|
|||||||
* @param callback Callback function
|
* @param callback Callback function
|
||||||
*/
|
*/
|
||||||
exports.updateNodeInfo = (nodeObject, callback) => {
|
exports.updateNodeInfo = (nodeObject, callback) => {
|
||||||
|
if(!nodeObject.id) throw new Error("Attempted to updated node without providing ID", nodeObject);
|
||||||
const name = nodeObject.name,
|
const name = nodeObject.name,
|
||||||
ip = nodeObject.ip,
|
ip = nodeObject.ip,
|
||||||
port = nodeObject.port,
|
port = nodeObject.port,
|
||||||
@@ -84,8 +85,8 @@ exports.updateNodeInfo = (nodeObject, callback) => {
|
|||||||
nearbySystems = utils.JsonToBuffer(nearbySystems)
|
nearbySystems = utils.JsonToBuffer(nearbySystems)
|
||||||
queryParams.push(`nearbySystems = '${nearbySystems}'`);
|
queryParams.push(`nearbySystems = '${nearbySystems}'`);
|
||||||
}
|
}
|
||||||
if (typeof online === "boolean") {
|
if (typeof online === "boolean" || typeof online === "number") {
|
||||||
if (online) queryParams.push(`online = 1`);
|
if (online || online === 1) queryParams.push(`online = 1`);
|
||||||
else queryParams.push(`online = 0`);
|
else queryParams.push(`online = 0`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -96,4 +96,31 @@ class BaseUserAccount {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.BaseUserAccount = BaseUserAccount;
|
exports.BaseUserAccount = BaseUserAccount;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
class nodeObject {
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {*} param0._id The ID of the node
|
||||||
|
* @param {*} param0._name The name of the node
|
||||||
|
* @param {*} param0._ip The IP that the master can contact the node at
|
||||||
|
* @param {*} param0._port The port that the client is listening on
|
||||||
|
* @param {*} param0._location The physical location of the node
|
||||||
|
* @param {*} param0._online An integer representation of the online status of the bot, ie 0=off, 1=on
|
||||||
|
* @param {*} param0._nearbySystems An object array of nearby systems
|
||||||
|
*/
|
||||||
|
constructor({ _id = null, _name = null, _ip = null, _port = null, _location = null, _nearbySystems = null, _online = null }) {
|
||||||
|
this.id = _id;
|
||||||
|
this.name = _name;
|
||||||
|
this.ip = _ip;
|
||||||
|
this.port = _port;
|
||||||
|
this.location = _location;
|
||||||
|
this.nearbySystems = _nearbySystems;
|
||||||
|
this.online = _online;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.nodeObject = nodeObject;
|
||||||
Reference in New Issue
Block a user