This commit is contained in:
Alex Holliday
2026-02-17 01:47:57 +00:00
parent f983863c72
commit fbb0e940c6
3 changed files with 93 additions and 0 deletions
@@ -65,6 +65,7 @@ class EmailService {
passwordResetTemplate: this.loadTemplate("passwordReset"),
hardwareIncidentTemplate: this.loadTemplate("hardwareIncident"),
testEmailTemplate: this.loadTemplate("testEmailTemplate"),
unifiedNotificationTemplate: this.loadTemplate("unifiedNotification"),
};
};
@@ -3,6 +3,7 @@ import type { Monitor, Notification, MonitorStatusResponse } from "@/types/index
import { INotificationProvider } from "@/service/index.js";
import type { MonitorActionDecision } from "@/service/infrastructure/SuperSimpleQueue/SuperSimpleQueueHelper.js";
import { buildHardwareAlerts, buildHardwareEmail, buildEmail, buildTestEmail } from "@/service/infrastructure/notificationProviders/utils.js";
import type { NotificationMessage } from "@/types/notificationMessage.js";
export class EmailProvider implements INotificationProvider {
private emailService: any;
@@ -90,4 +91,86 @@ export class EmailProvider implements INotificationProvider {
}
return true;
}
async sendMessage(notification: Notification, message: NotificationMessage): Promise<boolean> {
if (!notification.address) {
return false;
}
const subject = this.buildSubject(message);
const html = await this.buildEmailFromMessage(message);
const messageId = await this.emailService.sendEmail(notification.address, subject, html);
if (!messageId) {
this.logger.warn({
message: "Email notification failed via sendMessage",
service: SERVICE_NAME,
method: "sendMessage",
});
return false;
}
return true;
}
private buildSubject(message: NotificationMessage): string {
switch (message.type) {
case "monitor_down":
return `Monitor ${message.monitor.name} is down`;
case "monitor_up":
return `Monitor ${message.monitor.name} is back up`;
case "threshold_breach":
return `Monitor ${message.monitor.name} threshold breached`;
case "threshold_resolved":
return `Monitor ${message.monitor.name} thresholds resolved`;
default:
return `Alert: ${message.monitor.name}`;
}
}
private async buildEmailFromMessage(message: NotificationMessage): Promise<string> {
const context = {
title: message.content.title,
summary: message.content.summary,
monitorName: message.monitor.name,
monitorUrl: message.monitor.url,
monitorType: message.monitor.type,
monitorStatus: message.monitor.status,
headerColor: this.getColorForSeverity(message.severity),
thresholds: message.content.thresholds,
details: message.content.details,
incidentUrl: message.content.incident?.url,
};
this.logger.info({
message: "[DEBUG] Building email from message",
service: SERVICE_NAME,
method: "buildEmailFromMessage",
details: { context },
});
return await this.emailService.buildEmail("unifiedNotificationTemplate", context);
const html = await this.emailService.buildEmail("unifiedNotificationTemplate", context);
this.logger.info({
message: "[DEBUG] Email HTML generated",
service: SERVICE_NAME,
method: "buildEmailFromMessage",
details: {
htmlLength: html?.length || 0,
htmlPreview: html?.substring(0, 200),
},
});
return html;
}
private getColorForSeverity(severity: string): string {
const colorMap: Record<string, string> = {
critical: "red",
warning: "#f59e0b",
info: "#3b82f6",
success: "green",
};
return colorMap[severity] ?? "#3b82f6";
}
}
@@ -117,6 +117,15 @@ export class NotificationsService implements INotificationsService {
return await this.discordProvider.sendMessage(notification, notificationMessage);
}
if (notification.type === "email" && this.emailProvider.sendMessage && notificationMessage) {
this.logger.info({
message: "[NEW] Using sendMessage for email",
service: SERVICE_NAME,
method: "send",
});
return await this.emailProvider.sendMessage(notification, notificationMessage);
}
// Fallback to existing sendAlert for all providers
switch (notification.type) {
case "email":