feat(alerts): Health-Alarme via Webhook + Email-SMTP
Sidebar → System → Alarme.
Migration 0021: alert_channels (kind=webhook|email, target, settings,
active) + alert_events (kind, severity=info/warning/error/critical,
subject, message, sent_to JSONB).
internal/services/alerts/:
- Fire(kind, severity, subject, message) — broadcastet an alle
aktiven Channels + persistiert Event mit per-Channel-Result
(ok/error) in sent_to.
- Webhook-Sender: POST JSON {kind, severity, subject, message,
content, text, fired_at}. Slack/Discord/Teams akzeptieren das
out-of-the-box ohne Adapter (content + text-Felder gleichzeitig).
- Email-Sender: net/smtp + STARTTLS optional. Settings (smtp_host,
smtp_port, username/password, from, use_tls) liegen in
channel.settings JSONB.
internal/handlers/alerts.go: CRUD + POST /alerts/test + GET
/alerts/events (history).
Scheduler-Trigger:
- cert.expiring — TLS-Cert <14 Tage Restzeit (12h-dedupe pro cert)
severity warning, <3 Tage → error
- cert.renew_failed — Renewer-Cycle hat fails
- cert.renewer.run_failed — Renewer-Cycle abgebrochen
- backup.failed — Scheduled Backup error
- license.invalid — License-Server liefert valid=false
In-process Dedupe (12h TTL, map[key]time.Time) verhindert dass
identische Alerts in Schleifen feuern.
UI (pages/Alerts): Tabs Channels (CRUD-Tabelle, Add-Modal mit
conditional-Email-Fields) + History (200 letzte Events mit
severity-Tag + per-Channel-Delivery-Status). Header-Button
„Test-Alert" feuert einen Test-Event in alle aktiven Channels.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -22,6 +22,7 @@
|
||||
"logs": "Logs",
|
||||
"backups": "Backups",
|
||||
"diagnostics": "Diagnose",
|
||||
"alerts": "Alarme",
|
||||
"license": "Lizenz",
|
||||
"settings": "Einstellungen",
|
||||
"section": {
|
||||
@@ -682,6 +683,34 @@
|
||||
"src": "Quell-IP"
|
||||
}
|
||||
},
|
||||
"alerts": {
|
||||
"title": "Health-Alarme",
|
||||
"intro": "Notification-Channels für kritische Events. Webhook (Slack/Discord/Teams/Generic-HTTP) oder Email (SMTP). Triggers: cert.expiring (<14 d), cert.renew_failed, backup.failed, license.invalid.",
|
||||
"scopeTitle": "Was triggert Alarme?",
|
||||
"scopeDesc": "cert.expiring — TLS-Zertifikat <14 Tage Restzeit (dedupe 12h). cert.renew_failed — ACME-Renewer hat Fails. backup.failed — Scheduled Backup konnte nicht erstellt werden. license.invalid — License-Server liefert valid=false. Mehr Triggers folgen (Backend-Down, Disk-Usage).",
|
||||
"tabs": { "channels": "Channels", "events": "History" },
|
||||
"add": "Channel hinzufügen",
|
||||
"addTitle": "Notification-Channel anlegen",
|
||||
"editTitle": "Channel bearbeiten",
|
||||
"test": "Test-Alert",
|
||||
"testDone": "Test gesendet — {{ok}}/{{total}} Channels erfolgreich",
|
||||
"emptyChannels": "Keine Channels. Lege einen Webhook oder eine Email an.",
|
||||
"emptyEvents": "Noch keine Alarme — Triggers haben noch keinen Event gefeuert.",
|
||||
"noChannels": "kein Channel aktiv",
|
||||
"confirmDelete": "Channel {{name}} wirklich löschen?",
|
||||
"col": {
|
||||
"name": "Name",
|
||||
"kind": "Typ",
|
||||
"target": "Ziel",
|
||||
"targetWebhook": "Webhook-URL",
|
||||
"targetEmail": "Empfänger-Email",
|
||||
"active": "Aktiv",
|
||||
"time": "Zeit",
|
||||
"severity": "Severity",
|
||||
"subject": "Betreff",
|
||||
"delivered": "Gesendet"
|
||||
}
|
||||
},
|
||||
"diag": {
|
||||
"title": "Diagnose",
|
||||
"intro": "Operator-Tools direkt aus dem UI: ping, traceroute, DNS, HTTP-Probe, TCP-Connect. Alle Calls laufen authentifiziert auf dieser Box (nicht im Browser).",
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
"logs": "Logs",
|
||||
"backups": "Backups",
|
||||
"diagnostics": "Diagnostics",
|
||||
"alerts": "Alerts",
|
||||
"license": "License",
|
||||
"settings": "Settings",
|
||||
"section": {
|
||||
@@ -682,6 +683,34 @@
|
||||
"src": "Source IP"
|
||||
}
|
||||
},
|
||||
"alerts": {
|
||||
"title": "Health alerts",
|
||||
"intro": "Notification channels for critical events. Webhook (Slack/Discord/Teams/generic-HTTP) or email (SMTP). Triggers: cert.expiring (<14 d), cert.renew_failed, backup.failed, license.invalid.",
|
||||
"scopeTitle": "What triggers alerts?",
|
||||
"scopeDesc": "cert.expiring — TLS cert <14 days remaining (12 h dedupe). cert.renew_failed — ACME renewer cycle had failures. backup.failed — scheduled backup couldn't run. license.invalid — License server returns valid=false. More triggers coming (backend-down, disk usage).",
|
||||
"tabs": { "channels": "Channels", "events": "History" },
|
||||
"add": "Add channel",
|
||||
"addTitle": "Add notification channel",
|
||||
"editTitle": "Edit channel",
|
||||
"test": "Test alert",
|
||||
"testDone": "Test sent — {{ok}}/{{total}} channels OK",
|
||||
"emptyChannels": "No channels. Add a webhook or an email.",
|
||||
"emptyEvents": "No alerts yet — triggers haven't fired any events.",
|
||||
"noChannels": "no active channel",
|
||||
"confirmDelete": "Really delete channel {{name}}?",
|
||||
"col": {
|
||||
"name": "Name",
|
||||
"kind": "Kind",
|
||||
"target": "Target",
|
||||
"targetWebhook": "Webhook URL",
|
||||
"targetEmail": "Recipient email",
|
||||
"active": "Active",
|
||||
"time": "Time",
|
||||
"severity": "Severity",
|
||||
"subject": "Subject",
|
||||
"delivered": "Delivered"
|
||||
}
|
||||
},
|
||||
"diag": {
|
||||
"title": "Diagnostics",
|
||||
"intro": "Operator tools straight from the UI: ping, traceroute, DNS, HTTP probe, TCP connect. All calls run authenticated on this box (not in the browser).",
|
||||
|
||||
Reference in New Issue
Block a user