diff options
| author | Fuwn <[email protected]> | 2026-01-20 06:32:58 -0800 |
|---|---|---|
| committer | Fuwn <[email protected]> | 2026-01-20 06:32:58 -0800 |
| commit | a5e4fa5f372c6b2fb6e6194094015e5744c410dc (patch) | |
| tree | 27378a93fdc8d27cebf79ca2c0e11480af68a8b6 /internal/storage | |
| parent | feat: Add custom_head option for injecting HTML into head (diff) | |
| download | kaze-a5e4fa5f372c6b2fb6e6194094015e5744c410dc.tar.xz kaze-a5e4fa5f372c6b2fb6e6194094015e5744c410dc.zip | |
feat: Add uptime tooltip showing last failure time and reason
Diffstat (limited to 'internal/storage')
| -rw-r--r-- | internal/storage/sqlite.go | 32 |
1 files changed, 32 insertions, 0 deletions
diff --git a/internal/storage/sqlite.go b/internal/storage/sqlite.go index 48cf432..ce62e67 100644 --- a/internal/storage/sqlite.go +++ b/internal/storage/sqlite.go @@ -52,6 +52,8 @@ type MonitorStats struct { SSLExpiry *time.Time SSLDaysLeft int TotalChecks int64 + LastFailure *time.Time // Time of last failure + LastFailureError string // Error message from last failure } // TickData represents aggregated data for one tick in the history bar @@ -375,6 +377,36 @@ func (s *Storage) GetAllMonitorStats(ctx context.Context) (map[string]*MonitorSt } } + // Get last failure for each monitor + rows, err = s.db.QueryContext(ctx, ` + SELECT monitor_name, timestamp, error + FROM check_results + WHERE status != 'up' AND error != '' + AND (monitor_name, timestamp) IN ( + SELECT monitor_name, MAX(timestamp) + FROM check_results + WHERE status != 'up' AND error != '' + GROUP BY monitor_name + ) + `) + if err != nil { + return nil, fmt.Errorf("failed to query last failures: %w", err) + } + defer rows.Close() + + for rows.Next() { + var name string + var timestamp time.Time + var errorMsg string + if err := rows.Scan(&name, ×tamp, &errorMsg); err != nil { + return nil, fmt.Errorf("failed to scan last failure: %w", err) + } + if ms, ok := stats[name]; ok { + ms.LastFailure = ×tamp + ms.LastFailureError = errorMsg + } + } + return stats, nil } |