feat: Hub monitoring takeover — event push system + config cleanup (v0.21.0)
Replace external Healthchecks.io with Hub-native event system. Controller now pushes structured events via POST /api/v1/event with typed detail structs. Hub handles dead man's switch, notification dispatch, and cooldowns. Phase 5: PushEvent() core method, 21 event types, expanded notification settings (11 toggles), Hub connection monitoring on dashboard, alerts. Phase 6: Deprecation log for ping UUIDs, pinger kept for transition. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -411,21 +411,36 @@ func (s *Server) monitoringHandler(w http.ResponseWriter, _ *http.Request) {
|
||||
data["SystemInfo"] = system.GetInfo(s.primaryHDDPath(), s.cpuCollector)
|
||||
data["StorageBars"] = s.buildStorageBars()
|
||||
|
||||
// On monitoring page, exclude the "pings-missing" alert since the detailed table is visible
|
||||
if s.alertManager != nil {
|
||||
data["Alerts"] = s.alertManager.GetAlerts("pings-missing")
|
||||
data["Alerts"] = s.alertManager.GetAlerts()
|
||||
data["DiskWarnings"] = s.alertManager.GetInlineAlerts("monitoring")
|
||||
}
|
||||
|
||||
// Ping status section
|
||||
// Hub connection status section
|
||||
data["HubEnabled"] = s.cfg.Hub.Enabled && s.cfg.Hub.URL != ""
|
||||
data["HubURL"] = s.cfg.Hub.URL
|
||||
data["CustomerID"] = s.cfg.Customer.ID
|
||||
|
||||
if s.hubPushStatusFn != nil {
|
||||
ps := s.hubPushStatusFn()
|
||||
data["HubLastAttempt"] = ps.LastAttempt
|
||||
data["HubLastSuccess"] = ps.LastSuccess
|
||||
data["HubLastError"] = ps.LastError
|
||||
data["HubConsecutiveFailures"] = ps.Consecutive
|
||||
// Connected if last success was within 2x the push interval (or 30min default)
|
||||
connected := !ps.LastSuccess.IsZero() && time.Since(ps.LastSuccess) < 30*time.Minute
|
||||
data["HubConnected"] = connected
|
||||
}
|
||||
|
||||
// Legacy ping status section (still shown for backward compat during transition)
|
||||
data["MonitoringEnabled"] = s.cfg.Monitoring.Enabled
|
||||
if s.cfg.Monitoring.Enabled {
|
||||
pings := []map[string]interface{}{
|
||||
{"Label": "Életjel (Heartbeat)", "Icon": "💓", "Configured": isPingConfigured(s.cfg.Monitoring.PingUUIDs.Heartbeat), "Schedule": "5 percenként"},
|
||||
{"Label": "Rendszer állapot", "Icon": "🖥️", "Configured": isPingConfigured(s.cfg.Monitoring.PingUUIDs.SystemHealth), "Schedule": "5 percenként"},
|
||||
{"Label": "Adatbázis mentés", "Icon": "🗄️", "Configured": isPingConfigured(s.cfg.Monitoring.PingUUIDs.DBDump), "Schedule": "Naponta " + s.cfg.Backup.DBDumpSchedule},
|
||||
{"Label": "Biztonsági mentés", "Icon": "💾", "Configured": isPingConfigured(s.cfg.Monitoring.PingUUIDs.Backup), "Schedule": "Naponta " + s.cfg.Backup.ResticSchedule},
|
||||
{"Label": "Mentés integritás", "Icon": "🔍", "Configured": isPingConfigured(s.cfg.Monitoring.PingUUIDs.BackupIntegrity), "Schedule": "Hetente (vasárnap)"},
|
||||
{"Label": "Eletjel (Heartbeat)", "Icon": "heartbeat", "Configured": isPingConfigured(s.cfg.Monitoring.PingUUIDs.Heartbeat), "Schedule": "5 percenkent"},
|
||||
{"Label": "Rendszer allapot", "Icon": "system", "Configured": isPingConfigured(s.cfg.Monitoring.PingUUIDs.SystemHealth), "Schedule": "5 percenkent"},
|
||||
{"Label": "Adatbazis mentes", "Icon": "db", "Configured": isPingConfigured(s.cfg.Monitoring.PingUUIDs.DBDump), "Schedule": "Naponta " + s.cfg.Backup.DBDumpSchedule},
|
||||
{"Label": "Biztonsagi mentes", "Icon": "backup", "Configured": isPingConfigured(s.cfg.Monitoring.PingUUIDs.Backup), "Schedule": "Naponta " + s.cfg.Backup.ResticSchedule},
|
||||
{"Label": "Mentes integritas", "Icon": "integrity", "Configured": isPingConfigured(s.cfg.Monitoring.PingUUIDs.BackupIntegrity), "Schedule": "Hetente (vasarnap)"},
|
||||
}
|
||||
allConfigured := true
|
||||
for _, p := range pings {
|
||||
@@ -1076,11 +1091,24 @@ func (s *Server) settingsNotificationsHandler(w http.ResponseWriter, r *http.Req
|
||||
|
||||
// Collect enabled events from checkboxes
|
||||
var enabledEvents []string
|
||||
for _, evt := range []string{"disk_warning", "backup_failed", "update_available", "security_update"} {
|
||||
// Single-event checkboxes
|
||||
for _, evt := range []string{
|
||||
"backup_failed", "db_dump_failed", "backup_integrity_failed",
|
||||
"crossdrive_failed", "storage_disconnected",
|
||||
"node_down", "health_critical",
|
||||
"storage_reconnected", "health_recovered",
|
||||
} {
|
||||
if r.FormValue("event_"+evt) == "on" {
|
||||
enabledEvents = append(enabledEvents, evt)
|
||||
}
|
||||
}
|
||||
// Compound toggles: one checkbox → two event types
|
||||
if r.FormValue("event_disk_alerts") == "on" {
|
||||
enabledEvents = append(enabledEvents, "disk_warning", "disk_critical")
|
||||
}
|
||||
if r.FormValue("event_expected_missed") == "on" {
|
||||
enabledEvents = append(enabledEvents, "expected_backup_missed", "expected_dbdump_missed")
|
||||
}
|
||||
|
||||
prefs := &settings.NotificationPrefs{
|
||||
Email: email,
|
||||
@@ -1101,7 +1129,7 @@ func (s *Server) settingsNotificationsHandler(w http.ResponseWriter, r *http.Req
|
||||
// Sync preferences to hub
|
||||
data := s.settingsData()
|
||||
if s.notifier != nil && s.notifier.IsEnabled() {
|
||||
if err := s.notifier.SyncPreferences(email, enabledEvents); err != nil {
|
||||
if err := s.notifier.SyncPreferences(email, enabledEvents, cooldownHours); err != nil {
|
||||
s.logger.Printf("[WARN] Failed to sync preferences to hub: %v", err)
|
||||
data["NotificationSuccess"] = fmt.Sprintf("Értesítési beállítások mentve (helyi). A központi szinkronizálás sikertelen: %v", err)
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user