fix(backups): off-box LastRun is an RFC3339 string — 500'd the backups page

Found during the D0 click-through: OffboxTarget.LastRun persists as an
RFC3339 string (settings.json), but backups.html passed it to timeAgo
(expects time.Time), so GET /backups returned 500 on any node where an
off-box backup had ever run. Pre-existing since v0.93.0 (NAS Part B),
unrelated to the re-skin. New timeAgoStr funcmap helper parses the
string (falls back to the raw value); also v2-token fix for the offbox
error hint (var(--danger) fallback hex) and the dead stat-running/
stat-stopped classes on the offbox status card.
This commit is contained in:
2026-07-02 14:47:22 +02:00
parent 7df061c00f
commit 4906524c11
2 changed files with 26 additions and 3 deletions
+23
View File
@@ -229,6 +229,29 @@ func (s *Server) templateFuncMap() template.FuncMap {
return fmt.Sprintf("%d napja", int(d.Hours()/24))
}
},
// timeAgoStr is timeAgo for RFC3339 string timestamps (e.g. OffboxTarget.LastRun,
// which persists as a string in settings.json). Found during D0: the backups page
// passed the raw string to timeAgo and 500'd once an off-box backup had ever run.
"timeAgoStr": func(s string) string {
t, err := time.Parse(time.RFC3339, s)
if err != nil {
return s
}
now := time.Now().In(loc)
d := now.Sub(t.In(loc))
switch {
case d < time.Minute:
return "most"
case d < time.Hour:
return fmt.Sprintf("%d perce", int(d.Minutes()))
case d < 24*time.Hour:
return fmt.Sprintf("%d órája", int(d.Hours()))
case d < 48*time.Hour:
return "tegnap"
default:
return fmt.Sprintf("%d napja", int(d.Hours()/24))
}
},
"fmtTime": func(t time.Time) string {
if t.IsZero() {
return ""