diff --git a/server/src/config/services.ts b/server/src/config/services.ts index e25ee1706..3a740549a 100644 --- a/server/src/config/services.ts +++ b/server/src/config/services.ts @@ -7,7 +7,7 @@ import EmailService from "../service/infrastructure/emailService.js"; import BufferService from "../service/infrastructure/bufferService.js"; import NotificationUtils from "../service/infrastructure/notificationUtils.js"; import NotificationService from "../service/infrastructure/notificationService.js"; -import { NotificationsService, StatusService } from "@/service/index.js"; +import { NotificationsService, StatusService, WebhookProvider, SlackProvider, EmailProvider, DiscordProvider } 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"; @@ -228,7 +228,17 @@ export const initializeServices = async ({ notificationUtils, }); - const notificationsService = new NotificationsService(notificationsRepository); + const webhookProvider = new WebhookProvider(); + const slackProvider = new SlackProvider(); + const emailProvider = new EmailProvider(emailService); + const discordProvider = new DiscordProvider(); + + const notificationsService = new NotificationsService(notificationsRepository, { + webhookProvider, + slackProvider, + emailProvider, + discordProvider, + }); const superSimpleQueueHelper = new SuperSimpleQueueHelper({ db, @@ -323,6 +333,7 @@ export const initializeServices = async ({ invitesRepository, recoveryTokensRepository, settingsRepository, + notificationsRepository, notificationsService, }; diff --git a/server/src/service/infrastructure/notificationProviders/email.ts b/server/src/service/infrastructure/notificationProviders/email.ts index dda7338a0..e60775982 100644 --- a/server/src/service/infrastructure/notificationProviders/email.ts +++ b/server/src/service/infrastructure/notificationProviders/email.ts @@ -1,9 +1,35 @@ import type { Monitor, Notification, MonitorStatusResponse } from "@/types/index.js"; import { INotificationProvider } from "@/service/index.js"; +import { buildHardwareAlerts, buildHardwareEmail, buildEmail } from "@/service/infrastructure/notificationProviders/utils.js"; export class EmailProvider implements INotificationProvider { + private emailService: any; + constructor(emailService: any) { + this.emailService = emailService; + } + + private buildHardwareEmail = (monitor: Monitor, monitorStatusResponse: MonitorStatusResponse) => { + const { alertsToSend } = buildHardwareAlerts("HOST_PLACEHOLDER", monitor, monitorStatusResponse); + const html = buildHardwareEmail(this.emailService, monitor, alertsToSend); + return html; + }; + async sendAlert(notification: Notification, monitor: Monitor, monitorStatusResponse: MonitorStatusResponse): Promise { - return false; + const subject = `Monitor ${monitor.name} infrastructure alerts`; + let html; + if (monitor.type === "hardware") { + html = this.buildHardwareEmail(monitor, monitorStatusResponse); + } else { + html = await buildEmail(this.emailService, monitor); + } + + if (!notification.address) { + return false; + } + + const messageId = await this.emailService.sendEmail(notification.address, subject, html); + if (!messageId) return false; + return true; } async sendTestAlert(notification: Notification): Promise { diff --git a/server/src/service/infrastructure/notificationProviders/utils.ts b/server/src/service/infrastructure/notificationProviders/utils.ts index 9a10e668b..0d25543ea 100644 --- a/server/src/service/infrastructure/notificationProviders/utils.ts +++ b/server/src/service/infrastructure/notificationProviders/utils.ts @@ -134,6 +134,34 @@ export const shouldSendHardwareAlert = (monitor: Monitor, networkResponse: Monit 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`; + const now = new Date(); + const localTimeZone = Intl.DateTimeFormat().resolvedOptions().timeZone; + const localTime = new Intl.DateTimeFormat(undefined, { + year: "numeric", + month: "2-digit", + day: "2-digit", + hour: "2-digit", + minute: "2-digit", + second: "2-digit", + hour12: false, + timeZone: localTimeZone, + timeZoneName: "short", + }).format(now); + const utcTime = now.toUTCString(); + return `Monitor: ${monitor.name}\nLocal Time (${localTimeZone}): ${localTime}\nUTC Time: ${utcTime}\nStatus: ${status ? "UP" : "DOWN"}\nStatus Code: ${code}\n\u200B\n`; +}; + +export const buildHardwareEmail = async (emailService: any, monitor: Monitor, alerts: string[]) => { + const template = "hardwareIncidentTemplate"; + const context = { monitor: monitor.name, url: monitor.url, alerts }; + const html = await emailService.buildEmail(template, context); + return html; +}; + +export const buildEmail = async (emailService: any, monitor: Monitor): Promise => { + const template = monitor.status === false ? "serverIsUpTemplate" : "serverIsDownTemplate"; + const context = { monitor: monitor.name, url: monitor.url }; + const html = await emailService.buildEmail(template, context); + console.log({ html: typeof html }); + return html; }; diff --git a/server/src/service/infrastructure/notificationsService.ts b/server/src/service/infrastructure/notificationsService.ts index e170dd3c4..8b44d4f9c 100644 --- a/server/src/service/infrastructure/notificationsService.ts +++ b/server/src/service/infrastructure/notificationsService.ts @@ -1,7 +1,7 @@ import type { HardwareStatusPayload, Monitor, MonitorStatusResponse, Notification } from "@/types/index.js"; import { shouldSendHardwareAlert } from "@/service/infrastructure/notificationProviders/utils.js"; import { INotificationsRepository } from "@/repositories/index.js"; -import { WebhookProvider } from "@/service/index.js"; +import { DiscordProvider, EmailProvider, SlackProvider, WebhookProvider } from "@/service/index.js"; export interface INotificationsService { handleNotifications: ( monitor: Monitor, @@ -14,21 +14,39 @@ export interface INotificationsService { export class NotificationsService implements INotificationsService { private notificationsRepository: INotificationsRepository; private webhookProvider: WebhookProvider; + private emailProvider: EmailProvider; + private slackProvider: SlackProvider; + private discordProvider: DiscordProvider; - constructor(notificationsRepository: INotificationsRepository, webhookProvider: WebhookProvider) { + constructor( + notificationsRepository: INotificationsRepository, + providers: { + webhookProvider: WebhookProvider; + emailProvider: EmailProvider; + slackProvider: SlackProvider; + discordProvider: DiscordProvider; + } + ) { this.notificationsRepository = notificationsRepository; - this.webhookProvider = webhookProvider; + this.webhookProvider = providers.webhookProvider; + this.emailProvider = providers.emailProvider; + this.slackProvider = providers.slackProvider; + this.discordProvider = providers.discordProvider; } private send = async (notification: Notification, monitor: Monitor, monitorStatusResponse: MonitorStatusResponse): Promise => { switch (notification.type) { - case " email": { - } - case "webhook": { + case "email": + return await this.emailProvider.sendAlert(notification, monitor, monitorStatusResponse); + case "slack": + return await this.slackProvider.sendAlert(notification, monitor, monitorStatusResponse); + case "discord": + return await this.discordProvider.sendAlert(notification, monitor, monitorStatusResponse); + case "webhook": return await this.webhookProvider.sendAlert(notification, monitor, monitorStatusResponse); - } + default: + return false; } - return false; }; private sendNotifications = async (monitor: Monitor, monitorStatusResponse: MonitorStatusResponse) => {