From cc7e1bb111a27024afb170e3cd502152ab6fa804 Mon Sep 17 00:00:00 2001 From: M M Date: Thu, 1 Aug 2024 20:42:14 -0700 Subject: [PATCH 01/13] Testing email notifications for when monitor goes down. --- Server/models/Check.js | 40 ++++++++++++++++++++++++------------- Server/routes/checkRoute.js | 32 +++++++++++++++++++++++++++-- 2 files changed, 56 insertions(+), 16 deletions(-) diff --git a/Server/models/Check.js b/Server/models/Check.js index 7732fd312..43139d505 100644 --- a/Server/models/Check.js +++ b/Server/models/Check.js @@ -1,4 +1,5 @@ const mongoose = require("mongoose"); +const EmailService = require("../service/emailService"); const CheckSchema = mongoose.Schema( { @@ -11,10 +12,10 @@ const CheckSchema = mongoose.Schema( type: Boolean, }, responseTime: { - type: Number, // milliseconds + type: Number, }, statusCode: { - type: Number, // 200, ... , 500 + type: Number, }, message: { type: String, @@ -22,33 +23,44 @@ const CheckSchema = mongoose.Schema( expiry: { type: Date, default: Date.now, - expires: 60 * 60 * 24 * 30, // 30 days in seconds + expires: 60 * 60 * 24 * 30, }, }, - { timestamps: true, } ); -/** - * When a check is created, we should update the stauts of the associated monitor - * if it has changed. Monitor starts with default status: true - * @param {function} next - Callback to proceed to the next middleware. - */ - CheckSchema.pre("save", async function (next) { try { const monitor = await mongoose.model("Monitor").findById(this.monitorId); if (monitor && monitor.status !== this.status) { if (monitor.status === true && this.status === false) { - // TODO issue alert - console.log("Monitor went down"); + // Notify user that the monitor is down + const emailService = new EmailService(); + const users = await mongoose.model("User").find({ _id: monitor.userId }); + const user = users[0]; + + await emailService.buildAndSendEmail( + "serverIsDownTemplate", + { monitorName: monitor.name, monitorUrl: monitor.url }, + user.email, + `Monitor ${monitor.name} is down` + ); } if (monitor.status === false && this.status === true) { - // TODO issue alert - console.log("Monitor went up"); + // Notify user that the monitor is up + const emailService = new EmailService(); + const users = await mongoose.model("User").find({ _id: monitor.userId }); + const user = users[0]; + + await emailService.buildAndSendEmail( + "serverIsUpTemplate", + { monitorName: monitor.name, monitorUrl: monitor.url }, + user.email, + `Monitor ${monitor.name} is back up` + ); } monitor.status = this.status; await monitor.save(); diff --git a/Server/routes/checkRoute.js b/Server/routes/checkRoute.js index 6569dd49a..ff65fd2fc 100644 --- a/Server/routes/checkRoute.js +++ b/Server/routes/checkRoute.js @@ -1,7 +1,11 @@ const router = require("express").Router(); -const checkController = require("../controllers/checkController"); -const { verifyOwnership } = require("../middleware/verifyOwnership"); +const Check = require("../models/Check"); const Monitor = require("../models/Monitor"); +const { verifyOwnership } = require("../middleware/verifyOwnership"); +const { verifyJWT } = require("../middleware/verifyJWT"); +const checkController = require("../controllers/checkController"); + +router.use(verifyJWT); router.post( "/:monitorId", @@ -21,4 +25,28 @@ router.post( checkController.deleteChecks ); +// Temporary test route +router.post("/test-notification/:monitorId", async (req, res) => { + try { + const monitorId = req.params.monitorId; + const monitor = await Monitor.findById(monitorId); + if (!monitor) { + return res.status(404).json({ message: "Monitor not found" }); + } + + const newCheck = new Check({ + monitorId, + status: false, + responseTime: 100, + statusCode: 500, + message: "Server error" + }); + + await newCheck.save(); + res.status(201).json({ message: "Check created and email notification sent" }); + } catch (error) { + res.status(500).json({ message: error.message }); + } +}); + module.exports = router; From e6fac616db64a6783ef9bf1069dd705d82f93d72 Mon Sep 17 00:00:00 2001 From: M M Date: Tue, 6 Aug 2024 18:58:13 -0700 Subject: [PATCH 02/13] Fixed bug that sends an email even if a user does not have notifications turned on. --- Server/models/Check.js | 64 ++++++++++++++++++++++++------------------ 1 file changed, 36 insertions(+), 28 deletions(-) diff --git a/Server/models/Check.js b/Server/models/Check.js index 43139d505..f4c3c35d4 100644 --- a/Server/models/Check.js +++ b/Server/models/Check.js @@ -34,36 +34,43 @@ const CheckSchema = mongoose.Schema( CheckSchema.pre("save", async function (next) { try { const monitor = await mongoose.model("Monitor").findById(this.monitorId); - if (monitor && monitor.status !== this.status) { - if (monitor.status === true && this.status === false) { - // Notify user that the monitor is down - const emailService = new EmailService(); - const users = await mongoose.model("User").find({ _id: monitor.userId }); - const user = users[0]; + if (monitor) { + // Check if the monitor has notifications + if (monitor.notifications && monitor.notifications.length > 0) { + if (monitor.status !== this.status) { + const emailService = new EmailService(); - await emailService.buildAndSendEmail( - "serverIsDownTemplate", - { monitorName: monitor.name, monitorUrl: monitor.url }, - user.email, - `Monitor ${monitor.name} is down` - ); + if (monitor.status === true && this.status === false) { + // Notify user that the monitor is down + const users = await mongoose.model("User").find({ _id: monitor.userId }); + if (users.length > 0) { + const user = users[0]; + await emailService.buildAndSendEmail( + "serverIsDownTemplate", + { monitorName: monitor.name, monitorUrl: monitor.url }, + user.email, + `Monitor ${monitor.name} is down` + ); + } + } + + if (monitor.status === false && this.status === true) { + // Notify user that the monitor is up + const users = await mongoose.model("User").find({ _id: monitor.userId }); + if (users.length > 0) { + const user = users[0]; + await emailService.buildAndSendEmail( + "serverIsUpTemplate", + { monitorName: monitor.name, monitorUrl: monitor.url }, + user.email, + `Monitor ${monitor.name} is back up` + ); + } + } + monitor.status = this.status; + await monitor.save(); + } } - - if (monitor.status === false && this.status === true) { - // Notify user that the monitor is up - const emailService = new EmailService(); - const users = await mongoose.model("User").find({ _id: monitor.userId }); - const user = users[0]; - - await emailService.buildAndSendEmail( - "serverIsUpTemplate", - { monitorName: monitor.name, monitorUrl: monitor.url }, - user.email, - `Monitor ${monitor.name} is back up` - ); - } - monitor.status = this.status; - await monitor.save(); } } catch (error) { console.log(error); @@ -72,4 +79,5 @@ CheckSchema.pre("save", async function (next) { } }); + module.exports = mongoose.model("Check", CheckSchema); From 66d101877a8b1447a424730e637111f17ab1ed1a Mon Sep 17 00:00:00 2001 From: M M Date: Tue, 6 Aug 2024 19:35:03 -0700 Subject: [PATCH 03/13] look up notifications by monitor and not by user. --- Server/models/Check.js | 45 +++++++++++++++++++++++------------------- 1 file changed, 25 insertions(+), 20 deletions(-) diff --git a/Server/models/Check.js b/Server/models/Check.js index f4c3c35d4..0b46b4315 100644 --- a/Server/models/Check.js +++ b/Server/models/Check.js @@ -34,39 +34,43 @@ const CheckSchema = mongoose.Schema( CheckSchema.pre("save", async function (next) { try { const monitor = await mongoose.model("Monitor").findById(this.monitorId); + if (monitor) { // Check if the monitor has notifications if (monitor.notifications && monitor.notifications.length > 0) { + // Only send email if monitor status has changed if (monitor.status !== this.status) { const emailService = new EmailService(); if (monitor.status === true && this.status === false) { - // Notify user that the monitor is down - const users = await mongoose.model("User").find({ _id: monitor.userId }); - if (users.length > 0) { - const user = users[0]; - await emailService.buildAndSendEmail( - "serverIsDownTemplate", - { monitorName: monitor.name, monitorUrl: monitor.url }, - user.email, - `Monitor ${monitor.name} is down` - ); + // Notify users that the monitor is down + for (const notification of monitor.notifications) { + if (notification.email) { + await emailService.buildAndSendEmail( + "serverIsDownTemplate", + { monitorName: monitor.name, monitorUrl: monitor.url }, + notification.email, + `Monitor ${monitor.name} is down` + ); + } } } if (monitor.status === false && this.status === true) { - // Notify user that the monitor is up - const users = await mongoose.model("User").find({ _id: monitor.userId }); - if (users.length > 0) { - const user = users[0]; - await emailService.buildAndSendEmail( - "serverIsUpTemplate", - { monitorName: monitor.name, monitorUrl: monitor.url }, - user.email, - `Monitor ${monitor.name} is back up` - ); + // Notify users that the monitor is up + for (const notification of monitor.notifications) { + if (notification.email) { + await emailService.buildAndSendEmail( + "serverIsUpTemplate", + { monitorName: monitor.name, monitorUrl: monitor.url }, + notification.email, + `Monitor ${monitor.name} is back up` + ); + } } } + + // Update monitor status monitor.status = this.status; await monitor.save(); } @@ -80,4 +84,5 @@ CheckSchema.pre("save", async function (next) { }); + module.exports = mongoose.model("Check", CheckSchema); From 67711ffe2c896d129f30abee88a233eb16656e8e Mon Sep 17 00:00:00 2001 From: M M Date: Tue, 6 Aug 2024 19:35:49 -0700 Subject: [PATCH 04/13] Get rid of test route function. --- Server/routes/checkRoute.js | 24 ------------------------ 1 file changed, 24 deletions(-) diff --git a/Server/routes/checkRoute.js b/Server/routes/checkRoute.js index ff65fd2fc..871191d2e 100644 --- a/Server/routes/checkRoute.js +++ b/Server/routes/checkRoute.js @@ -25,28 +25,4 @@ router.post( checkController.deleteChecks ); -// Temporary test route -router.post("/test-notification/:monitorId", async (req, res) => { - try { - const monitorId = req.params.monitorId; - const monitor = await Monitor.findById(monitorId); - if (!monitor) { - return res.status(404).json({ message: "Monitor not found" }); - } - const newCheck = new Check({ - monitorId, - status: false, - responseTime: 100, - statusCode: 500, - message: "Server error" - }); - - await newCheck.save(); - res.status(201).json({ message: "Check created and email notification sent" }); - } catch (error) { - res.status(500).json({ message: error.message }); - } -}); - -module.exports = router; From 7b2311d4689c41201e91547618b70b54ffc69430 Mon Sep 17 00:00:00 2001 From: M M Date: Tue, 6 Aug 2024 19:36:43 -0700 Subject: [PATCH 05/13] Add docs. --- Server/models/Check.js | 48 ++++++++++++++++++++++++++++++++++++++---- 1 file changed, 44 insertions(+), 4 deletions(-) diff --git a/Server/models/Check.js b/Server/models/Check.js index 0b46b4315..2eb383340 100644 --- a/Server/models/Check.js +++ b/Server/models/Check.js @@ -1,36 +1,78 @@ const mongoose = require("mongoose"); const EmailService = require("../service/emailService"); +/** + * Check Schema for MongoDB collection. + * + * Represents a check associated with a monitor, storing information + * about the status and response of a particular check event. + */ const CheckSchema = mongoose.Schema( { + /** + * Reference to the associated Monitor document. + * + * @type {mongoose.Schema.Types.ObjectId} + */ monitorId: { type: mongoose.Schema.Types.ObjectId, ref: "Monitor", immutable: true, }, + /** + * Status of the check (true for up, false for down). + * + * @type {Boolean} + */ status: { type: Boolean, }, + /** + * Response time of the check in milliseconds. + * + * @type {Number} + */ responseTime: { type: Number, }, + /** + * HTTP status code received during the check. + * + * @type {Number} + */ statusCode: { type: Number, }, + /** + * Message or description of the check result. + * + * @type {String} + */ message: { type: String, }, + /** + * Expiry date of the check, auto-calculated to expire after 30 days. + * + * @type {Date} + */ expiry: { type: Date, default: Date.now, - expires: 60 * 60 * 24 * 30, + expires: 60 * 60 * 24 * 30, // 30 days }, }, { - timestamps: true, + timestamps: true, // Adds createdAt and updatedAt timestamps } ); +/** + * 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); @@ -83,6 +125,4 @@ CheckSchema.pre("save", async function (next) { } }); - - module.exports = mongoose.model("Check", CheckSchema); From e42b65054d0502ef7bdc9c8985778f3fa47514e2 Mon Sep 17 00:00:00 2001 From: M M Date: Tue, 6 Aug 2024 20:29:07 -0700 Subject: [PATCH 06/13] Fixed module export. --- Server/routes/checkRoute.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Server/routes/checkRoute.js b/Server/routes/checkRoute.js index 871191d2e..a222ba21c 100644 --- a/Server/routes/checkRoute.js +++ b/Server/routes/checkRoute.js @@ -25,4 +25,4 @@ router.post( checkController.deleteChecks ); - +module.exports = router; From f7161f0302c852280060ddbcc44b740ea291cd0c Mon Sep 17 00:00:00 2001 From: M M Date: Tue, 6 Aug 2024 20:45:27 -0700 Subject: [PATCH 07/13] Fix monitor notification check. --- Server/models/Check.js | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/Server/models/Check.js b/Server/models/Check.js index 2eb383340..f617e1ae3 100644 --- a/Server/models/Check.js +++ b/Server/models/Check.js @@ -1,5 +1,6 @@ const mongoose = require("mongoose"); const EmailService = require("../service/emailService"); +const Notification = require("../models/Notification"); /** * Check Schema for MongoDB collection. @@ -78,15 +79,17 @@ CheckSchema.pre("save", async function (next) { const monitor = await mongoose.model("Monitor").findById(this.monitorId); if (monitor) { - // Check if the monitor has notifications - if (monitor.notifications && monitor.notifications.length > 0) { + const notifications = await Notification.find({ monitorId: this.monitorId }); + + // Check if there are any notifications + if (notifications && notifications.length > 0) { // Only send email if monitor status has changed if (monitor.status !== this.status) { const emailService = new EmailService(); if (monitor.status === true && this.status === false) { // Notify users that the monitor is down - for (const notification of monitor.notifications) { + for (const notification of notifications) { if (notification.email) { await emailService.buildAndSendEmail( "serverIsDownTemplate", @@ -100,7 +103,7 @@ CheckSchema.pre("save", async function (next) { if (monitor.status === false && this.status === true) { // Notify users that the monitor is up - for (const notification of monitor.notifications) { + for (const notification of notifications) { if (notification.email) { await emailService.buildAndSendEmail( "serverIsUpTemplate", @@ -125,4 +128,5 @@ CheckSchema.pre("save", async function (next) { } }); + module.exports = mongoose.model("Check", CheckSchema); From 96a99e3d6deee6aa49fb7afc7cc7fd0ea8fe11e9 Mon Sep 17 00:00:00 2001 From: M M Date: Tue, 6 Aug 2024 20:53:42 -0700 Subject: [PATCH 08/13] Fixed error not sending the email. --- Server/models/Check.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Server/models/Check.js b/Server/models/Check.js index f617e1ae3..bf4413977 100644 --- a/Server/models/Check.js +++ b/Server/models/Check.js @@ -90,11 +90,11 @@ CheckSchema.pre("save", async function (next) { if (monitor.status === true && this.status === false) { // Notify users that the monitor is down for (const notification of notifications) { - if (notification.email) { + if (notification.type === "email") { await emailService.buildAndSendEmail( "serverIsDownTemplate", { monitorName: monitor.name, monitorUrl: monitor.url }, - notification.email, + notification.address, `Monitor ${monitor.name} is down` ); } @@ -104,11 +104,11 @@ CheckSchema.pre("save", async function (next) { if (monitor.status === false && this.status === true) { // Notify users that the monitor is up for (const notification of notifications) { - if (notification.email) { + if (notification.type === "email") { await emailService.buildAndSendEmail( "serverIsUpTemplate", { monitorName: monitor.name, monitorUrl: monitor.url }, - notification.email, + notification.address, `Monitor ${monitor.name} is back up` ); } From f13de0815794bf799475c30e757c8ed9c204045d Mon Sep 17 00:00:00 2001 From: M M Date: Tue, 6 Aug 2024 21:01:07 -0700 Subject: [PATCH 09/13] Get rid of verifyJWT. --- Server/routes/checkRoute.js | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/Server/routes/checkRoute.js b/Server/routes/checkRoute.js index a222ba21c..0216343c8 100644 --- a/Server/routes/checkRoute.js +++ b/Server/routes/checkRoute.js @@ -1,11 +1,7 @@ const router = require("express").Router(); -const Check = require("../models/Check"); -const Monitor = require("../models/Monitor"); -const { verifyOwnership } = require("../middleware/verifyOwnership"); -const { verifyJWT } = require("../middleware/verifyJWT"); const checkController = require("../controllers/checkController"); - -router.use(verifyJWT); +const { verifyOwnership } = require("../middleware/verifyOwnership"); +const Monitor = require("../models/Monitor"); router.post( "/:monitorId", @@ -25,4 +21,4 @@ router.post( checkController.deleteChecks ); -module.exports = router; +module.exports = router; \ No newline at end of file From 297c2a225de0529963a7c1d4f1344a6fa0427927 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Wed, 7 Aug 2024 12:22:23 -0700 Subject: [PATCH 10/13] Added nth check display limit for monitor details chart --- .../Charts/MonitorDetailsAreaChart/index.jsx | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/Client/src/Components/Charts/MonitorDetailsAreaChart/index.jsx b/Client/src/Components/Charts/MonitorDetailsAreaChart/index.jsx index 985b5b846..f5671e433 100644 --- a/Client/src/Components/Charts/MonitorDetailsAreaChart/index.jsx +++ b/Client/src/Components/Charts/MonitorDetailsAreaChart/index.jsx @@ -47,7 +47,7 @@ const MonitorDetailsAreaChart = ({ checks, filter }) => { }); }; - const filterChecks = (checks, filter) => { + const filterChecks = (checks, filter, numToDisplay) => { const limits = { day: 24 * 60 * 60 * 1000, week: 24 * 60 * 60 * 1000 * 7, @@ -55,20 +55,29 @@ const MonitorDetailsAreaChart = ({ checks, filter }) => { }; const now = new Date().getTime(); - const result = []; + let result = []; for (let i = 0; i < checks.length; i++) { const checkTime = new Date(checks[i].createdAt).getTime(); if (now - checkTime < limits[filter]) { result.push(checks[i]); } } + + // If more than 50 checks, pick every nth check + if (result.length > numToDisplay) { + const n = Math.ceil(result.length / numToDisplay); + result = result.filter((_, idx) => { + return idx % n === 0; + }); + } + return result; }; let normalizedChecks = []; if (checks && checks.length > 0) { - const filteredChecks = filterChecks(checks, filter); - normalizedChecks = NormalizeData(filteredChecks, 33, 100); + const filteredChecks = filterChecks(checks, filter, 75); + normalizedChecks = NormalizeData(filteredChecks, 10, 100); } return ( From d1f0d4f2d31150c4c24f8b6a14fa22cd897c2c5d Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Wed, 7 Aug 2024 12:24:06 -0700 Subject: [PATCH 11/13] fixed comment --- Client/src/Components/Charts/MonitorDetailsAreaChart/index.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Client/src/Components/Charts/MonitorDetailsAreaChart/index.jsx b/Client/src/Components/Charts/MonitorDetailsAreaChart/index.jsx index f5671e433..39aa8dcbf 100644 --- a/Client/src/Components/Charts/MonitorDetailsAreaChart/index.jsx +++ b/Client/src/Components/Charts/MonitorDetailsAreaChart/index.jsx @@ -63,7 +63,7 @@ const MonitorDetailsAreaChart = ({ checks, filter }) => { } } - // If more than 50 checks, pick every nth check + // If more than numToDisplay checks, pick every nth check if (result.length > numToDisplay) { const n = Math.ceil(result.length / numToDisplay); result = result.filter((_, idx) => { From 02e8b2b43cb652a56745c11ba67054869c15dbc0 Mon Sep 17 00:00:00 2001 From: M M Date: Wed, 7 Aug 2024 14:35:54 -0700 Subject: [PATCH 12/13] Changed the name here to pageSpeedCheckController as to not be confused with the actual check controller. --- Server/routes/pageSpeedCheckRoute.js | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/Server/routes/pageSpeedCheckRoute.js b/Server/routes/pageSpeedCheckRoute.js index 34ec0e544..4b1de49bc 100644 --- a/Server/routes/pageSpeedCheckRoute.js +++ b/Server/routes/pageSpeedCheckRoute.js @@ -1,7 +1,7 @@ const router = require("express").Router(); const { verifyOwnership } = require("../middleware/verifyOwnership"); const Monitor = require("../models/Monitor"); -const checkController = require("../controllers/pageSpeedCheckController"); +const pageSpeedCheckController = require("../controllers/pageSpeedCheckController"); /** * @route POST /:monitorId @@ -13,7 +13,7 @@ const checkController = require("../controllers/pageSpeedCheckController"); router.post( "/:monitorId", verifyOwnership(Monitor, "monitorId"), - checkController.createPageSpeedCheck + pageSpeedCheckController.createPageSpeedCheck ); /** @@ -26,7 +26,7 @@ router.post( router.get( "/:monitorId", verifyOwnership(Monitor, "monitorId"), - checkController.getPageSpeedChecks + pageSpeedCheckController.getPageSpeedChecks ); /** @@ -39,8 +39,7 @@ router.get( router.delete( "/:monitorId", verifyOwnership(Monitor, "monitorId"), - checkController.deletePageSpeedCheck + pageSpeedCheckController.deletePageSpeedCheck ); module.exports = router; - From 0f1433135888869fb5e7f9c90afd35d97652bcdc Mon Sep 17 00:00:00 2001 From: M M Date: Wed, 7 Aug 2024 14:44:34 -0700 Subject: [PATCH 13/13] Got rid of the redundant word back here. Made grammar more formal. --- Client/src/Pages/Monitors/index.jsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Client/src/Pages/Monitors/index.jsx b/Client/src/Pages/Monitors/index.jsx index 3c1d7bc64..71a5eccd6 100644 --- a/Client/src/Pages/Monitors/index.jsx +++ b/Client/src/Pages/Monitors/index.jsx @@ -294,10 +294,10 @@ const Monitors = () => { }} > - Really delete this monitor? + Do you really want to delete this monitor? - Once deleted, this monitor cannot be retrieved back. + Once deleted, this monitor cannot be retrieved.