From a86588f080b03ce88664b86517e1dc9d380dc7d0 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Sun, 18 Jan 2026 23:38:40 +0000 Subject: [PATCH] refactoring notifications --- server/src/config/services.ts | 3 +- server/src/service/index.ts | 1 + .../SuperSimpleQueueHelper.ts | 17 ++++--- .../infrastructure/notificationsService.ts | 48 +++++++++++++++++-- .../service/infrastructure/statusService.ts | 31 +++++++----- server/src/types/network.ts | 2 +- 6 files changed, 74 insertions(+), 28 deletions(-) diff --git a/server/src/config/services.ts b/server/src/config/services.ts index 92acd8868..ed1224ec7 100644 --- a/server/src/config/services.ts +++ b/server/src/config/services.ts @@ -5,10 +5,9 @@ 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 StatusService from "../service/infrastructure/statusService.js"; import NotificationUtils from "../service/infrastructure/notificationUtils.js"; import NotificationService from "../service/infrastructure/notificationService.js"; -import { NotificationsService } from "@/service/index.js"; +import { NotificationsService, StatusService } from "@/service/index.js"; import ErrorService from "../service/infrastructure/errorService.js"; import SuperSimpleQueueHelper from "../service/infrastructure/SuperSimpleQueue/SuperSimpleQueueHelper.js"; import SuperSimpleQueue from "../service/infrastructure/SuperSimpleQueue/SuperSimpleQueue.js"; diff --git a/server/src/service/index.ts b/server/src/service/index.ts index 08c02f397..fbba7714d 100644 --- a/server/src/service/index.ts +++ b/server/src/service/index.ts @@ -3,3 +3,4 @@ export * from "@/service/infrastructure/networkService.js"; export * from "@/service/infrastructure/notificationProviders/webhook.js"; export * from "@/service/infrastructure/notificationProviders/INotificationProvider.js"; export * from "@/service/infrastructure/notificationsService.js"; +export * from "@/service/infrastructure/statusService.js"; diff --git a/server/src/service/infrastructure/SuperSimpleQueue/SuperSimpleQueueHelper.ts b/server/src/service/infrastructure/SuperSimpleQueue/SuperSimpleQueueHelper.ts index c2a9e1267..21ee6e388 100644 --- a/server/src/service/infrastructure/SuperSimpleQueue/SuperSimpleQueueHelper.ts +++ b/server/src/service/infrastructure/SuperSimpleQueue/SuperSimpleQueueHelper.ts @@ -1,18 +1,19 @@ const SERVICE_NAME = "JobQueueHelper"; import type { Monitor } from "@/types/monitor.js"; import { AppError } from "@/utils/AppError.js"; -import { INetworkService, INotificationsService } from "@/service/index.js"; +import { INetworkService, INotificationsService, IStatusService } from "@/service/index.js"; class SuperSimpleQueueHelper { static SERVICE_NAME = SERVICE_NAME; private db: any; private logger: any; private networkService: INetworkService; - private statusService: any; + private statusService: IStatusService; private notificationService: any; private notificationsService: INotificationsService; private checkService: any; private buffer: any; + private incidentService: any; constructor({ db, @@ -23,15 +24,17 @@ class SuperSimpleQueueHelper { notificationsService, checkService, buffer, + incidentService, }: { db: any; logger: any; networkService: INetworkService; - statusService: any; + statusService: IStatusService; notificationService: any; notificationsService: INotificationsService; checkService: any; buffer: any; + incidentService: any; }) { this.db = db; this.logger = logger; @@ -41,6 +44,7 @@ class SuperSimpleQueueHelper { this.checkService = checkService; this.buffer = buffer; this.notificationsService = notificationsService; + this.incidentService = incidentService; } get serviceName() { @@ -82,10 +86,9 @@ class SuperSimpleQueueHelper { // Step 4. Update monitor status const statusChangeResult = await this.statusService.updateMonitorStatus(status, check); - // Step 5 handle incidents and notifications - if (statusChangeResult.statusChanged === true) { - this.notificationsService.handleNotifications(monitor, status); - } + // Step 5 handle notifications + + this.notificationsService.handleNotifications(monitor, status, statusChangeResult.prevStatus, statusChangeResult.statusChanged); // this.notificationService // .handleNotifications({ diff --git a/server/src/service/infrastructure/notificationsService.ts b/server/src/service/infrastructure/notificationsService.ts index f9fdbfea8..3dc5fe65e 100644 --- a/server/src/service/infrastructure/notificationsService.ts +++ b/server/src/service/infrastructure/notificationsService.ts @@ -1,11 +1,49 @@ -import type { Monitor, MonitorStatusResponse } from "@/types/index.js"; +import type { HardwareStatusPayload, Monitor, MonitorStatusResponse } from "@/types/index.js"; export interface INotificationsService { - handleNotifications: (monitor: Monitor, monitorStatusResponse: MonitorStatusResponse) => Promise; + handleNotifications: ( + monitor: Monitor, + monitorStatusResponse: MonitorStatusResponse, + prevStatus: boolean | undefined, + statusChanged: boolean + ) => Promise; } export class NotificationsService implements INotificationsService { - handleNotifications = async (monitor: Monitor, monitorStatusResponse: MonitorStatusResponse) => { - console.log(JSON.stringify(monitor, null, 2)); - console.log(JSON.stringify(monitorStatusResponse, null, 2)); + handleNotifications = async ( + monitor: Monitor, + monitorStatusResponse: MonitorStatusResponse, + prevStatus: boolean | undefined, + statusChanged: boolean + ) => { + const { type } = monitor; + const payload = monitorStatusResponse.payload as HardwareStatusPayload; + // If this is a non-hardeware type monitor and status did not change, we're done + if (type !== "hardware" && statusChanged === false) return false; + // if prevStatus is undefined, monitor is resuming, we're done + if (type !== "hardware" && prevStatus === undefined) return false; + + // Deal with hardware thresholds + if (type === "hardware") { + const thresholds = monitor.thresholds; + + if (thresholds === undefined) return false; // No thresholds set, we're done + const metrics = payload?.data ?? null; + if (metrics === null) return false; // No metrics, we're done + + // We shoul dsend a notificaiton + + console.log(JSON.stringify(monitor, null, 2)); + console.log(JSON.stringify(monitorStatusResponse, null, 2)); + // 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 true; + } + + return false; }; } diff --git a/server/src/service/infrastructure/statusService.ts b/server/src/service/infrastructure/statusService.ts index c9aab706b..3d9591c02 100755 --- a/server/src/service/infrastructure/statusService.ts +++ b/server/src/service/infrastructure/statusService.ts @@ -12,7 +12,17 @@ import type { } from "@/types/index.js"; const SERVICE_NAME = "StatusService"; -class StatusService { +export interface IStatusService { + updateRunningStats({ monitor, networkResponse }: { monitor: Monitor; networkResponse: any }): Promise; + getStatusString(status: boolean | undefined): string; + handleIncidentForCheck(check: any, monitor: Monitor, action: any, errorContext?: string): Promise; + updateMonitorStatus( + statusResponse: MonitorStatusResponse, + check: Check + ): Promise; +} + +export class StatusService implements IStatusService { static SERVICE_NAME = SERVICE_NAME; private db: any; private logger: any; @@ -108,11 +118,10 @@ class StatusService { return "unknown"; }; - handleIncidentForCheck = async (check: any, monitor: Monitor, action: any, errorContext = "incident handling") => { + handleIncidentForCheck = async (check: Check, monitor: Monitor, action: any, errorContext = "incident handling") => { try { - let savedCheck = check; - - if (!check._id) { + let savedCheck; + if (!check.id) { try { const checkModel = new CheckModel(check); savedCheck = await checkModel.save(); @@ -130,7 +139,7 @@ class StatusService { } } - if (savedCheck && savedCheck._id) { + if (savedCheck && savedCheck.id) { try { this.buffer.addIncidentToBuffer({ monitor, check: savedCheck, action }); } catch (incidentError: any) { @@ -167,13 +176,10 @@ class StatusService { this.updateRunningStats({ monitor, networkResponse: statusResponse }); // If the status window size has changed, empty - while (monitor.statusWindow.length > monitor.statusWindowSize) { - monitor.statusWindow.shift(); - } - // Update status sliding window + monitor.statusWindow = monitor.statusWindow || []; monitor.statusWindow.push(status); - if (monitor.statusWindow.length > monitor.statusWindowSize) { + while (monitor.statusWindow.length > monitor.statusWindowSize) { monitor.statusWindow.shift(); } @@ -181,9 +187,9 @@ class StatusService { monitor.status = status; } + const prevStatus = monitor.status; let newStatus = monitor.status; let statusChanged = false; - const prevStatus = monitor.status; // Return early if not enough data points if (monitor.statusWindow.length < monitor.statusWindowSize) { @@ -316,4 +322,3 @@ class StatusService { } }; } -export default StatusService; diff --git a/server/src/types/network.ts b/server/src/types/network.ts index 10f89842f..6bed40316 100644 --- a/server/src/types/network.ts +++ b/server/src/types/network.ts @@ -13,7 +13,7 @@ import type { MonitorType, } from "@/types/index.js"; -export interface MonitorStatusResponse { +export interface MonitorStatusResponse { monitorId: string; teamId: string; type: MonitorType;