package handlers import ( "context" "encoding/json" "net/http" "sync" "time" "crowdsec-dashy/internal/crowdsec" ) // APIHandler serves the internal JSON API consumed by frontend JS. type APIHandler struct { deps Deps } func NewAPIHandler(deps Deps) *APIHandler { return &APIHandler{deps: deps} } type statsResponse struct { Decisions int `json:"decisions"` Alerts24h int `json:"alerts_24h"` Alerts7d int `json:"alerts_7d"` Bouncers int `json:"bouncers"` Machines int `json:"machines"` Healthy bool `json:"healthy"` } type healthResponse struct { Healthy bool `json:"healthy"` CLIAvailable bool `json:"cli_available"` } // Stats returns dashboard summary counts. func (h *APIHandler) Stats(w http.ResponseWriter, r *http.Request) { ctx, cancel := context.WithTimeout(r.Context(), 15*time.Second) defer cancel() // Fetch 24h and 7d alert counts in parallel to minimize latency. var alerts24h, alerts7d []crowdsec.Alert var wg sync.WaitGroup wg.Add(2) go func() { defer wg.Done() alerts24h, _ = h.deps.LAPI.ListAlerts(ctx, crowdsec.AlertFilter{Limit: 5000, Since: "24h"}) }() go func() { defer wg.Done() alerts7d, _ = h.deps.LAPI.ListAlerts(ctx, crowdsec.AlertFilter{Limit: 5000, Since: "168h"}) }() wg.Wait() resp := statsResponse{ Alerts24h: len(alerts24h), Alerts7d: len(alerts7d), Healthy: h.deps.LAPI.IsHealthy(ctx), } if h.deps.CLIAvailable { decisions, _ := h.deps.CLI.ListDecisions(ctx, crowdsec.DecisionFilter{Limit: 500}) bouncers, _ := h.deps.CLI.ListBouncers(ctx) machines, _ := h.deps.CLI.ListMachines(ctx) resp.Decisions = len(decisions) resp.Bouncers = len(bouncers) resp.Machines = len(machines) } w.Header().Set("Content-Type", "application/json") if err := json.NewEncoder(w).Encode(resp); err != nil { http.Error(w, "internal error", http.StatusInternalServerError) } } // Health returns LAPI health and cscli availability. func (h *APIHandler) Health(w http.ResponseWriter, r *http.Request) { ctx, cancel := context.WithTimeout(r.Context(), 5*time.Second) defer cancel() w.Header().Set("Content-Type", "application/json") if err := json.NewEncoder(w).Encode(healthResponse{ Healthy: h.deps.LAPI.IsHealthy(ctx), CLIAvailable: h.deps.CLIAvailable, }); err != nil { http.Error(w, "internal error", http.StatusInternalServerError) } }