package httpapi import ( "context" "encoding/json" "log/slog" "net/http" "time" "drive.local/drivev2/internal/config" ) var ErrServerClosed = http.ErrServerClosed type Server struct { httpServer *http.Server } func NewServer(cfg config.Config, version string, logger *slog.Logger, frontend http.Handler) *Server { mux := http.NewServeMux() mux.HandleFunc("GET /health/live", jsonResponse(http.StatusOK, map[string]string{"status": "live"})) mux.HandleFunc("GET /health/ready", jsonResponse(http.StatusOK, map[string]string{"status": "ready"})) mux.HandleFunc("GET /api/v1/setup/status", jsonResponse(http.StatusOK, map[string]any{ "initialized": false, "phase": "foundation", "version": version, })) mux.Handle("/", frontend) return &Server{httpServer: &http.Server{ Addr: cfg.HTTPAddress, Handler: requestLogger(logger, mux), ReadHeaderTimeout: 10 * time.Second, IdleTimeout: 2 * time.Minute, }} } func (s *Server) ListenAndServe() error { return s.httpServer.ListenAndServe() } func (s *Server) Shutdown(ctx context.Context) error { return s.httpServer.Shutdown(ctx) } func jsonResponse(status int, payload any) http.HandlerFunc { return func(w http.ResponseWriter, _ *http.Request) { w.Header().Set("Content-Type", "application/json") w.Header().Set("Cache-Control", "no-store") w.WriteHeader(status) if err := json.NewEncoder(w).Encode(payload); err != nil { slog.Error("encode response", "error", err) } } } func requestLogger(logger *slog.Logger, next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { started := time.Now() next.ServeHTTP(w, r) logger.Info("http request", "method", r.Method, "path", r.URL.Path, "duration_ms", time.Since(started).Milliseconds()) }) }