Refactored notifications from Check model to NetworkService

This commit is contained in:
Alex Holliday
2024-08-14 10:20:35 -07:00
parent a6116da278
commit 809e6ee58f
5 changed files with 59 additions and 129 deletions
+2 -61
View File
@@ -263,69 +263,10 @@ const getMonitorStatsById = async (req) => {
* @returns {Promise<Monitor>}
* @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;
}
+3 -1
View File
@@ -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 () => {
-57
View File
@@ -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);
+3 -3
View File
@@ -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<JobQueue>} - 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);
+51 -7
View File
@@ -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);
}
}