mirror of
https://github.com/bluewave-labs/Checkmate.git
synced 2026-05-18 23:48:43 -05:00
remove old service
This commit is contained in:
@@ -5,7 +5,6 @@ import MongoDB from "../db/MongoDB.js";
|
||||
import NetworkService from "../service/infrastructure/networkService.js";
|
||||
import EmailService from "../service/infrastructure/emailService.js";
|
||||
import BufferService from "../service/infrastructure/bufferService.js";
|
||||
import NotificationUtils from "../service/infrastructure/notificationUtils.js";
|
||||
import {
|
||||
NotificationsService,
|
||||
StatusService,
|
||||
@@ -222,12 +221,6 @@ export const initializeServices = async ({
|
||||
|
||||
const statusService = new StatusService({ db, logger, buffer: bufferService, monitorsRepository });
|
||||
|
||||
const notificationUtils = new NotificationUtils({
|
||||
stringService,
|
||||
emailService,
|
||||
settingsService,
|
||||
});
|
||||
|
||||
const webhookProvider = new WebhookProvider(logger);
|
||||
const slackProvider = new SlackProvider(logger);
|
||||
const emailProvider = new EmailProvider(emailService, logger);
|
||||
|
||||
@@ -1,128 +0,0 @@
|
||||
const SERVICE_NAME = "NotificationService";
|
||||
import Matrix from "./notificationProviders/matrix.js";
|
||||
|
||||
class NotificationService {
|
||||
static SERVICE_NAME = SERVICE_NAME;
|
||||
|
||||
constructor({ emailService, db, logger, networkService, stringService, notificationUtils }) {
|
||||
this.emailService = emailService;
|
||||
this.db = db;
|
||||
this.logger = logger;
|
||||
this.networkService = networkService;
|
||||
this.stringService = stringService;
|
||||
this.notificationUtils = notificationUtils;
|
||||
}
|
||||
|
||||
get serviceName() {
|
||||
return NotificationService.SERVICE_NAME;
|
||||
}
|
||||
|
||||
sendNotification = async ({ notification, subject, content, html, discordContent = null, webhookBody = null }) => {
|
||||
const { type, address } = notification;
|
||||
|
||||
if (type === "email") {
|
||||
const messageId = await this.emailService.sendEmail(address, subject, html);
|
||||
if (!messageId) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Create a body for webhooks
|
||||
let body = { text: content };
|
||||
if (type === "discord") {
|
||||
body = !discordContent ? { content } : discordContent;
|
||||
}
|
||||
if (type === "webhook") {
|
||||
body = !webhookBody ? { content } : webhookBody;
|
||||
}
|
||||
if (type === "slack" || type === "discord" || type === "webhook") {
|
||||
const response = await this.networkService.requestWebhook(type, address, body);
|
||||
return response.status;
|
||||
}
|
||||
if (type === "pager_duty") {
|
||||
const response = await this.networkService.requestPagerDuty({
|
||||
message: content,
|
||||
monitorUrl: subject,
|
||||
routingKey: address,
|
||||
});
|
||||
|
||||
return response;
|
||||
}
|
||||
if (type === "matrix") {
|
||||
const { friendlyName, homeserverUrl, accessToken, roomId } = notification;
|
||||
const monitorName = subject;
|
||||
const message = content;
|
||||
const matrix = new Matrix({ networkService: this.networkService, logger: this.logger });
|
||||
const success = await matrix.send({ friendlyName, homeserverUrl, accessToken, roomId, message, monitorName });
|
||||
return success;
|
||||
}
|
||||
};
|
||||
|
||||
async handleNotifications(networkResponse) {
|
||||
const { monitor, statusChanged, prevStatus } = networkResponse;
|
||||
const { type } = monitor;
|
||||
if (type !== "hardware" && statusChanged === false) return false;
|
||||
// if prevStatus is undefined, monitor is resuming, we're done
|
||||
if (type !== "hardware" && prevStatus === undefined) return false;
|
||||
|
||||
const notificationIDs = networkResponse.monitor?.notifications ?? [];
|
||||
if (notificationIDs.length === 0) return false;
|
||||
if (networkResponse.monitor.type === "hardware") {
|
||||
const thresholds = networkResponse?.monitor?.thresholds;
|
||||
|
||||
if (thresholds === undefined) return false; // No thresholds set, we're done
|
||||
const metrics = networkResponse?.payload?.data ?? null;
|
||||
if (metrics === null) return false; // No metrics, we're done
|
||||
|
||||
const [alerts, discordContent] = await this.notificationUtils.buildHardwareAlerts(networkResponse);
|
||||
if (alerts.length === 0) return false;
|
||||
|
||||
const { subject, html } = await this.notificationUtils.buildHardwareEmail(networkResponse, alerts);
|
||||
const content = await this.notificationUtils.buildHardwareNotificationMessage(alerts, monitor);
|
||||
const webhookBody = await this.notificationUtils.buildHardwareWebhookBody(alerts, monitor);
|
||||
const success = await this.notifyAll({ notificationIDs, subject, html, content, discordContent, webhookBody });
|
||||
return success;
|
||||
}
|
||||
|
||||
// Status monitors
|
||||
const { subject, html } = await this.notificationUtils.buildStatusEmail(networkResponse);
|
||||
const [content, discordContent] = await this.notificationUtils.buildWebhookMessage(networkResponse);
|
||||
const success = this.notifyAll({ notificationIDs, subject, html, content, discordContent });
|
||||
return success;
|
||||
}
|
||||
|
||||
async notifyAll({ notificationIDs, subject, html, content, discordContent = null, webhookBody = null }) {
|
||||
const notifications = await this.db.notificationModule.getNotificationsByIds(notificationIDs);
|
||||
// Map each notification to a test promise
|
||||
const promises = notifications.map(async (notification) => {
|
||||
try {
|
||||
await this.sendNotification({ notification, subject, content, html, discordContent, webhookBody });
|
||||
return true;
|
||||
} catch (err) {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
const results = await Promise.all(promises);
|
||||
return results.every((r) => r === true);
|
||||
}
|
||||
|
||||
async getTestNotification() {
|
||||
const html = await this.notificationUtils.buildTestEmail();
|
||||
const content = "This is a test notification";
|
||||
const subject = "Test Notification";
|
||||
return { subject, html, content };
|
||||
}
|
||||
|
||||
async testAllNotifications(notificationIDs) {
|
||||
const { subject, html, content } = await this.getTestNotification();
|
||||
return this.notifyAll({ notificationIDs, subject, html, content });
|
||||
}
|
||||
|
||||
async sendTestNotification(notification) {
|
||||
const { subject, html, content } = await this.getTestNotification();
|
||||
const success = await this.sendNotification({ notification, subject, content, html });
|
||||
return success;
|
||||
}
|
||||
}
|
||||
|
||||
export default NotificationService;
|
||||
@@ -1,205 +0,0 @@
|
||||
const SERVICE_NAME = "NotificationUtils";
|
||||
|
||||
class NotificationUtils {
|
||||
static SERVICE_NAME = SERVICE_NAME;
|
||||
|
||||
constructor({ stringService, emailService, settingsService }) {
|
||||
this.stringService = stringService;
|
||||
this.emailService = emailService;
|
||||
this.settingsService = settingsService;
|
||||
}
|
||||
|
||||
get serviceName() {
|
||||
return NotificationUtils.SERVICE_NAME;
|
||||
}
|
||||
|
||||
buildTestEmail = async () => {
|
||||
const context = { testName: "Monitoring System" };
|
||||
const html = await this.emailService.buildEmail("testEmailTemplate", context);
|
||||
return html;
|
||||
};
|
||||
|
||||
buildStatusEmail = async (networkResponse) => {
|
||||
const { monitor, status, prevStatus } = networkResponse;
|
||||
const template = prevStatus === false ? "serverIsUpTemplate" : "serverIsDownTemplate";
|
||||
const context = { monitor: monitor.name, url: monitor.url };
|
||||
const subject = `Monitor ${monitor.name} is ${status === true ? "up" : "down"}`;
|
||||
const html = await this.emailService.buildEmail(template, context);
|
||||
return { subject, html };
|
||||
};
|
||||
|
||||
buildWebhookMessage = (networkResponse) => {
|
||||
const { monitor, status, code, timestamp } = networkResponse;
|
||||
// Format timestamp using the local system timezone
|
||||
const formatTime = (timestamp) => {
|
||||
const date = new Date(timestamp);
|
||||
|
||||
// Get timezone abbreviation and format the date
|
||||
const timeZoneAbbr = date.toLocaleTimeString("en-US", { timeZoneName: "short" }).split(" ").pop();
|
||||
|
||||
// Format the date with readable format
|
||||
return (
|
||||
date
|
||||
.toLocaleString("en-US", {
|
||||
year: "numeric",
|
||||
month: "2-digit",
|
||||
day: "2-digit",
|
||||
hour: "2-digit",
|
||||
minute: "2-digit",
|
||||
second: "2-digit",
|
||||
hour12: false,
|
||||
})
|
||||
.replace(/(\d+)\/(\d+)\/(\d+),\s/, "$3-$1-$2 ") +
|
||||
" " +
|
||||
timeZoneAbbr
|
||||
);
|
||||
};
|
||||
|
||||
// Get formatted time
|
||||
const formattedTime = timestamp ? formatTime(timestamp) : formatTime(new Date().getTime());
|
||||
|
||||
// Create different messages based on status with extra spacing
|
||||
let messageText;
|
||||
if (status === true) {
|
||||
messageText = this.stringService.monitorUpAlert
|
||||
.replace("{monitorName}", monitor.name)
|
||||
.replace("{time}", formattedTime)
|
||||
.replace("{code}", code || "Unknown");
|
||||
} else {
|
||||
messageText = this.stringService.monitorDownAlert
|
||||
.replace("{monitorName}", monitor.name)
|
||||
.replace("{time}", formattedTime)
|
||||
.replace("{code}", code || "Unknown");
|
||||
}
|
||||
const dn = this.stringService.discordNotification;
|
||||
let discordMessageText = {
|
||||
embeds: [
|
||||
{
|
||||
title: status
|
||||
? dn.uptimeAlert.replace("{monitorName}", monitor?.name ?? dn.unknown)
|
||||
: dn.downtimeAlert.replace("{monitorName}", monitor?.name ?? dn.unknown),
|
||||
color: status ? 5763719 : 15548997,
|
||||
|
||||
fields: [
|
||||
{ name: dn.monitor, value: monitor?.name ?? dn.unknown, inline: true },
|
||||
{ name: dn.status, value: status ? dn.up : dn.down, inline: true },
|
||||
{ name: dn.statusCode, value: String(code ?? dn.unknown), inline: true },
|
||||
{ name: dn.time, value: formattedTime, inline: true },
|
||||
{ name: dn.url, value: monitor?.url ?? dn.unknown, inline: false },
|
||||
],
|
||||
footer: { text: dn.checkmate },
|
||||
},
|
||||
],
|
||||
};
|
||||
return [messageText, discordMessageText];
|
||||
};
|
||||
|
||||
buildHardwareAlerts = async (networkResponse) => {
|
||||
const monitor = networkResponse?.monitor;
|
||||
const thresholds = networkResponse?.monitor?.thresholds;
|
||||
const { usage_cpu: cpuThreshold = -1, usage_memory: memoryThreshold = -1, usage_disk: diskThreshold = -1 } = thresholds;
|
||||
|
||||
const metrics = networkResponse?.payload?.data;
|
||||
const { cpu: { usage_percent: cpuUsage = -1 } = {}, memory: { usage_percent: memoryUsage = -1 } = {}, disk = [] } = metrics;
|
||||
|
||||
const { clientHost } = this.settingsService.getSettings();
|
||||
|
||||
const alerts = {
|
||||
cpu: cpuThreshold !== -1 && cpuUsage > cpuThreshold ? true : false,
|
||||
memory: memoryThreshold !== -1 && memoryUsage > memoryThreshold ? true : false,
|
||||
disk: disk?.some((d) => diskThreshold !== -1 && typeof d?.usage_percent === "number" && d?.usage_percent > diskThreshold) ?? false,
|
||||
};
|
||||
|
||||
const alertsToSend = [];
|
||||
const discordEmbeds = [];
|
||||
const monitorInfoFields = [
|
||||
{ name: "Monitor", value: monitor.name, inline: true },
|
||||
{ name: "URL", value: monitor.url, inline: false },
|
||||
];
|
||||
const goToIncidentField = { name: `Go to incident`, value: `${clientHost}/infrastructure/${monitor._id}` };
|
||||
const formatDiscordAlert = {
|
||||
cpu: () => ({
|
||||
title: "CPU alert",
|
||||
description: `Your current CPU usage (${(cpuUsage * 100).toFixed(0)}%) is above your threshold (${(cpuThreshold * 100).toFixed(0)}%)`,
|
||||
color: 15548997,
|
||||
fields: [...monitorInfoFields, goToIncidentField],
|
||||
footer: { text: "Checkmate" },
|
||||
}),
|
||||
|
||||
memory: () => ({
|
||||
title: "Memory alert",
|
||||
description: `Your current memory usage (${(memoryUsage * 100).toFixed(0)}%) is above your threshold (${(memoryThreshold * 100).toFixed(0)}%)`,
|
||||
color: 15548997,
|
||||
fields: [...monitorInfoFields, goToIncidentField],
|
||||
footer: { text: "Checkmate" },
|
||||
}),
|
||||
|
||||
disk: () => ({
|
||||
title: "Disk alert",
|
||||
description: `Your current disk usage is above your threshold (${(diskThreshold * 100).toFixed(0)}%)`,
|
||||
color: 15548997,
|
||||
footer: { text: "Checkmate" },
|
||||
fields: [
|
||||
...monitorInfoFields,
|
||||
...(Array.isArray(disk) ? disk : []).map((d, idx) => ({
|
||||
name: `Disk ${idx}`,
|
||||
value: `${(d?.usage_percent * 100).toFixed(0)}%`,
|
||||
inline: true,
|
||||
})),
|
||||
goToIncidentField,
|
||||
],
|
||||
}),
|
||||
};
|
||||
const alertTypes = ["cpu", "memory", "disk"];
|
||||
for (const type of alertTypes) {
|
||||
// Iterate over each alert type to see if any need to be decremented
|
||||
if (alerts[type] === true) {
|
||||
monitor[`${type}AlertThreshold`]--; // Decrement threshold if an alert is triggered
|
||||
|
||||
if (monitor[`${type}AlertThreshold`] <= 0) {
|
||||
// If threshold drops below 0, reset and send notification
|
||||
monitor[`${type}AlertThreshold`] = monitor.alertThreshold;
|
||||
|
||||
const formatAlert = {
|
||||
cpu: () => `Your current CPU usage (${(cpuUsage * 100).toFixed(0)}%) is above your threshold (${(cpuThreshold * 100).toFixed(0)}%)`,
|
||||
memory: () =>
|
||||
`Your current memory usage (${(memoryUsage * 100).toFixed(0)}%) is above your threshold (${(memoryThreshold * 100).toFixed(0)}%)`,
|
||||
disk: () =>
|
||||
`Your current disk usage: ${disk
|
||||
.map((d, idx) => `(Disk${idx}: ${(d.usage_percent * 100).toFixed(0)}%)`)
|
||||
.join(", ")} is above your threshold (${(diskThreshold * 100).toFixed(0)}%)`,
|
||||
};
|
||||
alertsToSend.push(formatAlert[type]());
|
||||
discordEmbeds.push(formatDiscordAlert[type]());
|
||||
}
|
||||
}
|
||||
}
|
||||
await monitor.save();
|
||||
const discordPayload = discordEmbeds.length ? { embeds: discordEmbeds } : null;
|
||||
return [alertsToSend, discordPayload];
|
||||
};
|
||||
|
||||
buildHardwareEmail = async (networkResponse, alerts) => {
|
||||
const { monitor } = networkResponse;
|
||||
const template = "hardwareIncidentTemplate";
|
||||
const context = { monitor: monitor.name, url: monitor.url, alerts };
|
||||
const subject = `Monitor ${monitor.name} infrastructure alerts`;
|
||||
const html = await this.emailService.buildEmail(template, context);
|
||||
return { subject, html };
|
||||
};
|
||||
|
||||
buildHardwareNotificationMessage = (alerts, monitor) => {
|
||||
const { clientHost } = this.settingsService.getSettings();
|
||||
const alertsHeader = [`Monitor: ${monitor.name}`, `URL: ${monitor.url}`];
|
||||
const alertFooter = [`Go to incident: ${clientHost}/infrastructure/${monitor._id}`];
|
||||
const alertText = alerts.length > 0 ? [...alertsHeader, ...alerts, ...alertFooter] : [];
|
||||
return alertText.map((alert) => alert).join("\n");
|
||||
};
|
||||
buildHardwareWebhookBody = (alerts, monitor) => {
|
||||
const content = alerts.map((alert) => alert).join("\n");
|
||||
const body = { text: content, name: monitor.name, url: monitor.url };
|
||||
return body;
|
||||
};
|
||||
}
|
||||
|
||||
export default NotificationUtils;
|
||||
Reference in New Issue
Block a user