From e97780ad384ce555248229f3dab93046cc4e8257 Mon Sep 17 00:00:00 2001 From: Tyler Koenig Date: Thu, 14 May 2026 20:51:06 -0400 Subject: [PATCH] fix(tui,status,store): add delete confirm, input validation, XSS fix, history persistence Prevent accidental deletes with y/n confirmation dialog. Validate all numeric form inputs (interval, port, timeout, threshold, retries) with range checks instead of silently defaulting to zero. Escape user-supplied data in status page JavaScript to close XSS via monitor names. Persist check history to new check_history table so sparklines and uptime percentages survive restarts. --- cmd/goupkeep/main.go | 1 + internal/models/models.go | 8 +++- internal/monitor/history.go | 30 +++++++++++++++ internal/server/server.go | 12 ++++-- internal/store/postgres.go | 42 ++++++++++++++++++++- internal/store/sqlite.go | 43 ++++++++++++++++++++- internal/store/store.go | 6 ++- internal/tui/tab_sites.go | 60 ++++++++++++++++++++++++++--- internal/tui/tui.go | 75 +++++++++++++++++++++++++++++++------ 9 files changed, 253 insertions(+), 24 deletions(-) diff --git a/cmd/goupkeep/main.go b/cmd/goupkeep/main.go index 0ae9cb0..26c01b3 100644 --- a/cmd/goupkeep/main.go +++ b/cmd/goupkeep/main.go @@ -112,6 +112,7 @@ func main() { fmt.Printf("Imported %d monitors and %d alerts from Uptime Kuma v%s\n", len(backup.Sites), len(backup.Alerts), kb.Version) } + monitor.InitHistoryFromStore() monitor.StartEngine() server.Start(server.ServerConfig{ diff --git a/internal/models/models.go b/internal/models/models.go index 467fd33..cdf4c68 100644 --- a/internal/models/models.go +++ b/internal/models/models.go @@ -50,7 +50,13 @@ type User struct { Role string } -// Phase 5: Backup Structure +type CheckRecord struct { + SiteID int + LatencyNs int64 + IsUp bool + CheckedAt time.Time +} + type Backup struct { Sites []Site `json:"sites"` Alerts []AlertConfig `json:"alerts"` diff --git a/internal/monitor/history.go b/internal/monitor/history.go index c6fbcbe..8642255 100644 --- a/internal/monitor/history.go +++ b/internal/monitor/history.go @@ -1,6 +1,7 @@ package monitor import ( + "go-upkeep/internal/store" "sync" "time" ) @@ -19,6 +20,31 @@ var ( historyMu sync.RWMutex ) +func InitHistoryFromStore() { + s := store.Get() + if s == nil { + return + } + all := s.LoadAllHistory(maxHistoryLen) + historyMu.Lock() + defer historyMu.Unlock() + for siteID, records := range all { + h := &SiteHistory{} + for _, r := range records { + h.TotalChecks++ + if r.IsUp { + h.UpChecks++ + } + h.Latencies = append(h.Latencies, time.Duration(r.LatencyNs)) + h.Statuses = append(h.Statuses, r.IsUp) + } + histories[siteID] = h + } + if len(all) > 0 { + AddLog("Loaded check history from database") + } +} + func RecordCheck(siteID int, latency time.Duration, isUp bool) { historyMu.Lock() defer historyMu.Unlock() @@ -43,6 +69,10 @@ func RecordCheck(siteID int, latency time.Duration, isUp bool) { if len(h.Statuses) > maxHistoryLen { h.Statuses = h.Statuses[len(h.Statuses)-maxHistoryLen:] } + + if s := store.Get(); s != nil { + go s.SaveCheck(siteID, latency.Nanoseconds(), isUp) + } } func GetHistory(siteID int) (SiteHistory, bool) { diff --git a/internal/server/server.go b/internal/server/server.go index 89460ed..f814cc3 100644 --- a/internal/server/server.go +++ b/internal/server/server.go @@ -185,6 +185,12 @@ func renderStatusPage(w http.ResponseWriter, title string) {