fetch_ml/internal/api/middleware.go
Jeremie Fraeys 7e5ceec069
feat(api): add groups and tokens handlers, refactor routes
Add new API endpoints and clean up handler interfaces:

- groups/handlers.go: New lab group management API
  * CRUD operations for lab groups
  * Member management with role assignment (admin/member/viewer)
  * Group listing and membership queries

- tokens/handlers.go: Token generation and validation endpoints
  * Create access tokens for public task sharing
  * Validate tokens for secure access
  * Token revocation and cleanup

- routes.go: Refactor handler registration
  * Integrate groups handler into WebSocket routes
  * Remove nil parameters from all handler constructors
  * Cleaner dependency injection pattern

- Handler interface cleanup across all modules:
  * jobs/handlers.go: Remove unused nil privacyEnforcer parameter
  * jupyter/handlers.go: Streamline initialization
  * scheduler/handlers.go: Consistent constructor signature
  * ws/handler.go: Add groups handler to dependencies
2026-03-08 12:51:25 -04:00

53 lines
1.7 KiB
Go

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)
})
}