diff --git a/application/public/upload/notification/pushover.png b/application/public/upload/notification/pushover.png new file mode 100644 index 0000000..8e7b04d Binary files /dev/null and b/application/public/upload/notification/pushover.png differ diff --git a/application/src/components/settings/notification-settings/NotificationChannelDialog.tsx b/application/src/components/settings/notification-settings/NotificationChannelDialog.tsx index 4d47b44..7b20f9f 100644 --- a/application/src/components/settings/notification-settings/NotificationChannelDialog.tsx +++ b/application/src/components/settings/notification-settings/NotificationChannelDialog.tsx @@ -36,7 +36,7 @@ interface NotificationChannelDialogProps { const baseSchema = z.object({ notify_name: z.string().min(1, "Name is required"), - notification_type: z.enum(["telegram", "discord", "slack", "signal", "google_chat", "email", "ntfy", "webhook"]), + notification_type: z.enum(["telegram", "discord", "slack", "signal", "google_chat", "email", "ntfy", "pushover", "webhook"]), enabled: z.boolean().default(true), service_id: z.string().default("global"), template_id: z.string().optional(), @@ -89,6 +89,12 @@ const webhookSchema = baseSchema.extend({ webhook_payload_template: z.string().optional(), }); +const pushoverSchema = baseSchema.extend({ + notification_type: z.literal("pushover"), + api_token: z.string().min(1, "API token is required"), + user_key: z.string().min(1, "User key is required"), +}); + const formSchema = z.discriminatedUnion("notification_type", [ telegramSchema, discordSchema, @@ -97,7 +103,8 @@ const formSchema = z.discriminatedUnion("notification_type", [ googleChatSchema, emailSchema, ntfySchema, - webhookSchema + pushoverSchema, + webhookSchema, ]); type FormValues = z.infer; @@ -145,6 +152,12 @@ const notificationTypeOptions = [ description: "Send notifications via NTFY push service", icon: "/upload/notification/ntfy.png" }, + { + value: "pushover", + label: "Pushover", + description: "Send push notifications via Pushover", + icon: "/upload/notification/pushover.png" + }, { value: "webhook", label: "Webhook", @@ -570,6 +583,43 @@ export const NotificationChannelDialog = ({ )} /> )} + + {notificationType === "pushover" && ( + <> + ( + + API Token + + + + + Your Pushover application API token + + + + )} + /> + ( + + User Key + + + + + Your Pushover user key (or group key) + + + + )} + /> + + )} {notificationType === "webhook" && ( <> diff --git a/application/src/services/alertConfigService.ts b/application/src/services/alertConfigService.ts index 9df6e6a..83a702c 100644 --- a/application/src/services/alertConfigService.ts +++ b/application/src/services/alertConfigService.ts @@ -7,7 +7,7 @@ export interface AlertConfiguration { collectionId?: string; collectionName?: string; service_id: string; - notification_type: "telegram" | "discord" | "slack" | "signal" | "google_chat" | "email" | "ntfy" | "webhook"; + notification_type: "telegram" | "discord" | "slack" | "signal" | "google_chat" | "email" | "ntfy" | "pushover" | "webhook"; telegram_chat_id?: string; discord_webhook_url?: string; signal_number?: string; @@ -29,6 +29,8 @@ export interface AlertConfiguration { webhook_id?: string; channel_id?: string; ntfy_endpoint?: string; + api_token?: string; + user_key?: string; webhook_url?: string; webhook_payload_template?: string; } @@ -85,6 +87,10 @@ export const alertConfigService = { } else if (config.notification_type === "ntfy") { cleanConfig.ntfy_endpoint = config.ntfy_endpoint || ""; + + } else if (config.notification_type === "pushover") { + cleanConfig.api_token = config.api_token || ""; + cleanConfig.user_key = config.user_key || ""; } else if (config.notification_type === "webhook") { cleanConfig.webhook_url = config.webhook_url || "";