aboutsummaryrefslogtreecommitdiff
path: root/internal/server/server.go
diff options
context:
space:
mode:
Diffstat (limited to 'internal/server/server.go')
-rw-r--r--internal/server/server.go101
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")