From cc7e1bb111a27024afb170e3cd502152ab6fa804 Mon Sep 17 00:00:00 2001 From: M M Date: Thu, 1 Aug 2024 20:42:14 -0700 Subject: [PATCH 1/9] 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 2/9] 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 3/9] 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 4/9] 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 5/9] 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 6/9] 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 7/9] 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 8/9] 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 9/9] 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