mirror of
https://github.com/bluewave-labs/Checkmate.git
synced 2026-01-19 16:19:45 -06:00
Merge pull request #1037 from bluewave-labs/feat/be/hardware-monitor-notifications
feat/be/hardware monitor notifications
This commit is contained in:
@@ -16,7 +16,7 @@ const memorySchema = mongoose.Schema({
|
||||
usage_percent: { type: Number, default: 0 },
|
||||
});
|
||||
|
||||
const discSchema = mongoose.Schema({
|
||||
const diskSchema = mongoose.Schema({
|
||||
read_speed_bytes: { type: Number, default: 0 },
|
||||
write_speed_bytes: { type: Number, default: 0 },
|
||||
total_bytes: { type: Number, default: 0 },
|
||||
@@ -37,6 +37,10 @@ const HardwareCheckSchema = mongoose.Schema(
|
||||
ref: "Monitor",
|
||||
immutable: true,
|
||||
},
|
||||
status: {
|
||||
type: Boolean,
|
||||
index: true,
|
||||
},
|
||||
cpu: {
|
||||
type: cpuSchema,
|
||||
default: () => ({}),
|
||||
@@ -46,7 +50,7 @@ const HardwareCheckSchema = mongoose.Schema(
|
||||
default: () => ({}),
|
||||
},
|
||||
disk: {
|
||||
type: [discSchema],
|
||||
type: [diskSchema],
|
||||
default: () => [],
|
||||
},
|
||||
host: {
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import mongoose from "mongoose";
|
||||
import Notification from "./Notification.js";
|
||||
|
||||
const MonitorSchema = mongoose.Schema(
|
||||
{
|
||||
@@ -48,6 +47,14 @@ const MonitorSchema = mongoose.Schema(
|
||||
type: Number,
|
||||
default: undefined,
|
||||
},
|
||||
thresholds: {
|
||||
type: {
|
||||
usage_cpu: { type: Number },
|
||||
usage_memory: { type: Number },
|
||||
usage_disk: { type: Number },
|
||||
},
|
||||
_id: false,
|
||||
},
|
||||
notifications: [
|
||||
{
|
||||
type: mongoose.Schema.Types.ObjectId,
|
||||
|
||||
@@ -65,6 +65,7 @@ class EmailService {
|
||||
serverIsDownTemplate: this.loadTemplate("serverIsDown"),
|
||||
serverIsUpTemplate: this.loadTemplate("serverIsUp"),
|
||||
passwordResetTemplate: this.loadTemplate("passwordReset"),
|
||||
thresholdViolatedTemplate: this.loadTemplate("thresholdViolated"),
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -34,24 +34,24 @@ class NetworkService {
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the notification process for a monitor.
|
||||
* Handles notifications for a given monitor configuration.
|
||||
*
|
||||
* @param {Object} monitor - The monitor object containing monitor details.
|
||||
* @param {boolean} isAlive - The status of the monitor (true if up, false if down).
|
||||
* @returns {Promise<void>}
|
||||
*/ async handleNotification(monitor, isAlive) {
|
||||
* @param {Object} config - The configuration object for the notification.
|
||||
* @param {Object} config.monitor - The monitor object containing the monitor ID.
|
||||
* @param {string} config.template - The email template to be used.
|
||||
* @param {Object} config.context - The context for the email template.
|
||||
* @param {string} config.subject - The subject of the email.
|
||||
*/
|
||||
async handleNotification(config) {
|
||||
try {
|
||||
let template = isAlive === true ? "serverIsUpTemplate" : "serverIsDownTemplate";
|
||||
let status = isAlive === true ? "up" : "down";
|
||||
|
||||
const notifications = await this.db.getNotificationsByMonitorId(monitor._id);
|
||||
const notifications = await this.db.getNotificationsByMonitorId(config.monitor._id);
|
||||
for (const notification of notifications) {
|
||||
if (notification.type === "email") {
|
||||
await this.emailService.buildAndSendEmail(
|
||||
template,
|
||||
{ monitorName: monitor.name, monitorUrl: monitor.url },
|
||||
config.template,
|
||||
config.context,
|
||||
notification.address,
|
||||
`Monitor ${monitor.name} is ${status}`
|
||||
config.subject
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -71,9 +71,10 @@ class NetworkService {
|
||||
*
|
||||
* @param {Object} job - The job object containing job details.
|
||||
* @param {boolean} isAlive - The status of the monitor (true if up, false if down).
|
||||
* @param {Object} [hardwareData] - The hardware data for the monitor.
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async handleStatusUpdate(job, isAlive) {
|
||||
async handleStatusUpdate(job, isAlive, hardwareData) {
|
||||
let monitor;
|
||||
const { _id } = job.data;
|
||||
|
||||
@@ -99,9 +100,18 @@ class NetworkService {
|
||||
await monitor.save();
|
||||
|
||||
if (oldStatus !== undefined && oldStatus !== isAlive) {
|
||||
this.handleNotification(monitor, isAlive);
|
||||
const config = {
|
||||
monitor: monitor,
|
||||
template: isAlive === true ? "serverIsUpTemplate" : "serverIsDownTemplate",
|
||||
context: { monitorName: monitor.name, monitorUrl: monitor.url },
|
||||
subject: `Monitor ${monitor.name} is ${isAlive === true ? "up" : "down"}`,
|
||||
};
|
||||
this.handleNotification(config);
|
||||
}
|
||||
}
|
||||
if (monitor.type === this.TYPE_HARDWARE) {
|
||||
this.handleHardwareUpdate(monitor, hardwareData);
|
||||
}
|
||||
} catch (error) {
|
||||
this.logger.error({
|
||||
message: error.message,
|
||||
@@ -113,6 +123,55 @@ class NetworkService {
|
||||
}
|
||||
}
|
||||
|
||||
async handleHardwareUpdate(monitor, hardwareData) {
|
||||
//Get Thresholds
|
||||
const thresholds = monitor?.thresholds;
|
||||
if (thresholds === undefined) {
|
||||
return;
|
||||
}
|
||||
//Get Values
|
||||
const cpuUsage = hardwareData?.cpu?.usage_percent;
|
||||
const memoryUsage = hardwareData?.memory?.usage_percent;
|
||||
const diskUsage = hardwareData?.disk[0]?.usage_percent;
|
||||
|
||||
// Return early if no values
|
||||
if (cpuUsage === undefined && memoryUsage === undefined && diskUsage === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Return early if all values are below thresholds
|
||||
if (
|
||||
cpuUsage < thresholds.usage_cpu &&
|
||||
memoryUsage < thresholds.usage_memory &&
|
||||
diskUsage < thresholds.usage_disk
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
let context = {
|
||||
message: "Threshold(s) exceeded:",
|
||||
};
|
||||
if (cpuUsage > thresholds.usage_cpu) {
|
||||
context.cpu = `CPU USAGE: ${cpuUsage * 100}% > ${thresholds.usage_cpu * 100}%`;
|
||||
}
|
||||
|
||||
if (memoryUsage > thresholds.usage_memory) {
|
||||
context.memory = `MEMORY USAGE: ${memoryUsage * 100}% > ${thresholds.usage_memory * 100}%`;
|
||||
}
|
||||
|
||||
if (diskUsage > thresholds.usage_disk) {
|
||||
context.disk = `DISK USAGE: ${diskUsage * 100}% > ${thresholds.usage_disk * 100}%`;
|
||||
}
|
||||
|
||||
const config = {
|
||||
monitor: monitor,
|
||||
template: "thresholdViolatedTemplate",
|
||||
context: context,
|
||||
subject: `Threshold Violated for ${monitor.name}`,
|
||||
};
|
||||
this.handleNotification(config);
|
||||
}
|
||||
|
||||
/**
|
||||
* Measures the response time of an asynchronous operation.
|
||||
* @param {Function} operation - An asynchronous operation to measure.
|
||||
@@ -311,13 +370,13 @@ class NetworkService {
|
||||
frequency: 266,
|
||||
temperature: null,
|
||||
free_percent: null,
|
||||
usage_percent: null,
|
||||
usage_percent: 1,
|
||||
},
|
||||
memory: {
|
||||
total_bytes: 4,
|
||||
available_bytes: 4,
|
||||
used_bytes: 2,
|
||||
usage_percent: 0.5,
|
||||
usage_percent: 1,
|
||||
},
|
||||
disk: [
|
||||
{
|
||||
@@ -325,7 +384,7 @@ class NetworkService {
|
||||
write_speed_bytes: 3,
|
||||
total_bytes: 10,
|
||||
free_bytes: 2,
|
||||
usage_percent: 0.8,
|
||||
usage_percent: 1,
|
||||
},
|
||||
],
|
||||
host: {
|
||||
@@ -372,7 +431,7 @@ class NetworkService {
|
||||
};
|
||||
this.logAndStoreCheck(nullData, this.db.createHardwareCheck);
|
||||
} finally {
|
||||
this.handleStatusUpdate(job, isAlive);
|
||||
this.handleStatusUpdate(job, isAlive, hardwareData);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
40
Server/templates/thresholdViolated.mjml
Normal file
40
Server/templates/thresholdViolated.mjml
Normal file
@@ -0,0 +1,40 @@
|
||||
<mjml>
|
||||
<mj-head>
|
||||
<mj-font name="Roboto" href="https://fonts.googleapis.com/css?family=Roboto:300,500"></mj-font>
|
||||
<mj-attributes>
|
||||
<mj-all font-family="Roboto, Helvetica, sans-serif"></mj-all>
|
||||
<mj-text font-weight="300" font-size="16px" color="#616161" line-height="24px"></mj-text>
|
||||
<mj-section padding="0px"></mj-section>
|
||||
</mj-attributes>
|
||||
</mj-head>
|
||||
<mj-body>
|
||||
<mj-section padding="20px 0">
|
||||
<mj-column width="100%">
|
||||
<mj-text align="left" font-size="10px">
|
||||
Message from BlueWave Uptime Service
|
||||
</mj-text>
|
||||
</mj-column>
|
||||
<mj-column width="45%" padding-top="20px">
|
||||
<mj-text font-weight="500" padding="0px" font-size="18px">
|
||||
{{message}}
|
||||
</mj-text>
|
||||
<mj-text font-weight="500" padding="0px" font-size="18px">
|
||||
{{#if cpu}}
|
||||
{{cpu}}
|
||||
{{/if}}
|
||||
</mj-text>
|
||||
<mj-text font-weight="500" padding="0px" font-size="18px">
|
||||
{{#if disk}}
|
||||
{{disk}}
|
||||
{{/if}}
|
||||
</mj-text>
|
||||
<mj-text font-weight="500" padding="0px" font-size="18px">
|
||||
{{#if memory}}
|
||||
{{memory}}
|
||||
{{/if}}
|
||||
</mj-text>
|
||||
|
||||
</mj-column>
|
||||
</mj-section>
|
||||
</mj-body>
|
||||
</mjml>
|
||||
@@ -222,6 +222,11 @@ const createMonitorBodyValidation = joi.object({
|
||||
url: joi.string().required(),
|
||||
isActive: joi.boolean(),
|
||||
interval: joi.number(),
|
||||
thresholds: joi.object().keys({
|
||||
usage_cpu: joi.number(),
|
||||
usage_memory: joi.number(),
|
||||
usage_disk: joi.number(),
|
||||
}),
|
||||
notifications: joi.array().items(joi.object()),
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user