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:
@@ -29,6 +29,7 @@ const FirewallLivePage = lazy(() => import('./pages/FirewallLive'))
|
||||
const LogsPage = lazy(() => import('./pages/Logs'))
|
||||
const BackupsPage = lazy(() => import('./pages/Backups'))
|
||||
const DiagnosticsPage = lazy(() => import('./pages/Diagnostics'))
|
||||
const AlertsPage = lazy(() => import('./pages/Alerts'))
|
||||
const LicensePage = lazy(() => import('./pages/License'))
|
||||
const SettingsPage = lazy(() => import('./pages/Settings'))
|
||||
|
||||
@@ -117,6 +118,7 @@ export default function App() {
|
||||
<Route path="/logs" element={<LogsPage />} />
|
||||
<Route path="/backups" element={<BackupsPage />} />
|
||||
<Route path="/diagnostics" element={<DiagnosticsPage />} />
|
||||
<Route path="/alerts" element={<AlertsPage />} />
|
||||
<Route path="/license" element={<LicensePage />} />
|
||||
<Route path="/settings" element={<SettingsPage />} />
|
||||
</Route>
|
||||
|
||||
Reference in New Issue
Block a user