2025-11-12 17:53:24 +00:00
|
|
|
package routes
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"net/http"
|
|
|
|
|
|
2026-04-08 00:20:48 +01:00
|
|
|
"github.com/juanatsap/cv-site/internal/chat"
|
2025-12-06 17:05:17 +00:00
|
|
|
c "github.com/juanatsap/cv-site/internal/constants"
|
2025-11-12 17:53:24 +00:00
|
|
|
"github.com/juanatsap/cv-site/internal/handlers"
|
|
|
|
|
"github.com/juanatsap/cv-site/internal/middleware"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// Setup configures all application routes and middleware
|
2026-04-08 00:20:48 +01:00
|
|
|
func Setup(cvHandler *handlers.CVHandler, healthHandler *handlers.HealthHandler, chatHandler *chat.Handler) http.Handler {
|
2025-11-12 17:53:24 +00:00
|
|
|
mux := http.NewServeMux()
|
|
|
|
|
|
2025-11-20 12:14:53 +00:00
|
|
|
// 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)
|
|
|
|
|
|
2025-12-01 13:03:06 +00:00
|
|
|
// API routes (must be before "/" to avoid catch-all)
|
2026-04-08 14:47:14 +01:00
|
|
|
mux.HandleFunc("/api/cmd-k", cvHandler.CmdKData) // CMD+K command palette data
|
|
|
|
|
|
|
|
|
|
// Chat endpoint with rate limiting (30 requests/hour per IP)
|
|
|
|
|
chatRateLimiter := middleware.NewRateLimiter(c.RateLimitChatRequests, c.RateLimitChatWindow)
|
|
|
|
|
mux.Handle("/api/chat", chatRateLimiter.Middleware(http.HandlerFunc(chatHandler.HandleChat)))
|
2026-04-09 10:54:23 +01:00
|
|
|
mux.HandleFunc("/api/chat/warmup", chatHandler.HandleWarmup) // Pre-load model on chat open
|
|
|
|
|
mux.HandleFunc("/api/chat/status", chatHandler.HandleStatus) // Model readiness check
|
2025-12-01 13:03:06 +00:00
|
|
|
|
2025-11-12 17:53:24 +00:00
|
|
|
// Public routes
|
|
|
|
|
mux.HandleFunc("/", cvHandler.Home)
|
|
|
|
|
mux.HandleFunc("/cv", cvHandler.CVContent)
|
2025-12-01 13:03:06 +00:00
|
|
|
mux.HandleFunc("/text", cvHandler.PlainText) // Plain text version for curl/AI
|
2025-11-12 17:53:24 +00:00
|
|
|
mux.HandleFunc("/health", healthHandler.Check)
|
|
|
|
|
|
2025-11-12 18:55:06 +00:00
|
|
|
// HTMX endpoints for interactive controls
|
2025-11-14 21:38:09 +00:00
|
|
|
mux.HandleFunc("/switch-language", cvHandler.SwitchLanguage)
|
2025-11-12 18:55:06 +00:00
|
|
|
mux.HandleFunc("/toggle/length", cvHandler.ToggleLength)
|
2025-11-15 18:42:35 +00:00
|
|
|
mux.HandleFunc("/toggle/icons", cvHandler.ToggleIcons)
|
2025-11-12 18:55:06 +00:00
|
|
|
mux.HandleFunc("/toggle/theme", cvHandler.ToggleTheme)
|
|
|
|
|
|
2025-11-30 14:31:58 +00:00
|
|
|
// Contact form endpoint with full security chain:
|
|
|
|
|
// BrowserOnly → RateLimiter → Handler
|
|
|
|
|
// This blocks curl/Postman, enforces rate limits, then processes the request
|
2025-12-06 17:05:17 +00:00
|
|
|
contactRateLimiter := middleware.NewRateLimiter(c.RateLimitContactRequests, c.RateLimitContactWindow)
|
2025-11-30 14:31:58 +00:00
|
|
|
protectedContactHandler := middleware.BrowserOnly(
|
|
|
|
|
contactRateLimiter.Middleware(
|
|
|
|
|
http.HandlerFunc(cvHandler.HandleContact),
|
|
|
|
|
),
|
2025-11-30 13:47:49 +00:00
|
|
|
)
|
|
|
|
|
mux.Handle("/api/contact", protectedContactHandler)
|
|
|
|
|
|
2025-11-12 17:53:24 +00:00
|
|
|
// Protected PDF endpoint with rate limiting (3 requests/minute per IP)
|
2025-12-06 17:05:17 +00:00
|
|
|
pdfRateLimiter := middleware.NewRateLimiter(c.RateLimitPDFRequests, c.RateLimitPDFWindow)
|
2025-11-12 17:53:24 +00:00
|
|
|
protectedPDFHandler := middleware.OriginChecker(
|
|
|
|
|
pdfRateLimiter.Middleware(
|
|
|
|
|
http.HandlerFunc(cvHandler.ExportPDF),
|
|
|
|
|
),
|
|
|
|
|
)
|
|
|
|
|
mux.Handle("/export/pdf", protectedPDFHandler)
|
|
|
|
|
|
|
|
|
|
// Static files with cache control
|
2025-12-06 17:05:17 +00:00
|
|
|
staticHandler := http.StripPrefix("/static/", http.FileServer(http.Dir(c.DirStatic)))
|
2025-11-12 17:53:24 +00:00
|
|
|
mux.Handle("/static/", middleware.CacheControl(staticHandler))
|
|
|
|
|
|
|
|
|
|
// Apply comprehensive middleware chain
|
2025-12-02 10:46:53 +00:00
|
|
|
// Order: Recovery → Logger → SecurityHeaders → DynamicCacheControl → Preferences → Mux
|
2025-11-12 17:53:24 +00:00
|
|
|
handler := middleware.Recovery(
|
|
|
|
|
middleware.Logger(
|
2025-11-20 17:56:47 +00:00
|
|
|
middleware.SecurityHeaders(
|
2025-12-02 10:46:53 +00:00
|
|
|
middleware.DynamicCacheControl(
|
|
|
|
|
middleware.PreferencesMiddleware(mux),
|
|
|
|
|
),
|
2025-11-20 17:56:47 +00:00
|
|
|
),
|
2025-11-12 17:53:24 +00:00
|
|
|
),
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
return handler
|
|
|
|
|
}
|