9a848e8c53
Implement a command palette accessible via CMD+K/Ctrl+K using the ninja-keys web component. Features include: - New /api/cmd-k endpoint serving dynamic CV entries (experiences, projects, courses) - Language-aware responses with 1-hour cache headers - Scroll-to-section functionality for quick navigation - Enhanced keyboard shortcuts modal with CMD+K documentation - Comprehensive test coverage for API and UI interactions Also includes cleanup of deprecated debug test files and various UI polish improvements to contact form, themes, and action bar components.
70 lines
2.3 KiB
Go
70 lines
2.3 KiB
Go
package routes
|
|
|
|
import (
|
|
"net/http"
|
|
"time"
|
|
|
|
"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) 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
|
|
|
|
// 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(5, 1*time.Hour)
|
|
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(3, 1*time.Minute)
|
|
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("static")))
|
|
mux.Handle("/static/", middleware.CacheControl(staticHandler))
|
|
|
|
// Apply comprehensive middleware chain
|
|
// Order: Recovery → Logger → SecurityHeaders → Preferences → Mux
|
|
handler := middleware.Recovery(
|
|
middleware.Logger(
|
|
middleware.SecurityHeaders(
|
|
middleware.PreferencesMiddleware(mux),
|
|
),
|
|
),
|
|
)
|
|
|
|
return handler
|
|
}
|