package routes import ( "net/http" "github.com/juanatsap/cv-site/internal/chat" c "github.com/juanatsap/cv-site/internal/constants" "github.com/juanatsap/cv-site/internal/handlers" "github.com/juanatsap/cv-site/internal/middleware" ) // Setup configures all application routes and middleware func Setup(cvHandler *handlers.CVHandler, healthHandler *handlers.HealthHandler, chatHandler *chat.Handler) http.Handler { mux := http.NewServeMux() // Shortcut routes for default CV (year-aware) - MUST be before "/" route // Pattern: /cv-jamr-{year}-{lang}.pdf (e.g., /cv-jamr-2025-en.pdf) mux.HandleFunc("/cv-jamr-", cvHandler.DefaultCVShortcut) // API routes (must be before "/" to avoid catch-all) mux.HandleFunc("/api/cmd-k", cvHandler.CmdKData) // CMD+K command palette data mux.HandleFunc("/api/chat", chatHandler.HandleChat) // AI chat endpoint // Public routes mux.HandleFunc("/", cvHandler.Home) mux.HandleFunc("/cv", cvHandler.CVContent) mux.HandleFunc("/text", cvHandler.PlainText) // Plain text version for curl/AI mux.HandleFunc("/health", healthHandler.Check) // HTMX endpoints for interactive controls mux.HandleFunc("/switch-language", cvHandler.SwitchLanguage) mux.HandleFunc("/toggle/length", cvHandler.ToggleLength) mux.HandleFunc("/toggle/icons", cvHandler.ToggleIcons) mux.HandleFunc("/toggle/theme", cvHandler.ToggleTheme) // Contact form endpoint with full security chain: // BrowserOnly → RateLimiter → Handler // This blocks curl/Postman, enforces rate limits, then processes the request contactRateLimiter := middleware.NewRateLimiter(c.RateLimitContactRequests, c.RateLimitContactWindow) protectedContactHandler := middleware.BrowserOnly( contactRateLimiter.Middleware( http.HandlerFunc(cvHandler.HandleContact), ), ) mux.Handle("/api/contact", protectedContactHandler) // Protected PDF endpoint with rate limiting (3 requests/minute per IP) pdfRateLimiter := middleware.NewRateLimiter(c.RateLimitPDFRequests, c.RateLimitPDFWindow) protectedPDFHandler := middleware.OriginChecker( pdfRateLimiter.Middleware( http.HandlerFunc(cvHandler.ExportPDF), ), ) mux.Handle("/export/pdf", protectedPDFHandler) // Static files with cache control staticHandler := http.StripPrefix("/static/", http.FileServer(http.Dir(c.DirStatic))) mux.Handle("/static/", middleware.CacheControl(staticHandler)) // Apply comprehensive middleware chain // Order: Recovery → Logger → SecurityHeaders → DynamicCacheControl → Preferences → Mux handler := middleware.Recovery( middleware.Logger( middleware.SecurityHeaders( middleware.DynamicCacheControl( middleware.PreferencesMiddleware(mux), ), ), ), ) return handler }