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:
@@ -8,6 +8,23 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
budapestLoc *time.Location
|
||||
budapestLocOnce sync.Once
|
||||
)
|
||||
|
||||
func getBudapestLocation() *time.Location {
|
||||
budapestLocOnce.Do(func() {
|
||||
loc, err := time.LoadLocation("Europe/Budapest")
|
||||
if err != nil {
|
||||
log.Printf("[ERROR] Cannot load Europe/Budapest timezone: %v — using UTC", err)
|
||||
loc = time.UTC
|
||||
}
|
||||
budapestLoc = loc
|
||||
})
|
||||
return budapestLoc
|
||||
}
|
||||
|
||||
// JobFunc is the function signature for scheduler jobs.
|
||||
type JobFunc func(ctx context.Context) error
|
||||
|
||||
@@ -41,6 +58,11 @@ func New(logger *log.Logger) *Scheduler {
|
||||
|
||||
// Every registers a periodic job that runs every interval.
|
||||
func (s *Scheduler) Every(name string, interval time.Duration, fn JobFunc) {
|
||||
if interval <= 0 {
|
||||
s.logger.Printf("[ERROR] Periodic job %s has invalid interval %s — job not registered", name, interval)
|
||||
return
|
||||
}
|
||||
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
@@ -187,6 +209,7 @@ func (s *Scheduler) executeJob(job *Job, quiet bool) {
|
||||
if r := recover(); r != nil {
|
||||
s.mu.Lock()
|
||||
job.LastErr = fmt.Errorf("panic: %v", r)
|
||||
job.LastRun = time.Now()
|
||||
s.mu.Unlock()
|
||||
s.logger.Printf("[ERROR] Job %s panicked: %v", job.Name, r)
|
||||
}
|
||||
@@ -238,18 +261,16 @@ func nextDailyRun(timeStr string) time.Time {
|
||||
return time.Now().Add(24 * time.Hour)
|
||||
}
|
||||
|
||||
loc, err := time.LoadLocation("Europe/Budapest")
|
||||
if err != nil {
|
||||
// Fallback to UTC if timezone not available
|
||||
loc = time.UTC
|
||||
}
|
||||
loc := getBudapestLocation()
|
||||
|
||||
now := time.Now().In(loc)
|
||||
next := time.Date(now.Year(), now.Month(), now.Day(), hour, min, 0, 0, loc)
|
||||
|
||||
// If the time has already passed today, schedule for tomorrow
|
||||
// If the time has already passed today, schedule for tomorrow.
|
||||
// Use time.Date with day+1 instead of Add(24h) to correctly handle DST transitions
|
||||
// (spring forward/fall back in Europe/Budapest would shift by 1 hour with Add(24h)).
|
||||
if !next.After(now) {
|
||||
next = next.Add(24 * time.Hour)
|
||||
next = time.Date(now.Year(), now.Month(), now.Day()+1, hour, min, 0, 0, loc)
|
||||
}
|
||||
|
||||
return next
|
||||
|
||||
Reference in New Issue
Block a user