From 2cd2a6d917e55f7ab30b01ec49975cea5cef73b9 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Mon, 16 Feb 2026 21:58:58 +0000 Subject: [PATCH] pagerduty --- .../notificationProviders/pagerduty.ts | 100 ++++++++++++++++++ .../infrastructure/notificationsService.ts | 9 ++ 2 files changed, 109 insertions(+) diff --git a/server/src/service/infrastructure/notificationProviders/pagerduty.ts b/server/src/service/infrastructure/notificationProviders/pagerduty.ts index 291cd7de5..2a51eb341 100644 --- a/server/src/service/infrastructure/notificationProviders/pagerduty.ts +++ b/server/src/service/infrastructure/notificationProviders/pagerduty.ts @@ -3,6 +3,7 @@ import got from "got"; import type { Monitor, Notification, MonitorStatusResponse } from "@/types/index.js"; import type { MonitorActionDecision } from "@/service/infrastructure/SuperSimpleQueue/SuperSimpleQueueHelper.js"; import { INotificationProvider } from "@/service/index.js"; +import type { NotificationMessage } from "@/types/notificationMessage.js"; import { buildHardwareAlerts, buildHardwareNotificationMessage, @@ -105,4 +106,103 @@ export class PagerDutyProvider implements INotificationProvider { return false; } } + + /** + * New unified message format implementation + */ + async sendMessage(notification: Notification, message: NotificationMessage): Promise { + if (!notification.address) { + this.logger.warn({ + message: "PagerDuty notification missing routing key", + service: SERVICE_NAME, + method: "sendMessage", + }); + return false; + } + + try { + const payload = this.buildPagerDutyPayload(notification, message); + await got.post("https://events.pagerduty.com/v2/enqueue", { + json: payload, + }); + this.logger.info({ + message: "[NEW] PagerDuty notification sent via sendMessage", + service: SERVICE_NAME, + method: "sendMessage", + }); + return true; + } catch (error) { + const err = error as Error; + this.logger.warn({ + message: "PagerDuty notification failed", + service: SERVICE_NAME, + method: "sendMessage", + stack: err?.stack, + }); + return false; + } + } + + private buildPagerDutyPayload(notification: Notification, message: NotificationMessage): any { + // Map our notification type to PagerDuty event_action + const eventAction = message.type === "monitor_up" || message.type === "threshold_resolved" ? "resolve" : "trigger"; + + // Map severity to PagerDuty severity levels + const severityMap: Record = { + critical: "critical", + warning: "warning", + info: "info", + success: "info", + }; + + const severity = severityMap[message.severity] || "error"; + + // Build deduplication key based on monitor ID for event grouping + const dedupKey = `checkmate-${message.monitor.id}`; + + // Build summary + let summary = `${message.content.title} - ${message.content.summary}`; + + // Add threshold details to summary if present + if (message.content.thresholds && message.content.thresholds.length > 0) { + const thresholdInfo = message.content.thresholds + .map((t) => `${t.metric.toUpperCase()}: ${t.formattedValue}/${t.threshold}${t.unit}`) + .join(", "); + summary += ` [${thresholdInfo}]`; + } + + // Build custom details + const customDetails: any = { + monitor_name: message.monitor.name, + monitor_url: message.monitor.url, + monitor_type: message.monitor.type, + monitor_status: message.monitor.status, + notification_type: message.type, + }; + + if (message.content.thresholds && message.content.thresholds.length > 0) { + customDetails.threshold_breaches = message.content.thresholds.map((t) => ({ + metric: t.metric, + current: t.formattedValue, + threshold: `${t.threshold}${t.unit}`, + })); + } + + if (message.content.details && message.content.details.length > 0) { + customDetails.details = message.content.details; + } + + return { + routing_key: notification.address, + dedup_key: dedupKey, + event_action: eventAction, + payload: { + summary, + severity, + source: message.monitor.url, + timestamp: message.content.timestamp.toISOString(), + custom_details: customDetails, + }, + }; + } } diff --git a/server/src/service/infrastructure/notificationsService.ts b/server/src/service/infrastructure/notificationsService.ts index 0c6918510..eec41a0a5 100644 --- a/server/src/service/infrastructure/notificationsService.ts +++ b/server/src/service/infrastructure/notificationsService.ts @@ -99,6 +99,15 @@ export class NotificationsService implements INotificationsService { return await this.matrixProvider.sendMessage(notification, notificationMessage); } + if (notification.type === "pager_duty" && this.pagerDutyProvider.sendMessage && notificationMessage) { + this.logger.info({ + message: "[NEW] Using sendMessage for pagerduty", + service: SERVICE_NAME, + method: "send", + }); + return await this.pagerDutyProvider.sendMessage(notification, notificationMessage); + } + // Fallback to existing sendAlert for all providers switch (notification.type) { case "email":