From 809e6ee58fe40f9b2247893fa96fdf67db4944f8 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Wed, 14 Aug 2024 10:20:35 -0700 Subject: [PATCH] Refactored notifications from Check model to NetworkService --- Server/db/mongo/modules/monitorModule.js | 63 +----------------------- Server/index.js | 4 +- Server/models/Check.js | 57 --------------------- Server/service/jobQueue.js | 6 +-- Server/service/networkService.js | 58 +++++++++++++++++++--- 5 files changed, 59 insertions(+), 129 deletions(-) diff --git a/Server/db/mongo/modules/monitorModule.js b/Server/db/mongo/modules/monitorModule.js index 1d53db978..491106a17 100644 --- a/Server/db/mongo/modules/monitorModule.js +++ b/Server/db/mongo/modules/monitorModule.js @@ -263,69 +263,10 @@ const getMonitorStatsById = async (req) => { * @returns {Promise} * @throws {Error} */ -const getMonitorById = async (req, res) => { +const getMonitorById = async (monitorId) => { try { - const { monitorId } = req.params; - let { status, limit, sortOrder, filter, numToDisplay, normalize } = - req.query; - - const filterLookup = { - day: new Date(new Date().setDate(new Date().getDate() - 1)), - week: new Date(new Date().setDate(new Date().getDate() - 7)), - month: new Date(new Date().setMonth(new Date().getMonth() - 1)), - }; - - // This effectively removes limit, returning all checks - if (limit === undefined) limit = 0; - - // Default sort order is newest -> oldest - if (sortOrder === "asc") { - sortOrder = 1; - } else if (sortOrder === "desc") { - sortOrder = -1; - } else sortOrder = -1; - const monitor = await Monitor.findById(monitorId); - - const checksQuery = { monitorId: monitor._id }; - - if (status !== undefined) { - checksQuery.status = status; - } - - // Filter checks by "day", "week", or "month" - if (filter !== undefined) { - checksQuery.createdAt = { $gte: filterLookup[filter] }; - } - - // Determine model type - let model = - monitor.type === "http" || monitor.type === "ping" - ? Check - : PageSpeedCheck; - - let checks = await model - .find(checksQuery) - .sort({ - createdAt: sortOrder, - }) - .limit(limit); - - // If more than numToDisplay checks, pick every nth check - if (numToDisplay !== undefined && checks && checks.length > numToDisplay) { - const n = Math.ceil(checks.length / numToDisplay); - checks = checks.filter( - (_, index) => index % n === 0 || index === checks.length - 1 - ); - } - - // Normalize checks if requested - if (normalize !== undefined) { - checks = NormalizeData(checks, 1, 100); - } - const notifications = await Notification.find({ monitorId: monitor._id }); - const monitorWithChecks = { ...monitor.toObject(), checks, notifications }; - return monitorWithChecks; + return monitor; } catch (error) { throw error; } diff --git a/Server/index.js b/Server/index.js index 1eb5d886b..110057987 100644 --- a/Server/index.js +++ b/Server/index.js @@ -16,6 +16,7 @@ const maintenanceWindowRouter = require("./routes/maintenanceWindowRoute"); const { connectDbAndRunServer } = require("./configs/db"); const queueRouter = require("./routes/queueRoute"); const JobQueue = require("./service/jobQueue"); +const NetworkService = require("./service/networkService"); const EmailService = require("./service/emailService"); const PageSpeedService = require("./service/pageSpeedService"); @@ -114,8 +115,9 @@ const startApp = async () => { // Create services await connectDbAndRunServer(app, db); - const jobQueue = await JobQueue.createJobQueue(db); const emailService = new EmailService(); + const networkService = new NetworkService(db, emailService); + const jobQueue = await JobQueue.createJobQueue(db, networkService); const pageSpeedService = new PageSpeedService(); const cleanup = async () => { diff --git a/Server/models/Check.js b/Server/models/Check.js index a9ac7f288..55bb2d988 100644 --- a/Server/models/Check.js +++ b/Server/models/Check.js @@ -69,61 +69,4 @@ const CheckSchema = mongoose.Schema( } ); -/** - * Pre-save middleware to handle status change notifications. - * - * This middleware checks if the status of the monitor associated - * with the check has changed and sends notifications if necessary. - */ -CheckSchema.pre("save", async function (next) { - try { - const monitor = await mongoose.model("Monitor").findById(this.monitorId); - - if (monitor) { - const notifications = await Notification.find({ - monitorId: this.monitorId, - }); - - // Check if there are any notifications - - // Only send email if monitor status has changed - if (monitor.status !== this.status) { - const emailService = new EmailService(); - - let template = ""; - let status = ""; - if (monitor.status === true && this.status === false) { - template = "serverIsDownTemplate"; - status = "down"; - } - - if (monitor.status === false && this.status === true) { - // Notify users that the monitor is up - template = "serverIsUpTemplate"; - status = "up"; - } - - for (const notification of notifications) { - if (notification.type === "email") { - await emailService.buildAndSendEmail( - template, - { monitorName: monitor.name, monitorUrl: monitor.url }, - notification.address, - `Monitor ${monitor.name} is ${status}` - ); - } - } - - // Update monitor status - monitor.status = this.status; - await monitor.save(); - } - } - } catch (error) { - console.log(error); - } finally { - next(); - } -}); - module.exports = mongoose.model("Check", CheckSchema); diff --git a/Server/service/jobQueue.js b/Server/service/jobQueue.js index 12c151abd..4c7a02192 100644 --- a/Server/service/jobQueue.js +++ b/Server/service/jobQueue.js @@ -16,7 +16,7 @@ class JobQueue { * @constructor * @throws {Error} */ - constructor() { + constructor(networkService) { this.queue = new Queue(QUEUE_NAME, { connection, }); @@ -32,11 +32,11 @@ class JobQueue { * @returns {Promise} - Returns a new JobQueue * */ - static async createJobQueue(db) { + static async createJobQueue(db, networkService) { const queue = new JobQueue(); try { queue.db = db; - queue.networkService = new NetworkService(db); + queue.networkService = networkService; const monitors = await db.getAllMonitors(); for (const monitor of monitors) { await queue.addJob(monitor.id, monitor); diff --git a/Server/service/networkService.js b/Server/service/networkService.js index dc8994945..375380f2e 100644 --- a/Server/service/networkService.js +++ b/Server/service/networkService.js @@ -3,8 +3,9 @@ const ping = require("ping"); const logger = require("../utils/logger"); class NetworkService { - constructor(db) { + constructor(db, emailService) { this.db = db; + this.emailService = emailService; this.TYPE_PING = "ping"; this.TYPE_HTTP = "http"; this.TYPE_PAGESPEED = "pagespeed"; @@ -12,6 +13,33 @@ class NetworkService { this.NETWORK_ERROR = 5000; } + async handleNotification(job, isAlive) { + const { _id } = job.data; + const monitor = await this.db.getMonitorById(_id); + + // If monitor status changes, update monitor status and send notification + if (monitor.status !== isAlive) { + monitor.status = !monitor.status; + await monitor.save(); + + let template = + isAlive === true ? "serverIsUpTemplate" : "serverIsDownTemplate"; + let status = isAlive === true ? "up" : "down"; + + const notifications = await this.db.getNotificationsByMonitorId(_id); + for (const notification of notifications) { + if (notification.type === "email") { + await this.emailService.buildAndSendEmail( + template, + { monitorName: monitor.name, monitorUrl: monitor.url }, + notification.address, + `Monitor ${monitor.name} is ${status}` + ); + } + } + } + } + /** * Measures the response time of an asynchronous operation. * @param {Function} operation - An asynchronous operation to measure. @@ -42,10 +70,12 @@ class NetworkService { return response; }; + let isAlive; + try { const { responseTime, response } = await this.measureResponseTime(operation); - const isAlive = response.alive; + isAlive = response.alive; const checkData = { monitorId: job.data._id, @@ -54,12 +84,15 @@ class NetworkService { }; return await this.logAndStoreCheck(checkData, this.db.createCheck); } catch (error) { + isAlive = false; const checkData = { monitorId: job.data._id, - status: false, + status: isAlive, responseTime: error.responseTime, }; return await this.logAndStoreCheck(checkData, this.db.createCheck); + } finally { + this.handleNotification(job, isAlive); } } @@ -75,13 +108,15 @@ class NetworkService { return response; }; + let isAlive; + // attempt connection try { const { responseTime, response } = await this.measureResponseTime(operation); // check if response is in the 200 range, if so, service is up - const isAlive = response.status >= 200 && response.status < 300; + isAlive = response.status >= 200 && response.status < 300; //Create a check with relevant data const checkData = { @@ -92,9 +127,10 @@ class NetworkService { }; return await this.logAndStoreCheck(checkData, this.db.createCheck); } catch (error) { + isAlive = false; const checkData = { monitorId: job.data._id, - status: false, + status: isAlive, responseTime: error.responseTime, }; // The server returned a response @@ -104,6 +140,8 @@ class NetworkService { checkData.statusCode = this.NETWORK_ERROR; } return await this.logAndStoreCheck(checkData, this.db.createCheck); + } finally { + this.handleNotification(job, isAlive); } } @@ -122,6 +160,8 @@ class NetworkService { * @throws {Error} Throws an error if there is an issue with fetching or processing the PageSpeed insights. */ async handlePagespeed(job) { + let isAlive; + try { const url = job.data.url; const response = await axios.get( @@ -145,9 +185,10 @@ class NetworkService { // Total Blocking Time 30% // Cumulative Layout Shift 25% + isAlive = true; const checkData = { monitorId: job.data._id, - status: true, + status: isAlive, accessibility: (categories.accessibility?.score || 0) * 100, bestPractices: (categories["best-practices"]?.score || 0) * 100, seo: (categories.seo?.score || 0) * 100, @@ -163,15 +204,18 @@ class NetworkService { this.logAndStoreCheck(checkData, this.db.createPageSpeedCheck); } catch (error) { + isAlive = false; const checkData = { monitorId: job.data._id, - status: false, + status: isAlive, accessibility: 0, bestPractices: 0, seo: 0, performance: 0, }; this.logAndStoreCheck(checkData, this.db.createPageSpeedCheck); + } finally { + this.handleNotification(job, isAlive); } }