refactor(alert): extract shared HTTPProvider for webhook-based alerts
Discord, Slack, and Webhook providers now use a single HTTPProvider struct with a PayloadFunc for the only part that differs. Centralizes response body handling and adds HTTP status code checking (4xx/5xx now return errors instead of being silently ignored). Email and Ntfy keep separate implementations (different protocols). Adding a new HTTP-based alert provider is now a one-line PayloadFunc.
This commit is contained in:
+41
-60
@@ -17,15 +17,49 @@ type Provider interface {
|
|||||||
Send(title, message string) error
|
Send(title, message string) error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type PayloadFunc func(title, message string) ([]byte, error)
|
||||||
|
|
||||||
|
type HTTPProvider struct {
|
||||||
|
URL string
|
||||||
|
Payload PayloadFunc
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *HTTPProvider) Send(title, message string) error {
|
||||||
|
body, err := h.Payload(title, message)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
resp, err := alertClient.Post(h.URL, "application/json", bytes.NewBuffer(body))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
if resp.StatusCode >= 400 {
|
||||||
|
return fmt.Errorf("alert webhook returned HTTP %d", resp.StatusCode)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func discordPayload(title, message string) ([]byte, error) {
|
||||||
|
return json.Marshal(map[string]string{"content": fmt.Sprintf("**%s**\n%s", title, message)})
|
||||||
|
}
|
||||||
|
|
||||||
|
func slackPayload(title, message string) ([]byte, error) {
|
||||||
|
return json.Marshal(map[string]string{"text": fmt.Sprintf("*%s*\n%s", title, message)})
|
||||||
|
}
|
||||||
|
|
||||||
|
func webhookPayload(title, message string) ([]byte, error) {
|
||||||
|
return json.Marshal(map[string]string{"title": title, "message": message, "status": "alert"})
|
||||||
|
}
|
||||||
|
|
||||||
func GetProvider(cfg models.AlertConfig) Provider {
|
func GetProvider(cfg models.AlertConfig) Provider {
|
||||||
switch cfg.Type {
|
switch cfg.Type {
|
||||||
case "discord":
|
case "discord":
|
||||||
return &DiscordProvider{URL: cfg.Settings["url"]}
|
return &HTTPProvider{URL: cfg.Settings["url"], Payload: discordPayload}
|
||||||
case "slack":
|
case "slack":
|
||||||
return &SlackProvider{URL: cfg.Settings["url"]}
|
return &HTTPProvider{URL: cfg.Settings["url"], Payload: slackPayload}
|
||||||
case "webhook":
|
case "webhook":
|
||||||
// Generic Webhook
|
return &HTTPProvider{URL: cfg.Settings["url"], Payload: webhookPayload}
|
||||||
return &WebhookProvider{URL: cfg.Settings["url"]}
|
|
||||||
case "email":
|
case "email":
|
||||||
port := "25"
|
port := "25"
|
||||||
if p, ok := cfg.Settings["port"]; ok {
|
if p, ok := cfg.Settings["port"]; ok {
|
||||||
@@ -56,62 +90,6 @@ func GetProvider(cfg models.AlertConfig) Provider {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- DISCORD ---
|
|
||||||
type DiscordProvider struct{ URL string }
|
|
||||||
|
|
||||||
func (d *DiscordProvider) Send(title, message string) error {
|
|
||||||
payload := map[string]string{"content": fmt.Sprintf("**%s**\n%s", title, message)}
|
|
||||||
jsonValue, err := json.Marshal(payload)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
resp, err := alertClient.Post(d.URL, "application/json", bytes.NewBuffer(jsonValue))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer resp.Body.Close()
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- SLACK ---
|
|
||||||
type SlackProvider struct{ URL string }
|
|
||||||
|
|
||||||
func (s *SlackProvider) Send(title, message string) error {
|
|
||||||
payload := map[string]string{"text": fmt.Sprintf("*%s*\n%s", title, message)}
|
|
||||||
jsonValue, err := json.Marshal(payload)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
resp, err := alertClient.Post(s.URL, "application/json", bytes.NewBuffer(jsonValue))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer resp.Body.Close()
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- GENERIC WEBHOOK ---
|
|
||||||
type WebhookProvider struct{ URL string }
|
|
||||||
|
|
||||||
func (w *WebhookProvider) Send(title, message string) error {
|
|
||||||
payload := map[string]string{
|
|
||||||
"title": title,
|
|
||||||
"message": message,
|
|
||||||
"status": "alert",
|
|
||||||
}
|
|
||||||
jsonValue, err := json.Marshal(payload)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
resp, err := alertClient.Post(w.URL, "application/json", bytes.NewBuffer(jsonValue))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer resp.Body.Close()
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- EMAIL ---
|
|
||||||
type EmailProvider struct {
|
type EmailProvider struct {
|
||||||
Host, Port, User, Pass, To, From string
|
Host, Port, User, Pass, To, From string
|
||||||
}
|
}
|
||||||
@@ -149,5 +127,8 @@ func (n *NtfyProvider) Send(title, message string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
|
if resp.StatusCode >= 400 {
|
||||||
|
return fmt.Errorf("ntfy returned HTTP %d", resp.StatusCode)
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user