diff options
Diffstat (limited to 'internal/server/server.go')
| -rw-r--r-- | internal/server/server.go | 101 |
1 files changed, 101 insertions, 0 deletions
diff --git a/internal/server/server.go b/internal/server/server.go index a5ded3d..d7dacaa 100644 --- a/internal/server/server.go +++ b/internal/server/server.go @@ -71,6 +71,13 @@ func New(cfg *config.Config, store *storage.Storage, sched *monitor.Scheduler, l mux.HandleFunc("GET /api/monitor/{name}", s.withAPIAuth(s.handleAPIMonitor)) mux.HandleFunc("GET /api/history/{name}", s.withAPIAuth(s.handleAPIHistory)) + // Full page data endpoint - public if refresh_mode=api, otherwise follows api.access + if cfg.Display.RefreshMode == "api" { + mux.HandleFunc("GET /api/page", s.handleAPIPage) + } else { + mux.HandleFunc("GET /api/page", s.withAPIAuth(s.handleAPIPage)) + } + // Create HTTP server s.server = &http.Server{ Addr: fmt.Sprintf("%s:%d", cfg.Server.Host, cfg.Server.Port), @@ -536,6 +543,100 @@ func (s *Server) handleAPIHistory(w http.ResponseWriter, r *http.Request) { }) } +// APIPageResponse contains all data needed to render/update the status page +type APIPageResponse struct { + Monitors map[string]APIMonitorData `json:"monitors"` + OverallStatus string `json:"overall_status"` + Counts StatusCounts `json:"counts"` + LastUpdated time.Time `json:"last_updated"` +} + +// APIMonitorData contains monitor data for the API page response +type APIMonitorData struct { + Status string `json:"status"` + ResponseTime int64 `json:"response_time"` + Uptime float64 `json:"uptime"` + LastError string `json:"last_error,omitempty"` + SSLDaysLeft int `json:"ssl_days_left,omitempty"` + Ticks []*storage.TickData `json:"ticks"` +} + +// handleAPIPage returns all data needed to update the status page in a single request +func (s *Server) handleAPIPage(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + + // Get all monitor stats + stats, err := s.storage.GetAllMonitorStats(ctx) + if err != nil { + s.jsonError(w, "Failed to get stats", http.StatusInternalServerError) + return + } + + response := APIPageResponse{ + Monitors: make(map[string]APIMonitorData), + LastUpdated: time.Now(), + } + + overallUp := true + hasDegraded := false + + // Build monitor data with history + for _, group := range s.config.Groups { + for _, monCfg := range group.Monitors { + stat, ok := stats[monCfg.Name] + if !ok { + continue + } + + // Get history ticks + ticks, err := s.storage.GetAggregatedHistory( + ctx, + monCfg.Name, + s.config.Display.TickCount, + s.config.Display.TickMode, + s.config.Display.PingFixedSlots, + ) + if err != nil { + s.logger.Error("failed to get tick history", "monitor", monCfg.Name, "error", err) + ticks = nil + } + + response.Monitors[monCfg.Name] = APIMonitorData{ + Status: stat.CurrentStatus, + ResponseTime: stat.LastResponseTime, + Uptime: stat.UptimePercent, + LastError: stat.LastError, + SSLDaysLeft: stat.SSLDaysLeft, + Ticks: ticks, + } + + // Track status counts + response.Counts.Total++ + switch stat.CurrentStatus { + case "down": + overallUp = false + response.Counts.Down++ + case "degraded": + hasDegraded = true + response.Counts.Degraded++ + case "up": + response.Counts.Up++ + } + } + } + + // Determine overall status + if !overallUp { + response.OverallStatus = "Major Outage" + } else if hasDegraded { + response.OverallStatus = "Partial Outage" + } else { + response.OverallStatus = "All Systems Operational" + } + + s.jsonResponse(w, response) +} + // jsonResponse writes a JSON response func (s *Server) jsonResponse(w http.ResponseWriter, data interface{}) { w.Header().Set("Content-Type", "application/json") |