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:
@@ -61,6 +61,11 @@ type Settings struct {
|
||||
// Per-app backup preferences
|
||||
AppBackup map[string]AppBackupPrefs `json:"app_backup,omitempty"`
|
||||
|
||||
// Customer-configurable backup-window start "HH:MM" (v0.168.0). "" = use controller.yaml
|
||||
// db_dump_schedule (then the "02:30" default). Every nightly leg derives from this at fixed
|
||||
// offsets; overrides yaml when a valid value is present (mirrors PasswordHash precedence).
|
||||
BackupWindowStart string `json:"backup_window_start,omitempty"`
|
||||
|
||||
// Storage paths registry
|
||||
StoragePaths []StoragePath `json:"storage_paths,omitempty"`
|
||||
|
||||
@@ -146,7 +151,7 @@ type AppBackupPrefs struct {
|
||||
type OffboxTarget struct {
|
||||
Enabled bool `json:"enabled"`
|
||||
Host string `json:"host"`
|
||||
Port int `json:"port"` // default 22
|
||||
Port int `json:"port"` // default 22
|
||||
User string `json:"user"`
|
||||
RepoPath string `json:"repo_path"` // absolute path on the NAS, e.g. /volume1/felhom-backup/repo
|
||||
Schedule string `json:"schedule"` // "daily" | "manual"
|
||||
@@ -217,7 +222,7 @@ type CrossDriveBackup struct {
|
||||
LastRun string `json:"last_run,omitempty"` // RFC3339
|
||||
LastStatus string `json:"last_status,omitempty"` // "ok", "error", "running"
|
||||
LastError string `json:"last_error,omitempty"`
|
||||
LastWarning string `json:"last_warning,omitempty"` // Tier-2 3b: capture-gap / state-only notice (Hungarian)
|
||||
LastWarning string `json:"last_warning,omitempty"` // Tier-2 3b: capture-gap / state-only notice (Hungarian)
|
||||
LastDuration string `json:"last_duration,omitempty"` // "2m34s"
|
||||
LastSizeHuman string `json:"last_size_human,omitempty"` // "1.2 GB"
|
||||
|
||||
@@ -492,6 +497,26 @@ func (s *Settings) SetPasswordHash(hash string) error {
|
||||
|
||||
// ── Guest launcher share (v0.165.0) ──────────────────────────────────────────────
|
||||
|
||||
// ── Backup window (v0.168.0) ─────────────────────────────────────────────────────
|
||||
|
||||
// GetBackupWindowStart returns the customer-set backup-window start "HH:MM" ("" = fall back to
|
||||
// controller.yaml, then the default — resolve via backupwindow.EffectiveWindow, never in isolation).
|
||||
func (s *Settings) GetBackupWindowStart() string {
|
||||
s.mu.RLock()
|
||||
defer s.mu.RUnlock()
|
||||
return s.BackupWindowStart
|
||||
}
|
||||
|
||||
// SetBackupWindowStart stores (or clears, on "") the backup-window start and saves. The caller
|
||||
// validates the HH:MM format first (the scheduler/backupwindow gate) and fans the change out to the
|
||||
// three daily legs via UpdateDaily — this only persists the single source-of-truth value.
|
||||
func (s *Settings) SetBackupWindowStart(start string) error {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
s.BackupWindowStart = start
|
||||
return s.save()
|
||||
}
|
||||
|
||||
// GetLauncherShareToken returns the guest-launcher capability token ("" = sharing disabled).
|
||||
func (s *Settings) GetLauncherShareToken() string {
|
||||
s.mu.RLock()
|
||||
|
||||
Reference in New Issue
Block a user