feat: storage watchdog — USB disconnect detection, auto-stop, safe eject, auto-reconnect (v0.17.0)
New storage watchdog monitors registered storage paths every 5s. On disconnect (3 consecutive probe failures), auto-stops affected apps, lazy-unmounts stale VFS entries, fires alerts/notifications/hub report. On reconnect (UUID detected), auto-remounts via fstab, cleans stale restic locks, offers app restart. Safe disconnect UI for USB drives: confirmation dialog, stop apps, sync, unmount. Disconnected state visible across all pages (dashboard, settings, backups, monitoring) with hatched red bars and badges. Backup guards skip disconnected drives. 22 files changed (1 new: monitor/watchdog.go), ~1500 lines added. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -9,6 +9,7 @@ import (
|
||||
"gitea.dooplex.hu/admin/felhom-controller/internal/backup"
|
||||
"gitea.dooplex.hu/admin/felhom-controller/internal/config"
|
||||
"gitea.dooplex.hu/admin/felhom-controller/internal/monitor"
|
||||
"gitea.dooplex.hu/admin/felhom-controller/internal/settings"
|
||||
)
|
||||
|
||||
// Alert represents a persistent dashboard alert banner.
|
||||
@@ -39,10 +40,29 @@ func NewAlertManager(logger *log.Logger) *AlertManager {
|
||||
}
|
||||
|
||||
// Refresh regenerates alerts from the latest health check report and config state.
|
||||
// Called after each health check cycle (every 5 minutes).
|
||||
func (am *AlertManager) Refresh(report *monitor.HealthReport, cfg *config.Config, backupMgr *backup.Manager, updateAvailable bool, latestVersion string) {
|
||||
// Called after each health check cycle (every 5 minutes) and on storage state changes.
|
||||
func (am *AlertManager) Refresh(report *monitor.HealthReport, cfg *config.Config, backupMgr *backup.Manager, updateAvailable bool, latestVersion string, storagePaths ...[]settings.StoragePath) {
|
||||
var alerts []Alert
|
||||
|
||||
// Disconnected storage alerts (top-level error banners on all pages)
|
||||
if len(storagePaths) > 0 {
|
||||
for _, sp := range storagePaths[0] {
|
||||
if sp.Disconnected {
|
||||
label := sp.Label
|
||||
if label == "" {
|
||||
label = sp.Path
|
||||
}
|
||||
alerts = append(alerts, Alert{
|
||||
ID: "storage-disconnected-" + simpleHash(sp.Path),
|
||||
Level: "error",
|
||||
Message: fmt.Sprintf("Meghajtó leválasztva: %s (%s)", label, sp.Path),
|
||||
Link: "/settings",
|
||||
LinkText: "Beállítások",
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// From health check issues (critical)
|
||||
for _, issue := range report.Issues {
|
||||
alerts = append(alerts, Alert{
|
||||
|
||||
Reference in New Issue
Block a user