3217cb4751
Replace external Healthchecks.io with Hub-native monitoring. New events table + /api/v1/event endpoint for structured events from controllers. Staleness checker (60s) detects unresponsive nodes. Backup deadline checker (daily 05:00) catches missed backups. Notification dispatcher sends operator (English) + customer (Hungarian) emails via Resend with per-event cooldowns. Event timeline on customer page, dashboard badges. Config form deprecates Monitoring UUIDs section. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
154 lines
5.3 KiB
Go
154 lines
5.3 KiB
Go
package notify
|
|
|
|
import (
|
|
"fmt"
|
|
"time"
|
|
)
|
|
|
|
// budapest timezone for formatting.
|
|
var budapest *time.Location
|
|
|
|
func init() {
|
|
var err error
|
|
budapest, err = time.LoadLocation("Europe/Budapest")
|
|
if err != nil {
|
|
budapest = time.FixedZone("CET", 3600)
|
|
}
|
|
}
|
|
|
|
// ──────────────────────────────────────────────────────────────────────
|
|
// Operator email — concise, English
|
|
// ──────────────────────────────────────────────────────────────────────
|
|
|
|
// FormatOperatorEmail returns (subject, textBody) for the operator channel.
|
|
func FormatOperatorEmail(customerID, eventType, severity, message, detailsJSON string) (string, string) {
|
|
icon := "⚠️"
|
|
if severity == "error" {
|
|
icon = "🔴"
|
|
}
|
|
|
|
subject := fmt.Sprintf("[Felhom] %s %s: %s", icon, customerID, eventType)
|
|
|
|
now := time.Now().In(budapest).Format("2006-01-02 15:04 MST")
|
|
body := fmt.Sprintf(`Customer: %s
|
|
Event: %s
|
|
Severity: %s
|
|
Time: %s
|
|
Message: %s`, customerID, eventType, severity, now, message)
|
|
|
|
if detailsJSON != "" && detailsJSON != "{}" {
|
|
body += fmt.Sprintf("\nDetails: %s", detailsJSON)
|
|
}
|
|
|
|
body += fmt.Sprintf("\n\nDashboard: https://hub.felhom.eu/customers/%s", customerID)
|
|
|
|
return subject, body
|
|
}
|
|
|
|
// ──────────────────────────────────────────────────────────────────────
|
|
// Customer email — Hungarian, friendly
|
|
// ──────────────────────────────────────────────────────────────────────
|
|
|
|
// customerMessages maps event_type → Hungarian customer message.
|
|
var customerMessages = map[string]string{
|
|
// Backup events
|
|
"backup_completed": "A biztonsági mentés sikeresen elkészült.",
|
|
"backup_failed": "A biztonsági mentés sikertelen! Kérjük, ellenőrizd a rendszert.",
|
|
"db_dump_completed": "Az adatbázis mentés sikeresen elkészült.",
|
|
"db_dump_failed": "Az adatbázis mentés sikertelen!",
|
|
"backup_integrity_ok": "A mentés integritás ellenőrzés sikeres.",
|
|
"backup_integrity_failed": "A mentés integritás ellenőrzés hibát talált!",
|
|
"crossdrive_completed": "A másodlagos mentés sikeresen elkészült.",
|
|
"crossdrive_failed": "A másodlagos mentés sikertelen!",
|
|
|
|
// Disk events
|
|
"disk_warning": "A lemezterület 90% felett van — kérjük, szabadíts fel helyet.",
|
|
"disk_critical": "A lemezterület kritikusan magas (95%+) — azonnali beavatkozás szükséges!",
|
|
|
|
// Storage events
|
|
"storage_disconnected": "Egy meghajtó leválasztva — a mentések szünetelhetnek.",
|
|
"storage_reconnected": "A meghajtó újra csatlakoztatva.",
|
|
|
|
// Staleness events (Hub-generated)
|
|
"node_stale": "A szerver nem küldött jelentést az elmúlt időszakban.",
|
|
"node_down": "A szerver nem elérhető!",
|
|
"node_recovered": "A szerver újra elérhető.",
|
|
|
|
// Health events
|
|
"health_degraded": "A rendszer állapota romlott.",
|
|
"health_critical": "A rendszer állapota kritikus!",
|
|
"health_recovered": "A rendszer állapota helyreállt.",
|
|
|
|
// Controller events
|
|
"controller_started": "A vezérlő elindult.",
|
|
"controller_updated": "A vezérlő frissítve lett.",
|
|
|
|
// Deadline events (Hub-generated)
|
|
"expected_backup_missed": "A mai biztonsági mentés nem készült el a határidőig!",
|
|
"expected_dbdump_missed": "A mai adatbázis mentés nem készült el a határidőig!",
|
|
|
|
// App lifecycle events
|
|
"app_deployed": "Alkalmazás telepítve.",
|
|
"app_removed": "Alkalmazás eltávolítva.",
|
|
|
|
// Disaster recovery events
|
|
"disaster_recovery_started": "Katasztrófa helyreállítás elindítva.",
|
|
"disaster_recovery_completed": "Katasztrófa helyreállítás befejezve.",
|
|
|
|
// Test
|
|
"test": "Ez egy teszt értesítés.",
|
|
}
|
|
|
|
// severityLabels maps severity to Hungarian labels.
|
|
var severityLabels = map[string]string{
|
|
"info": "Információ",
|
|
"warning": "Figyelmeztetés",
|
|
"error": "Hiba",
|
|
}
|
|
|
|
// FormatCustomerEmail returns (subject, textBody) for the customer channel.
|
|
func FormatCustomerEmail(customerID, eventType, severity, message, detailsJSON string) (string, string) {
|
|
label := severityLabels[severity]
|
|
if label == "" {
|
|
label = severity
|
|
}
|
|
|
|
// Use the per-event-type Hungarian message if available, otherwise fall back to message
|
|
hunMessage := customerMessages[eventType]
|
|
if hunMessage == "" {
|
|
hunMessage = message
|
|
}
|
|
|
|
subject := fmt.Sprintf("[Felhom] %s: %s", label, hunMessage)
|
|
|
|
now := time.Now().In(budapest).Format("2006-01-02 15:04")
|
|
body := fmt.Sprintf(`Kedves Ügyfél!
|
|
|
|
A Felhom rendszered a következő értesítést küldte:
|
|
|
|
%s
|
|
|
|
Részletek:
|
|
- Szerver: %s
|
|
- Időpont: %s
|
|
- Szint: %s
|
|
- Típus: %s`, hunMessage, customerID, now, label, eventType)
|
|
|
|
if message != "" && message != hunMessage {
|
|
body += fmt.Sprintf("\n- Üzenet: %s", message)
|
|
}
|
|
|
|
if detailsJSON != "" && detailsJSON != "{}" {
|
|
body += fmt.Sprintf("\n- Megjegyzés: %s", detailsJSON)
|
|
}
|
|
|
|
body += `
|
|
|
|
Ha kérdésed van, vedd fel a kapcsolatot az üzemeltetővel.
|
|
|
|
Üdvözlettel,
|
|
Felhom.eu monitoring`
|
|
|
|
return subject, body
|
|
}
|