import React, { useEffect } from "react"; import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter } from "@/components/ui/dialog"; import { Button } from "@/components/ui/button"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; import { AlertConfiguration, alertConfigService } from "@/services/alertConfigService"; import { useForm } from "react-hook-form"; import { z } from "zod"; import { zodResolver } from "@hookform/resolvers/zod"; import { Input } from "@/components/ui/input"; import { Textarea } from "@/components/ui/textarea"; import { Form, FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage, } from "@/components/ui/form"; import { Switch } from "@/components/ui/switch"; import { Loader2, Copy } from "lucide-react"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { toast } from "@/hooks/use-toast"; interface NotificationChannelDialogProps { open: boolean; onClose: (refreshList: boolean) => void; editingConfig: AlertConfiguration | null; } 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"]), enabled: z.boolean().default(true), service_id: z.string().default("global"), template_id: z.string().optional(), }); const telegramSchema = baseSchema.extend({ notification_type: z.literal("telegram"), telegram_chat_id: z.string().min(1, "Chat ID is required"), bot_token: z.string().min(1, "Bot token is required"), }); const discordSchema = baseSchema.extend({ notification_type: z.literal("discord"), discord_webhook_url: z.string().url("Must be a valid URL"), }); const slackSchema = baseSchema.extend({ notification_type: z.literal("slack"), slack_webhook_url: z.string().url("Must be a valid URL"), }); const signalSchema = baseSchema.extend({ notification_type: z.literal("signal"), signal_number: z.string().min(1, "Signal number is required"), signal_api_endpoint: z.string().url("Must be a valid API endpoint URL"), }); const googleChatSchema = baseSchema.extend({ notification_type: z.literal("google_chat"), google_chat_webhook_url: z.string().url("Must be a valid URL"), }); const emailSchema = baseSchema.extend({ notification_type: z.literal("email"), email_address: z.string().email("Valid email is required"), email_sender_name: z.string().min(1, "Sender name is required"), smtp_server: z.string().min(1, "SMTP server is required"), smtp_port: z.string().min(1, "SMTP port is required"), smtp_password: z.string().min(1, "SMTP password is required"), }); const ntfySchema = baseSchema.extend({ notification_type: z.literal("ntfy"), ntfy_endpoint: z.string().url("Must be a valid NTFY endpoint URL"), }); const webhookSchema = baseSchema.extend({ notification_type: z.literal("webhook"), webhook_url: z.string().url("Must be a valid URL"), webhook_payload_template: z.string().optional(), }); const formSchema = z.discriminatedUnion("notification_type", [ telegramSchema, discordSchema, slackSchema, signalSchema, googleChatSchema, emailSchema, ntfySchema, webhookSchema ]); type FormValues = z.infer; const notificationTypeOptions = [ { value: "telegram", label: "Telegram", description: "Send notifications via Telegram bot", icon: "/upload/notification/telegram.png" }, { value: "discord", label: "Discord", description: "Send notifications to Discord webhook", icon: "/upload/notification/discord.png" }, { value: "slack", label: "Slack", description: "Send notifications to Slack webhook", icon: "/upload/notification/slack.png" }, { value: "signal", label: "Signal", description: "Send notifications via Signal", icon: "/upload/notification/signal.png" }, { value: "google_chat", label: "Google Chat", description: "Send notifications to Google Chat webhook", icon: "/upload/notification/google.png" }, { value: "email", label: "Email", description: "Send notifications via email", icon: "/upload/notification/email.png" }, { value: "ntfy", label: "NTFY", description: "Send notifications via NTFY push service", icon: "/upload/notification/ntfy.png" }, { value: "webhook", label: "Webhook", description: "Send notifications to custom webhook", icon: "/upload/notification/webhook.png" }, ]; const webhookPayloadTemplates = { server: `{ "alert_type": "server", "server_name": "\${server_name}", "status": "\${status}", "cpu_usage": "\${cpu_usage}", "ram_usage": "\${ram_usage}", "disk_usage": "\${disk_usage}", "network_usage": "\${network_usage}", "cpu_temp": "\${cpu_temp}", "disk_io": "\${disk_io}", "threshold": "\${time}", "message": "Server \${server_name} alert: \${status}" }`, service: `{ "alert_type": "service", "service_name": "\${service_name}", "status": "\${status}", "response_time": "\${response_time}", "url": "\${url}", "uptime": "\${uptime}", "downtime": "\${downtime}", "timestamp": "\${time}", "message": "Service \${service_name} is \${status}" }`, ssl: `{ "alert_type": "ssl", "domain": "\${domain}", "certificate_name": "\${certificate_name}", "expiry_date": "\${expiry_date}", "days_left": "\${days_left}", "issuer": "\${issuer}", "serial_number": "\${serial_number}", "timestamp": "\${time}", "message": "SSL certificate for \${domain} expires in \${days_left} days" }` }; const defaultPayloadTemplate = `{ "alert_type": "general", "service_name": "\${service_name}", "status": "\${status}", "message": "\${message}", "timestamp": "\${time}" }`; export const NotificationChannelDialog = ({ open, onClose, editingConfig }: NotificationChannelDialogProps) => { const isEditing = !!editingConfig; const form = useForm({ resolver: zodResolver(formSchema), defaultValues: { notify_name: "", notification_type: "telegram" as const, enabled: true, service_id: "global", template_id: "", }, }); const { watch, reset, setValue } = form; const notificationType = watch("notification_type"); const [isSubmitting, setIsSubmitting] = React.useState(false); useEffect(() => { if (editingConfig) { // Handle string vs boolean for enabled field const enabled = typeof editingConfig.enabled === 'string' ? editingConfig.enabled === "true" : !!editingConfig.enabled; reset({ ...editingConfig, enabled }); } else if (open) { reset({ notify_name: "", notification_type: "telegram" as const, enabled: true, service_id: "global", template_id: "", }); } }, [editingConfig, open, reset]); const handleClose = () => { onClose(false); }; const insertTemplate = (template: string) => { setValue("webhook_payload_template", template); }; const onSubmit = async (values: FormValues) => { setIsSubmitting(true); try { // Handle all notification types including webhook through alert_configurations const configData = { ...values, service_id: values.service_id || "global", }; if (isEditing && editingConfig?.id) { await alertConfigService.updateAlertConfiguration(editingConfig.id, configData); } else { await alertConfigService.createAlertConfiguration(configData as any); } onClose(true); // Close with refresh } catch (error) { toast({ title: "Error", description: "Failed to save notification channel", variant: "destructive" }); } finally { setIsSubmitting(false); } }; return ( handleClose()}> {isEditing ? "Edit Notification Channel" : "Add Notification Channel"}
( Channel Name A name to identify this notification channel )} /> ( Channel Type )} /> {notificationType === "telegram" && ( <> ( Chat ID The Telegram chat ID to send notifications to )} /> ( Bot Token Your Telegram bot token from @BotFather )} /> )} {notificationType === "discord" && ( ( Webhook URL Discord webhook URL from your server settings )} /> )} {notificationType === "slack" && ( ( Webhook URL Slack incoming webhook URL )} /> )} {notificationType === "signal" && ( <> ( Signal Number Signal phone number to send notifications to )} /> ( Signal API Endpoint The Rest API endpoint for your Signal service )} /> )} {notificationType === "google_chat" && ( ( Google Chat Webhook URL Google Chat webhook URL from your Google Chat space )} /> )} {notificationType === "email" && ( <> ( Email Address Email address to send notifications to )} /> ( Sender Name Display name for outgoing emails )} />
( SMTP Server )} /> ( SMTP Port )} />
( SMTP Password Password for authenticating with the SMTP server )} /> )} {notificationType === "ntfy" && ( ( NTFY Endpoint The NTFY endpoint URL including your topic (e.g., https://ntfy.sh/checkcle) )} /> )} {notificationType === "webhook" && ( <> ( Webhook URL The URL where webhook notifications will be sent )} />
( Payload Template (Optional) JSON template for the webhook payload. Leave empty to use default template. )} /> Payload Templates

Available Placeholders:

Server:

${'{server_name}'}
${'{cpu_usage}'}
${'{ram_usage}'}
${'{disk_usage}'}

Service:

${'{service_name}'}
${'{response_time}'}
${'{url}'}
${'{uptime}'}

SSL:

${'{domain}'}
${'{expiry_date}'}
${'{days_left}'}
${'{issuer}'}

Common:

${'{status}'}
${'{time}'}
${'{message}'}
${'{threshold}'}
)} (
Enabled Enable or disable this notification channel
)} />
); };