v0.12.4 — 15 bug fixes (CRITICAL/HIGH/MEDIUM)

CRITICAL:
- C1: SetAppBackupBulk data loss + nil map panic (settings.go)
- C2: UpdateStackConfig nil Env map panic (deploy.go)
- C3: ValidateDump missing scanner.Err() check (dbdump.go)

HIGH:
- H1: nextDailyRun DST bug — use time.Date(day+1) not Add(24h)
- H2: Cache Europe/Budapest timezone with sync.Once in scheduler
- H3: settings.save() leaks .tmp file on WriteFile failure
- H4: SetNotificationPrefs nil pointer panic
- H5: appDirSize + getDirSizeBytes ignore Sscanf return value
- H6: getDirSizeBytes has no timeout — add 30s context
- H7: dbdump.go tmpFile not using defer Close
- H8: UpdateCrossDriveStatus misleading comment

MEDIUM:
- M1: Replace custom containsBytes with strings.Contains
- M2: scheduler.Every() validates interval > 0
- M3: executeJob panic recovery now sets LastRun
- M4: logPostStartStatus copies env slice before goroutine
- M5: Cache timezone in web package via getTimezone() sync.Once

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-02-18 07:50:02 +01:00
parent 731cca15a8
commit d160c6c06d
11 changed files with 115 additions and 42 deletions
+20 -4
View File
@@ -4,18 +4,34 @@ import (
"fmt"
"html/template"
"strings"
"sync"
"time"
"gitea.dooplex.hu/admin/felhom-controller/internal/backup"
"gitea.dooplex.hu/admin/felhom-controller/internal/stacks"
)
var (
webTimezone *time.Location
webTimezoneOnce sync.Once
)
// getTimezone returns the Europe/Budapest timezone, cached after first load.
// Falls back to UTC if tzdata is unavailable.
func getTimezone() *time.Location {
webTimezoneOnce.Do(func() {
loc, err := time.LoadLocation("Europe/Budapest")
if err != nil {
loc = time.UTC
}
webTimezone = loc
})
return webTimezone
}
// templateFuncMap returns the FuncMap used by all HTML templates.
func (s *Server) templateFuncMap() template.FuncMap {
loc, err := time.LoadLocation("Europe/Budapest")
if err != nil {
loc = time.UTC
}
loc := getTimezone()
return template.FuncMap{
"stateColor": func(state stacks.ContainerState) string {
+2 -11
View File
@@ -447,12 +447,7 @@ func (s *Server) backupsHandler(w http.ResponseWriter, r *http.Request) {
}
if cfg.LastRun != "" {
if t, err := time.Parse(time.RFC3339, cfg.LastRun); err == nil {
// M7: Handle LoadLocation error — fall back to UTC if tzdata missing.
loc, err := time.LoadLocation("Europe/Budapest")
if err != nil {
loc = time.UTC
}
item.LastRunShort = t.In(loc).Format("01-02 15:04")
item.LastRunShort = t.In(getTimezone()).Format("01-02 15:04")
}
}
fullStatus.CrossDriveSummary = append(fullStatus.CrossDriveSummary, item)
@@ -543,11 +538,7 @@ func (s *Server) buildAppBackupRows(
crossConfigs map[string]*settings.CrossDriveBackup,
destLabels map[string]string,
) []AppBackupRow {
// M7: Handle LoadLocation error — fall back to UTC if tzdata missing.
loc, err := time.LoadLocation("Europe/Budapest")
if err != nil {
loc = time.UTC
}
loc := getTimezone()
// Build a quick lookup: which stacks have a DB dump?
dbStacks := make(map[string]bool)