package watchdog import ( "log/slog" "orbits-server/internal/server/bootstrap" "orbits-server/internal/server/database" "time" "gorm.io/gorm" ) type State struct { FS map[string]struct{} DB map[string]database.File } type Result struct { FSOrphans []string DBOrphans []database.File } func Kickoff(env bootstrap.Environment, db *gorm.DB) { interval := time.Second * time.Duration(env.WatchdogInterval) ticker := time.NewTicker(interval) go func() { defer ticker.Stop() run(env, db) running := false for range ticker.C { if running { slog.Warn("watchdog is still running, skipping tick") continue } running = true func() { defer func() { running = false }() run(env, db) }() } }() } // the watchdog has 2 tasks // 1 revoke expires keys // 2 remove / sync orphaned files/db records func run(env bootstrap.Environment, db *gorm.DB) { slog.Debug("watchdog cycle start") revokeExpired(db) fsState, err := scanFS(env) if err != nil { slog.Error("scanFS failed", "error", err) return } dbState, err := scanDB(db) if err != nil { slog.Error("scanDB failed", "error", err) return } result := reconcile(fsState, dbState) if len(result.FSOrphans) > 0 { slog.Info("filesystem orphans detected") applyFS(env, db, result.FSOrphans) } if len(result.DBOrphans) > 0 { slog.Info("database orphans detected") applyDB(db, result.DBOrphans) } }