package server import ( "context" "encoding/json" "log/slog" "net/http" "time" "git.daviestechlabs.io/daviestechlabs/ntfy-discord/internal/bridge" "github.com/prometheus/client_golang/prometheus/promhttp" ) // Server provides health and metrics endpoints type Server struct { httpServer *http.Server bridge *bridge.Bridge } // New creates a new HTTP server func New(port string, b *bridge.Bridge) *Server { mux := http.NewServeMux() s := &Server{ bridge: b, httpServer: &http.Server{ Addr: ":" + port, Handler: mux, ReadTimeout: 5 * time.Second, WriteTimeout: 10 * time.Second, }, } mux.HandleFunc("/health", s.handleHealth) mux.HandleFunc("/ready", s.handleReady) mux.Handle("/metrics", promhttp.Handler()) return s } // Start begins serving HTTP requests func (s *Server) Start() { slog.Info("starting HTTP server", "addr", s.httpServer.Addr) if err := s.httpServer.ListenAndServe(); err != http.ErrServerClosed { slog.Error("HTTP server error", "error", err) } } // Shutdown gracefully stops the server func (s *Server) Shutdown(ctx context.Context) { ctx, cancel := context.WithTimeout(ctx, 5*time.Second) defer cancel() if err := s.httpServer.Shutdown(ctx); err != nil { slog.Error("HTTP server shutdown error", "error", err) } } func (s *Server) handleHealth(w http.ResponseWriter, r *http.Request) { status := struct { Status string `json:"status"` Healthy bool `json:"healthy"` }{ Status: "ok", Healthy: s.bridge.IsHealthy(), } if !status.Healthy { status.Status = "unhealthy" w.WriteHeader(http.StatusServiceUnavailable) } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(status) } func (s *Server) handleReady(w http.ResponseWriter, r *http.Request) { status := struct { Status string `json:"status"` Ready bool `json:"ready"` }{ Status: "ok", Ready: s.bridge.IsReady(), } if !status.Ready { status.Status = "not ready" w.WriteHeader(http.StatusServiceUnavailable) } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(status) }