// Package server provides the HTTP server for kindexr, including the /health endpoint. package server import ( "encoding/json" "net/http" "github.com/go-chi/chi/v5" "github.com/go-chi/chi/v5/middleware" "git.utn.lol/enki/kindexr/internal/config" "git.utn.lol/enki/kindexr/internal/db" "git.utn.lol/enki/kindexr/internal/torznab" ) // Server holds the HTTP server state. type Server struct { cfg *config.Config db *db.DB router chi.Router version string connectedRelays func() int // returns live relay count; nil = always 0 } // New creates a new Server with the given config, database, and version string. func New(cfg *config.Config, database *db.DB, version string) *Server { s := &Server{ cfg: cfg, db: database, version: version, } s.router = s.buildRouter() return s } // SetConnectedRelays wires in a function that returns the current connected // relay count. Call this after the reader is started. func (s *Server) SetConnectedRelays(fn func() int) { s.connectedRelays = fn } // Handler returns the root http.Handler for the server. func (s *Server) Handler() http.Handler { return s.router } // buildRouter wires up all routes and middleware. func (s *Server) buildRouter() chi.Router { r := chi.NewRouter() r.Use(middleware.Logger) r.Use(middleware.Recoverer) r.Get("/health", s.healthHandler) tz := torznab.New(s.cfg, s.db, s.version) tz.Mount(r) return r } // healthResponse is the JSON body returned by GET /health. type healthResponse struct { Status string `json:"status"` Version string `json:"version"` RelaysConnected int `json:"relays_connected"` EventsTotal int64 `json:"events_total"` LastEventAt *int64 `json:"last_event_at"` } // healthHandler handles GET /health. func (s *Server) healthHandler(w http.ResponseWriter, r *http.Request) { stats, err := s.db.GetStats(r.Context()) if err != nil { http.Error(w, `{"error":"internal server error"}`, http.StatusInternalServerError) return } relaysConnected := 0 if s.connectedRelays != nil { relaysConnected = s.connectedRelays() } resp := healthResponse{ Status: "ok", Version: s.version, RelaysConnected: relaysConnected, EventsTotal: stats.EventsTotal, LastEventAt: stats.LastEventAt, } w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) _ = json.NewEncoder(w).Encode(resp) }