mirror of
https://github.com/bluewave-labs/Checkmate.git
synced 2026-05-18 23:48:43 -05:00
providers
This commit is contained in:
@@ -2,6 +2,7 @@ import type { Notification } from "@/types/index.js";
|
||||
export interface INotificationsRepository {
|
||||
// create
|
||||
// fetch
|
||||
findNotificationsByIds(ids: string[]): Promise<Notification[]>;
|
||||
// update
|
||||
// delete
|
||||
}
|
||||
|
||||
@@ -35,6 +35,12 @@ class MongoNotificationsRepository implements INotificationsRepository {
|
||||
updatedAt: toDateString(doc.updatedAt),
|
||||
};
|
||||
};
|
||||
|
||||
findNotificationsByIds = async (ids: string[]) => {
|
||||
const mongoIds = ids.map((id) => new mongoose.Types.ObjectId(id));
|
||||
const documents = await NotificationModel.find({ _id: { $in: mongoIds } });
|
||||
return this.mapDocuments(documents);
|
||||
};
|
||||
}
|
||||
|
||||
export default MongoNotificationsRepository;
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
export * from "@/service/business/monitorService.js";
|
||||
export * from "@/service/infrastructure/networkService.js";
|
||||
export * from "@/service/infrastructure/notificationProviders/webhook.js";
|
||||
export * from "@/service/infrastructure/notificationProviders/discord.js";
|
||||
export * from "@/service/infrastructure/notificationProviders/email.js";
|
||||
export * from "@/service/infrastructure/notificationProviders/INotificationProvider.js";
|
||||
export * from "@/service/infrastructure/notificationProviders/slack.js";
|
||||
export * from "@/service/infrastructure/notificationProviders/webhook.js";
|
||||
export * from "@/service/infrastructure/notificationsService.js";
|
||||
export * from "@/service/infrastructure/statusService.js";
|
||||
export * from "@/service/infrastructure/notificationProviders/webhook.js";
|
||||
|
||||
@@ -83,7 +83,7 @@ class SuperSimpleQueueHelper {
|
||||
// Step 4. Update monitor status
|
||||
const statusChangeResult = await this.statusService.updateMonitorStatus(status, check);
|
||||
|
||||
// Step 5 handle notifications
|
||||
// Step 5 handle notifications (best effort, continue even in event of failure, don't wait)
|
||||
this.notificationsService.handleNotifications(monitor, status, statusChangeResult.prevStatus, statusChangeResult.statusChanged);
|
||||
|
||||
// this.notificationService
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import type { Monitor, Notification, Alert, MonitorStatusResponse } from "@/types/index.js";
|
||||
|
||||
export interface INotificationProvider {
|
||||
buildAlert: (monitor: Monitor, monitorStatusResponse: MonitorStatusResponse) => Alert;
|
||||
sendAlert: (alert: Alert, notification: Notification) => Promise<boolean>;
|
||||
sendAlert: (notification: Notification, monitor: Monitor, monitorStatusResponse: MonitorStatusResponse) => Promise<boolean>;
|
||||
sendTestAlert(notification: Notification): Promise<boolean>;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
import type { Monitor, Notification, MonitorStatusResponse } from "@/types/index.js";
|
||||
import { INotificationProvider } from "@/service/index.js";
|
||||
export class DiscordProvider implements INotificationProvider {
|
||||
sendAlert = async (notification: Notification, monitor: Monitor, monitorStatusResponse: MonitorStatusResponse) => {
|
||||
return false;
|
||||
};
|
||||
sendTestAlert = async (notification: Notification) => {
|
||||
return false;
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
import type { Monitor, Notification, MonitorStatusResponse } from "@/types/index.js";
|
||||
import { INotificationProvider } from "@/service/index.js";
|
||||
|
||||
export class EmailProvider implements INotificationProvider {
|
||||
async sendAlert(notification: Notification, monitor: Monitor, monitorStatusResponse: MonitorStatusResponse): Promise<boolean> {
|
||||
return false;
|
||||
}
|
||||
|
||||
async sendTestAlert(notification: Notification): Promise<boolean> {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
import type { Monitor, Notification, MonitorStatusResponse } from "@/types/index.js";
|
||||
import { INotificationProvider } from "@/service/index.js";
|
||||
|
||||
export class SlackProvider implements INotificationProvider {
|
||||
async sendAlert(notification: Notification, monitor: Monitor, monitorStatusResponse: MonitorStatusResponse): Promise<boolean> {
|
||||
return false;
|
||||
}
|
||||
|
||||
async sendTestAlert(notification: Notification): Promise<boolean> {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -93,13 +93,19 @@ export const buildHardwareAlerts = (
|
||||
return { alertsToSend, discordPayload };
|
||||
};
|
||||
|
||||
const buildHardwareNotificationMessage = (clientHost: string, alerts: any, monitor: Monitor) => {
|
||||
export const buildHardwareNotificationMessage = (clientHost: string, alerts: any, monitor: Monitor) => {
|
||||
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");
|
||||
};
|
||||
|
||||
export const buildHardwareWebhookBody = (alerts: string[], monitor: Monitor): { text: string; name: string; url: string } => {
|
||||
const content = alerts.map((alert) => alert).join("\n");
|
||||
const body = { text: content, name: monitor.name, url: monitor.url };
|
||||
return body;
|
||||
};
|
||||
|
||||
export const shouldSendHardwareAlert = (monitor: Monitor, networkResponse: MonitorStatusResponse): boolean => {
|
||||
const thresholds = monitor.thresholds || {};
|
||||
const { usage_cpu: cpuThreshold = -1, usage_memory: memoryThreshold = -1, usage_disk: diskThreshold = -1 } = thresholds;
|
||||
@@ -125,3 +131,9 @@ export const shouldSendHardwareAlert = (monitor: Monitor, networkResponse: Monit
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
export const buildWebhookBody = (monitor: Monitor, monitorStatusResponse: MonitorStatusResponse) => {
|
||||
const { status, code } = monitorStatusResponse;
|
||||
const formattedTime = Date.now().toLocaleString();
|
||||
return `Monitor: ${monitor.name}\nTime: ${formattedTime}\nStatus: ${status ? "UP" : "DOWN"}\n Status Code: ${code}\n\u200B\n`;
|
||||
};
|
||||
|
||||
@@ -1,15 +1,47 @@
|
||||
import type { Monitor, Alert, Notification, MonitorStatusResponse } from "@/types/index.js";
|
||||
import { INotificationProvider } from "@/service/index.js";
|
||||
import { buildHardwareAlerts, buildHardwareWebhookBody, buildWebhookBody } from "@/service/infrastructure/notificationProviders/utils.js";
|
||||
import got from "got";
|
||||
|
||||
export class WebhookProvider implements INotificationProvider {
|
||||
buildAlert = (monitor: Monitor, monitorStatusResponse: MonitorStatusResponse) => {
|
||||
return null;
|
||||
private getHardwareContent = (monitor: Monitor, monitorStatusResponse: MonitorStatusResponse) => {
|
||||
const { alertsToSend } = buildHardwareAlerts("HOST_PLACEHOLDER", monitor, monitorStatusResponse);
|
||||
const body = buildHardwareWebhookBody(alertsToSend, monitor);
|
||||
return body;
|
||||
};
|
||||
|
||||
sendAlert = async (alert: Alert) => {
|
||||
return false;
|
||||
private getContent = (monitor: Monitor, monitorStatusResponse: MonitorStatusResponse) => {
|
||||
const body = buildWebhookBody(monitor, monitorStatusResponse);
|
||||
return body;
|
||||
};
|
||||
|
||||
sendTestAlert(notification: Notification): Promise<boolean> {
|
||||
sendAlert = async (notification: Notification, monitor: Monitor, monitorStatusResponse: MonitorStatusResponse) => {
|
||||
let body;
|
||||
if (monitor.type === "hardware") {
|
||||
body = this.getHardwareContent(monitor, monitorStatusResponse);
|
||||
} else {
|
||||
body = this.getContent(monitor, monitorStatusResponse);
|
||||
}
|
||||
|
||||
if (!notification.address) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
await got.post(notification.address, {
|
||||
json: body,
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
responseType: "json",
|
||||
});
|
||||
return true;
|
||||
} catch (error) {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
sendTestAlert = async (notification: Notification) => {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { HardwareStatusPayload, Monitor, MonitorStatusResponse, Notification } from "@/types/index.js";
|
||||
import { NotificationModel } from "@/db/models/index.js";
|
||||
import { buildHardwareAlerts, shouldSendHardwareAlert } from "@/service/infrastructure/notificationProviders/utils.js";
|
||||
import { shouldSendHardwareAlert } from "@/service/infrastructure/notificationProviders/utils.js";
|
||||
import { INotificationsRepository } from "@/repositories/index.js";
|
||||
import { WebhookProvider } from "@/service/index.js";
|
||||
export interface INotificationsService {
|
||||
handleNotifications: (
|
||||
monitor: Monitor,
|
||||
@@ -13,21 +13,36 @@ export interface INotificationsService {
|
||||
|
||||
export class NotificationsService implements INotificationsService {
|
||||
private notificationsRepository: INotificationsRepository;
|
||||
constructor(notificationsRepository: INotificationsRepository) {
|
||||
private webhookProvider: WebhookProvider;
|
||||
|
||||
constructor(notificationsRepository: INotificationsRepository, webhookProvider: WebhookProvider) {
|
||||
this.notificationsRepository = notificationsRepository;
|
||||
this.webhookProvider = webhookProvider;
|
||||
}
|
||||
|
||||
private send = (notification: Notification, monitor: Monitor, monitorStatusResponse: MonitorStatusResponse) => {
|
||||
console.log(notification);
|
||||
private send = async (notification: Notification, monitor: Monitor, monitorStatusResponse: MonitorStatusResponse): Promise<boolean> => {
|
||||
switch (notification.type) {
|
||||
case " email": {
|
||||
}
|
||||
case "webhook": {
|
||||
return await this.webhookProvider.sendAlert(notification, monitor, monitorStatusResponse);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
private sendNotifications = async (monitor: Monitor, monitorStatusResponse: MonitorStatusResponse) => {
|
||||
const notifications = await this.notificationsRepository.findByMonitorId(monitor.id);
|
||||
console.log({ notifications });
|
||||
const tasks = notifications.map((notification) => {
|
||||
this.send(notification, monitor, monitorStatusResponse);
|
||||
});
|
||||
return true;
|
||||
const notificationIds = monitor.notifications ?? [];
|
||||
const notifications = await this.notificationsRepository.findNotificationsByIds(notificationIds);
|
||||
const tasks = notifications.map((notification) => this.send(notification, monitor, monitorStatusResponse));
|
||||
const outcomes = await Promise.all(tasks);
|
||||
const succeeded = outcomes.filter(Boolean).length;
|
||||
const failed = outcomes.length - succeeded;
|
||||
if (failed > 0) {
|
||||
// logger.warn(`Notification send completed with ${succeeded} success, ${failed} failure(s)`);
|
||||
}
|
||||
// Return true if all notificaitons succeeded
|
||||
return succeeded === notifications.length;
|
||||
};
|
||||
|
||||
handleNotifications = async (
|
||||
@@ -51,12 +66,11 @@ export class NotificationsService implements INotificationsService {
|
||||
const metrics = payload?.data ?? null;
|
||||
if (metrics === null) return false; // No metrics, we're done
|
||||
|
||||
// We shoul dsend a notificaiton
|
||||
// We should send a notificaiton
|
||||
|
||||
const shouldSend = shouldSendHardwareAlert(monitor, monitorStatusResponse);
|
||||
if (shouldSend === false) return false;
|
||||
console.log(JSON.stringify(monitor, null, 2));
|
||||
console.log(JSON.stringify(monitorStatusResponse, null, 2));
|
||||
// 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);
|
||||
|
||||
Reference in New Issue
Block a user