diff --git a/server/src/service/infrastructure/notificationProviders/INotificationProvider.ts b/server/src/service/infrastructure/notificationProviders/INotificationProvider.ts index 2743dfab9..b5c886408 100644 --- a/server/src/service/infrastructure/notificationProviders/INotificationProvider.ts +++ b/server/src/service/infrastructure/notificationProviders/INotificationProvider.ts @@ -1,7 +1,30 @@ import type { Notification } from "@/types/index.js"; import type { NotificationMessage } from "@/types/notificationMessage.js"; +import { ILogger } from "@/utils/logger.js"; +import type { OptionsOfJSONResponseBody } from "got"; +export const DEFAULT_NOTIFICATION_TIMEOUT_MS = 30_000; export interface INotificationProvider { sendMessage: (notification: Notification, message: NotificationMessage) => Promise; sendTestAlert(notification: Partial): Promise; } + +export abstract class NotificationProvider implements INotificationProvider { + protected readonly logger: ILogger; + protected readonly timeoutMs: number; + + constructor(logger: ILogger, timeoutMs: number = DEFAULT_NOTIFICATION_TIMEOUT_MS) { + this.logger = logger; + this.timeoutMs = timeoutMs; + } + + protected gotRequestOptions(): Pick { + return { + timeout: { request: this.timeoutMs }, + retry: { limit: 0 }, + }; + } + + abstract sendMessage(notification: Notification, message: NotificationMessage): Promise; + abstract sendTestAlert(notification: Partial): Promise; +} diff --git a/server/src/service/infrastructure/notificationProviders/discord.ts b/server/src/service/infrastructure/notificationProviders/discord.ts index 3bd2556ae..942e0b578 100644 --- a/server/src/service/infrastructure/notificationProviders/discord.ts +++ b/server/src/service/infrastructure/notificationProviders/discord.ts @@ -1,18 +1,11 @@ const SERVICE_NAME = "DiscordProvider"; import type { AlertDiscordPayload, DiscordEmbedField, Notification } from "@/types/index.js"; -import { INotificationProvider } from "@/service/index.js"; +import { NotificationProvider } from "@/service/infrastructure/notificationProviders/INotificationProvider.js"; import { getTestMessage } from "@/service/infrastructure/notificationProviders/utils.js"; import type { NotificationMessage, NotificationSeverity } from "@/types/notificationMessage.js"; import got from "got"; -import { ILogger } from "@/utils/logger.js"; - -export class DiscordProvider implements INotificationProvider { - private logger: ILogger; - - constructor(logger: ILogger) { - this.logger = logger; - } +export class DiscordProvider extends NotificationProvider { sendTestAlert = async (notification: Partial) => { if (!notification.address) { return false; @@ -23,6 +16,7 @@ export class DiscordProvider implements INotificationProvider { headers: { "Content-Type": "application/json", }, + ...this.gotRequestOptions(), }); return true; } catch (error) { @@ -55,6 +49,7 @@ export class DiscordProvider implements INotificationProvider { headers: { "Content-Type": "application/json", }, + ...this.gotRequestOptions(), }); return true; } catch (error) { diff --git a/server/src/service/infrastructure/notificationProviders/email.ts b/server/src/service/infrastructure/notificationProviders/email.ts index b3686651c..0890eac7f 100644 --- a/server/src/service/infrastructure/notificationProviders/email.ts +++ b/server/src/service/infrastructure/notificationProviders/email.ts @@ -1,17 +1,16 @@ const SERVICE_NAME = "EmailProvider"; import type { Notification } from "@/types/index.js"; -import { INotificationProvider } from "@/service/index.js"; +import { NotificationProvider } from "@/service/infrastructure/notificationProviders/INotificationProvider.js"; import { buildTestEmail } from "@/service/infrastructure/notificationProviders/utils.js"; import type { NotificationMessage } from "@/types/notificationMessage.js"; import type { ILogger } from "@/utils/logger.js"; import { IEmailService } from "@/service/infrastructure/emailService.js"; -export class EmailProvider implements INotificationProvider { +export class EmailProvider extends NotificationProvider { private emailService: IEmailService; - private logger: ILogger; constructor(emailService: IEmailService, logger: ILogger) { + super(logger); this.emailService = emailService; - this.logger = logger; } async sendTestAlert(notification: Partial): Promise { diff --git a/server/src/service/infrastructure/notificationProviders/matrix.ts b/server/src/service/infrastructure/notificationProviders/matrix.ts index 9d92e1f3c..f8a093ab7 100644 --- a/server/src/service/infrastructure/notificationProviders/matrix.ts +++ b/server/src/service/infrastructure/notificationProviders/matrix.ts @@ -1,19 +1,12 @@ const SERVICE_NAME = "MatrixProvider"; import got from "got"; -import type { INotificationProvider } from "@/service/index.js"; +import { NotificationProvider } from "@/service/infrastructure/notificationProviders/INotificationProvider.js"; import type { AlertMatrixPayload, Notification } from "@/types/index.js"; import type { NotificationMessage } from "@/types/notificationMessage.js"; import { getTestMessage } from "@/service/infrastructure/notificationProviders/utils.js"; -import { ILogger } from "@/utils/logger.js"; import { randomUUID } from "crypto"; -export class MatrixProvider implements INotificationProvider { - private logger: ILogger; - - constructor(logger: ILogger) { - this.logger = logger; - } - +export class MatrixProvider extends NotificationProvider { sendTestAlert = async (notification: Partial) => { const { homeserverUrl, accessToken, roomId } = notification; if (!homeserverUrl || !accessToken || !roomId) { @@ -55,6 +48,7 @@ export class MatrixProvider implements INotificationProvider { "Content-Type": "application/json", Authorization: `Bearer ${accessToken}`, }, + ...this.gotRequestOptions(), }); return true; } catch (error) { diff --git a/server/src/service/infrastructure/notificationProviders/pagerduty.ts b/server/src/service/infrastructure/notificationProviders/pagerduty.ts index 304c38cf4..0d2fa75be 100644 --- a/server/src/service/infrastructure/notificationProviders/pagerduty.ts +++ b/server/src/service/infrastructure/notificationProviders/pagerduty.ts @@ -1,19 +1,12 @@ const SERVICE_NAME = "PagerDutyProvider"; import got from "got"; import type { Notification } from "@/types/index.js"; -import { INotificationProvider } from "@/service/index.js"; +import { NotificationProvider } from "@/service/infrastructure/notificationProviders/INotificationProvider.js"; import type { NotificationMessage } from "@/types/notificationMessage.js"; import { getTestMessage } from "@/service/infrastructure/notificationProviders/utils.js"; -import { ILogger } from "@/utils/logger.js"; import { AlertPagerDutyPayload } from "@/types/index.js"; -export class PagerDutyProvider implements INotificationProvider { - private logger: ILogger; - - constructor(logger: ILogger) { - this.logger = logger; - } - +export class PagerDutyProvider extends NotificationProvider { async sendTestAlert(notification: Partial): Promise { try { await got.post("https://events.pagerduty.com/v2/enqueue", { @@ -28,6 +21,7 @@ export class PagerDutyProvider implements INotificationProvider { }, }, responseType: "json", + ...this.gotRequestOptions(), }); return true; } catch (error) { @@ -59,6 +53,7 @@ export class PagerDutyProvider implements INotificationProvider { const payload = this.buildPagerDutyPayload(notification, message); await got.post("https://events.pagerduty.com/v2/enqueue", { json: payload, + ...this.gotRequestOptions(), }); this.logger.info({ message: "[NEW] PagerDuty notification sent via sendMessage", diff --git a/server/src/service/infrastructure/notificationProviders/slack.ts b/server/src/service/infrastructure/notificationProviders/slack.ts index cfb135901..1ab046f7f 100644 --- a/server/src/service/infrastructure/notificationProviders/slack.ts +++ b/server/src/service/infrastructure/notificationProviders/slack.ts @@ -1,18 +1,11 @@ const SERVICE_NAME = "SlackProvider"; import type { Notification } from "@/types/index.js"; -import { INotificationProvider } from "@/service/index.js"; +import { NotificationProvider } from "@/service/infrastructure/notificationProviders/INotificationProvider.js"; import type { NotificationMessage } from "@/types/notificationMessage.js"; import { getTestMessage } from "@/service/infrastructure/notificationProviders/utils.js"; import got, { HTTPError } from "got"; -import { ILogger } from "@/utils/logger.js"; - -export class SlackProvider implements INotificationProvider { - private logger: ILogger; - - constructor(logger: ILogger) { - this.logger = logger; - } +export class SlackProvider extends NotificationProvider { async sendTestAlert(notification: Partial): Promise { if (!notification.address) { return false; @@ -24,6 +17,7 @@ export class SlackProvider implements INotificationProvider { headers: { "Content-Type": "application/json", }, + ...this.gotRequestOptions(), }); return true; } catch (error) { @@ -54,6 +48,7 @@ export class SlackProvider implements INotificationProvider { headers: { "Content-Type": "application/json", }, + ...this.gotRequestOptions(), }); this.logger.info({ message: "[NEW] Slack notification sent via sendMessage", diff --git a/server/src/service/infrastructure/notificationProviders/teams.ts b/server/src/service/infrastructure/notificationProviders/teams.ts index dcf48c4cd..f98724295 100644 --- a/server/src/service/infrastructure/notificationProviders/teams.ts +++ b/server/src/service/infrastructure/notificationProviders/teams.ts @@ -1,9 +1,8 @@ const SERVICE_NAME = "TeamsProvider"; import type { Notification } from "@/types/index.js"; -import { INotificationProvider } from "@/service/index.js"; +import { NotificationProvider } from "@/service/infrastructure/notificationProviders/INotificationProvider.js"; import type { NotificationMessage } from "@/types/notificationMessage.js"; import { getTestMessage } from "@/service/infrastructure/notificationProviders/utils.js"; -import type { ILogger } from "@/utils/logger.js"; import got, { HTTPError } from "got"; // Types for Adaptive Card elements @@ -58,13 +57,7 @@ type TeamsMessage = { }>; }; -export class TeamsProvider implements INotificationProvider { - private logger: ILogger; - - constructor(logger: ILogger) { - this.logger = logger; - } - +export class TeamsProvider extends NotificationProvider { async sendTestAlert(notification: Partial): Promise { if (!notification.address) { return false; @@ -90,6 +83,7 @@ export class TeamsProvider implements INotificationProvider { headers: { "Content-Type": "application/json", }, + ...this.gotRequestOptions(), }); return true; } catch (error) { @@ -117,6 +111,7 @@ export class TeamsProvider implements INotificationProvider { headers: { "Content-Type": "application/json", }, + ...this.gotRequestOptions(), }); this.logger.info({ message: "Teams notification sent via sendMessage", diff --git a/server/src/service/infrastructure/notificationProviders/telegram.ts b/server/src/service/infrastructure/notificationProviders/telegram.ts index fcb7bdacc..9d6968fdd 100644 --- a/server/src/service/infrastructure/notificationProviders/telegram.ts +++ b/server/src/service/infrastructure/notificationProviders/telegram.ts @@ -1,18 +1,11 @@ const SERVICE_NAME = "TelegramProvider"; import type { Notification } from "@/types/index.js"; -import { INotificationProvider } from "@/service/index.js"; +import { NotificationProvider } from "@/service/infrastructure/notificationProviders/INotificationProvider.js"; import type { NotificationMessage } from "@/types/notificationMessage.js"; import { getTestMessage } from "@/service/infrastructure/notificationProviders/utils.js"; import got from "got"; -import { ILogger } from "@/utils/logger.js"; - -export class TelegramProvider implements INotificationProvider { - private logger: ILogger; - - constructor(logger: ILogger) { - this.logger = logger; - } +export class TelegramProvider extends NotificationProvider { async sendTestAlert(notification: Partial): Promise { if (!notification.address || !notification.accessToken) { return false; @@ -25,6 +18,7 @@ export class TelegramProvider implements INotificationProvider { text: getTestMessage(), parse_mode: "HTML", }, + ...this.gotRequestOptions(), }); return true; } catch (error) { @@ -55,7 +49,9 @@ export class TelegramProvider implements INotificationProvider { text, parse_mode: "HTML", }, + ...this.gotRequestOptions(), }); + this.logger.info({ message: "Telegram notification sent", service: SERVICE_NAME, diff --git a/server/src/service/infrastructure/notificationProviders/webhook.ts b/server/src/service/infrastructure/notificationProviders/webhook.ts index fcb5728d4..292650f91 100644 --- a/server/src/service/infrastructure/notificationProviders/webhook.ts +++ b/server/src/service/infrastructure/notificationProviders/webhook.ts @@ -1,18 +1,11 @@ const SERVICE_NAME = "WebhookProvider"; import type { Notification } from "@/types/index.js"; -import { INotificationProvider } from "@/service/index.js"; +import { NotificationProvider } from "@/service/infrastructure/notificationProviders/INotificationProvider.js"; import type { NotificationMessage } from "@/types/notificationMessage.js"; import { getTestMessage } from "@/service/infrastructure/notificationProviders/utils.js"; import got from "got"; -import { ILogger } from "@/utils/logger.js"; - -export class WebhookProvider implements INotificationProvider { - private logger: ILogger; - - constructor(logger: ILogger) { - this.logger = logger; - } +export class WebhookProvider extends NotificationProvider { sendMessage = async (notification: Notification, message: NotificationMessage): Promise => { if (!notification.address) { return false; @@ -27,6 +20,7 @@ export class WebhookProvider implements INotificationProvider { headers: { "Content-Type": "application/json", }, + ...this.gotRequestOptions(), }); this.logger.info({ message: "Webhook notification sent", @@ -108,6 +102,7 @@ export class WebhookProvider implements INotificationProvider { headers: { "Content-Type": "application/json", }, + ...this.gotRequestOptions(), }); return true; } catch (error) {