package api import ( "net/http" "strings" "time" "github.com/jfraeys/fetch_ml/internal/auth" "github.com/jfraeys/fetch_ml/internal/middleware" ) // wrapWithMiddleware wraps the handler with security middleware func (s *Server) wrapWithMiddleware(mux *http.ServeMux) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { // Skip auth for WebSocket and health endpoints if r.URL.Path == "/ws" || strings.HasPrefix(r.URL.Path, "/health") { mux.ServeHTTP(w, r) return } handler := s.sec.APIKeyAuth(mux) handler = s.provisionUserMiddleware(handler) handler = s.sec.RateLimit(handler) handler = middleware.SecurityHeaders(handler) handler = middleware.CORS(s.config.Security.AllowedOrigins)(handler) handler = middleware.RequestTimeout(30 * time.Second)(handler) handler = middleware.AuditLogger(handler) if len(s.config.Security.IPWhitelist) > 0 { handler = s.sec.IPWhitelist(s.config.Security.IPWhitelist)(handler) } // Add OpenAPI validation if available if s.validationMiddleware != nil { handler = s.validationMiddleware.ValidateRequest(handler) } handler.ServeHTTP(w, r) }) } // provisionUserMiddleware provisions new users on first login func (s *Server) provisionUserMiddleware(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { // Only provision if database is available if s.db != nil { if user := auth.GetUserFromContext(r.Context()); user != nil { if err := s.db.ProvisionUserOnFirstLogin(user.Name); err != nil { // Log error but don't fail the request - provisioning is best-effort s.logger.Error("failed to provision user on first login", "user", user.Name, "error", err) } } } next.ServeHTTP(w, r) }) }