v0.168.0: customer-configurable backup window (Mentési időablak)

ONE setting (window start W) drives every nightly leg at fixed, never-stored
offsets: DB dump at W, tier-2 at W+60m, off-box at W+105m (wrap-safe). Precedence
settings > controller.yaml db_dump_schedule > 02:30.

- scheduler.UpdateDaily: retime a daily job at runtime (no restart) via a per-job
  buffered resched chan + a select case in runDailyJob.
- new pure package internal/backupwindow (LegTimes/GateWindow/EffectiveWindow).
- quiesce disk-tier window gate: scheduled cycles run only inside [W+2h,W+6h) with a
  safety valve (age>cadence+24h runs regardless); manual TriggerNow never gated.
  Backend.Due now also returns the backup age (from the agent's own /backup/due).
- backup page: Mentési időablak card (time input + derived leg/gate rows); POST
  /backups/window validates -> saves -> UpdateDaily x3 -> flash.

Tests: 5 groups, all red-proofed. Agent/cadence//backup/due untouched.
This commit is contained in:
2026-07-24 20:55:44 +02:00
parent e33c1aeabc
commit 82c67e32e1
19 changed files with 831 additions and 31 deletions
+5 -2
View File
@@ -174,7 +174,7 @@ func (s *Server) dashboardHandler(w http.ResponseWriter, r *http.Request) {
// Backup status
data["BackupEnabled"] = s.cfg.Backup.Enabled
if s.backupMgr != nil {
nextDBDump := scheduler.NextDailyRun(s.cfg.Backup.DBDumpSchedule)
nextDBDump := scheduler.NextDailyRun(s.effectiveBackupWindow())
fullStatus := s.backupMgr.GetFullStatus(nextDBDump)
data["DBDumpStatus"] = fullStatus.LastDBDump
// F3 (AUDIT-vacation-remote-ops-2026-07-20): the card's "Utolsó mentés" row branches on
@@ -731,7 +731,7 @@ func (s *Server) backupsCommonData(page, title string, r *http.Request) map[stri
data["Backup"] = nil
return data
}
nextDBDump := scheduler.NextDailyRun(s.cfg.Backup.DBDumpSchedule)
nextDBDump := scheduler.NextDailyRun(s.effectiveBackupWindow())
fullStatus := s.backupMgr.GetFullStatus(nextDBDump)
// Pass flash messages from query params (set by redirect handlers)
@@ -818,6 +818,9 @@ func (s *Server) backupsHandler(w http.ResponseWriter, r *http.Request) {
// Whole-guest backup view (agent-sourced, read-only) for the "Rendszermentés" section.
data["GuestBackup"] = s.loadGuestBackup(r.Context())
// Customer-configurable backup window (v0.168.0): effective start + derived leg/gate times.
s.backupWindowData(data)
if fullStatus, ok := data["Backup"].(*backup.FullBackupStatus); ok && fullStatus != nil {
// DB-section state — honest messaging for embedded-DB-only boxes (SQLite etc.):
// "dumps" (real dumps) | "pending" (discovered, first run tonight) | "embedded".