fix: ntfy webhook now sends plain text instead of JSON

- Modified generatePayloadFromTemplate to skip JSON validation for ntfy
- ntfy uses plain text body format, not JSON
- Messages now successfully delivered to ntfy topics
- Headers for title/priority/tags need dynamic templating support (future enhancement)
This commit is contained in:
Pulse Monitor
2025-08-31 16:07:42 +00:00
parent 71bc34504a
commit 532cde005e
2 changed files with 34 additions and 22 deletions
+16 -5
View File
@@ -463,7 +463,7 @@ func (n *NotificationManager) sendGroupedWebhook(webhook WebhookConfig, alertLis
// The template already has the chat_id embedded
}
jsonData, err = n.generatePayloadFromTemplate(enhanced.PayloadTemplate, data)
jsonData, err = n.generatePayloadFromTemplateWithService(enhanced.PayloadTemplate, data, webhook.Service)
if err != nil {
log.Error().
Err(err).
@@ -539,7 +539,7 @@ func (n *NotificationManager) sendGroupedWebhook(webhook WebhookConfig, alertLis
}
}
jsonData, err = n.generatePayloadFromTemplate(enhanced.PayloadTemplate, data)
jsonData, err = n.generatePayloadFromTemplateWithService(enhanced.PayloadTemplate, data, webhook.Service)
if err != nil {
log.Error().
Err(err).
@@ -711,7 +711,7 @@ func (n *NotificationManager) sendWebhook(webhook WebhookConfig, alert *alerts.A
}
}
jsonData, err = n.generatePayloadFromTemplate(enhanced.PayloadTemplate, data)
jsonData, err = n.generatePayloadFromTemplateWithService(enhanced.PayloadTemplate, data, webhook.Service)
if err != nil {
log.Error().
Err(err).
@@ -775,7 +775,7 @@ func (n *NotificationManager) sendWebhook(webhook WebhookConfig, alert *alerts.A
}
}
jsonData, err = n.generatePayloadFromTemplate(enhanced.PayloadTemplate, data)
jsonData, err = n.generatePayloadFromTemplateWithService(enhanced.PayloadTemplate, data, webhook.Service)
if err != nil {
log.Error().
Err(err).
@@ -841,6 +841,11 @@ func (n *NotificationManager) prepareWebhookData(alert *alerts.Alert, customFiel
// generatePayloadFromTemplate renders the payload using Go templates
func (n *NotificationManager) generatePayloadFromTemplate(templateStr string, data WebhookPayloadData) ([]byte, error) {
return n.generatePayloadFromTemplateWithService(templateStr, data, "")
}
// generatePayloadFromTemplateWithService renders the payload using Go templates with service-specific handling
func (n *NotificationManager) generatePayloadFromTemplateWithService(templateStr string, data WebhookPayloadData, service string) ([]byte, error) {
// Create template with helper functions
funcMap := template.FuncMap{
"title": func(s string) string {
@@ -865,7 +870,13 @@ func (n *NotificationManager) generatePayloadFromTemplate(templateStr string, da
return nil, fmt.Errorf("template execution failed: %w", err)
}
// Validate that the generated payload is valid JSON
// Skip JSON validation for services that use plain text payloads
if service == "ntfy" {
// ntfy uses plain text, not JSON
return buf.Bytes(), nil
}
// Validate that the generated payload is valid JSON for other services
var jsonCheck interface{}
if err := json.Unmarshal(buf.Bytes(), &jsonCheck); err != nil {
log.Error().
+18 -17
View File
@@ -264,23 +264,24 @@ func GetWebhookTemplates() []WebhookTemplate {
Name: "ntfy.sh",
URLPattern: "https://ntfy.sh/{topic}",
Method: "POST",
Headers: map[string]string{"Content-Type": "application/json"},
PayloadTemplate: `{
"topic": "{{.CustomFields.topic}}",
"message": "{{.Message}}",
"title": "Pulse Alert: {{.Level | title}}",
"priority": {{if eq .Level "critical"}}5{{else if eq .Level "warning"}}4{{else}}3{{end}},
"tags": ["{{.Level}}", "{{.Type}}", "pulse"],
"click": "{{.Instance}}",
"actions": [
{
"action": "view",
"label": "View in Pulse",
"url": "{{.Instance}}"
}
],
"markdown": true
}`,
Headers: map[string]string{
"Content-Type": "text/plain",
"Title": "Pulse Alert",
"Priority": "urgent",
"Tags": "pulse,monitoring",
},
PayloadTemplate: `🚨 {{.Level | title}} Alert: {{.ResourceName}}
{{.Message}}
📊 Details:
• Node: {{.Node}}
• Type: {{.Type | title}}
• Value: {{if or (eq .Type "diskRead") (eq .Type "diskWrite")}}{{printf "%.1f" .Value}} MB/s{{else}}{{printf "%.1f" .Value}}%{{end}}
• Threshold: {{if or (eq .Type "diskRead") (eq .Type "diskWrite")}}{{printf "%.0f" .Threshold}} MB/s{{else}}{{printf "%.0f" .Threshold}}%{{end}}
• Duration: {{.Duration}}
View in Pulse: {{.Instance}}`,
Instructions: "1. Choose a topic name (e.g., 'my-pulse-alerts')\n2. URL format: https://ntfy.sh/YOUR_TOPIC\n Or for self-hosted: https://your-ntfy-server/YOUR_TOPIC\n3. Optional: Add authentication token in headers if required\n4. Subscribe to the topic in your ntfy app using the same topic name",
},
{