refactor: Integrate PreferencesMiddleware and update handlers

Complete middleware integration with comprehensive testing:

1. Middleware Integration
   - Added PreferencesMiddleware to middleware chain in routes
   - Order: Recovery → Logger → SecurityHeaders → Preferences → Mux
   - Reads all preference cookies once per request
   - Stores in context for handlers to access

2. Handler Updates
   - cv_pages.go: Home handler uses middleware.GetPreferences()
   - cv_htmx.go: All toggle handlers use middleware preferences
   - Eliminated manual cookie reading in handlers
   - Migration logic handled entirely by middleware

3. Comprehensive Middleware Tests
   - Created preferences_test.go with 10+ test functions
   - Tests: default values, migrations, cookie setting, context access
   - Verified: extended→long, true→show, false→hide migrations
   - All tests passing

Benefits:
- Performance: Cookies read once per request (not multiple times)
- Consistency: All handlers get same preference values
- Maintainability: Migration logic centralized in middleware
- Testability: Easy to mock preferences via context

Testing:
- All unit tests pass (handlers + middleware)
- Build succeeds
- No breaking changes
This commit is contained in:
juanatsap
2025-11-20 17:56:47 +00:00
parent 399ddded6c
commit ae89d84e07
6 changed files with 515 additions and 56 deletions
+26 -36
View File
@@ -2,6 +2,8 @@ package handlers
import (
"net/http"
"github.com/juanatsap/cv-site/internal/middleware"
)
// ==============================================================================
@@ -17,13 +19,9 @@ func (h *CVHandler) ToggleLength(w http.ResponseWriter, r *http.Request) {
return
}
// Get current state
currentLength := getPreferenceCookie(r, "cv-length", "short")
// Migrate old value if needed
if currentLength == "extended" {
currentLength = "long"
}
// Get current preferences from context (set by middleware, already migrated)
prefs := middleware.GetPreferences(r)
currentLength := prefs.CVLength
// Toggle state
newLength := "long"
@@ -32,12 +30,12 @@ func (h *CVHandler) ToggleLength(w http.ResponseWriter, r *http.Request) {
}
// Save new state
setPreferenceCookie(w, "cv-length", newLength)
middleware.SetPreferenceCookie(w, "cv-length", newLength)
// Get language
// Get language from query or use current preference
lang := r.URL.Query().Get("lang")
if lang == "" {
lang = getPreferenceCookie(r, "cv-language", "en")
lang = prefs.CVLanguage
}
// Prepare template data with length state
@@ -72,16 +70,9 @@ func (h *CVHandler) ToggleIcons(w http.ResponseWriter, r *http.Request) {
return
}
// Get current state
currentIcons := getPreferenceCookie(r, "cv-icons", "show")
// Migrate old values if needed
switch currentIcons {
case "true":
currentIcons = "show"
case "false":
currentIcons = "hide"
}
// Get current preferences from context (set by middleware, already migrated)
prefs := middleware.GetPreferences(r)
currentIcons := prefs.CVIcons
// Toggle state
newIcons := "hide"
@@ -90,12 +81,12 @@ func (h *CVHandler) ToggleIcons(w http.ResponseWriter, r *http.Request) {
}
// Save new state
setPreferenceCookie(w, "cv-icons", newIcons)
middleware.SetPreferenceCookie(w, "cv-icons", newIcons)
// Get language
// Get language from query or use current preference
lang := r.URL.Query().Get("lang")
if lang == "" {
lang = getPreferenceCookie(r, "cv-language", "en")
lang = prefs.CVLanguage
}
// Prepare template data with logo state
@@ -135,7 +126,7 @@ func (h *CVHandler) SwitchLanguage(w http.ResponseWriter, r *http.Request) {
}
// Save language preference
setPreferenceCookie(w, "cv-language", lang)
middleware.SetPreferenceCookie(w, "cv-language", lang)
// Prepare template data
data, err := h.prepareTemplateData(lang)
@@ -144,19 +135,17 @@ func (h *CVHandler) SwitchLanguage(w http.ResponseWriter, r *http.Request) {
return
}
// Preserve current length and logo preferences
cvLength := getPreferenceCookie(r, "cv-length", "short")
cvIcons := getPreferenceCookie(r, "cv-icons", "show")
cvTheme := getPreferenceCookie(r, "cv-theme", "default")
// Get current preferences from context (set by middleware)
prefs := middleware.GetPreferences(r)
// Add preferences to data
if cvLength == "long" {
if prefs.CVLength == "long" {
data["CVLengthClass"] = "cv-long"
} else {
data["CVLengthClass"] = "cv-short"
}
data["ShowIcons"] = (cvIcons == "show")
data["ThemeClean"] = (cvTheme == "clean")
data["ShowIcons"] = (prefs.CVIcons == "show")
data["ThemeClean"] = (prefs.CVTheme == "clean")
// Render language-switch template with out-of-band swaps
tmpl, err := h.templates.Render("language-switch.html")
@@ -179,8 +168,9 @@ func (h *CVHandler) ToggleTheme(w http.ResponseWriter, r *http.Request) {
return
}
// Get current state
currentTheme := getPreferenceCookie(r, "cv-theme", "default")
// Get current preferences from context (set by middleware)
prefs := middleware.GetPreferences(r)
currentTheme := prefs.CVTheme
// Toggle state
newTheme := "clean"
@@ -189,12 +179,12 @@ func (h *CVHandler) ToggleTheme(w http.ResponseWriter, r *http.Request) {
}
// Save new state
setPreferenceCookie(w, "cv-theme", newTheme)
middleware.SetPreferenceCookie(w, "cv-theme", newTheme)
// Get language
// Get language from query or use current preference
lang := r.URL.Query().Get("lang")
if lang == "" {
lang = getPreferenceCookie(r, "cv-language", "en")
lang = prefs.CVLanguage
}
// Prepare template data with theme state
+7 -19
View File
@@ -8,6 +8,7 @@ import (
"strings"
"time"
"github.com/juanatsap/cv-site/internal/middleware"
"github.com/juanatsap/cv-site/internal/pdf"
)
@@ -43,26 +44,13 @@ func (h *CVHandler) Home(w http.ResponseWriter, r *http.Request) {
return
}
// Get user preferences from context (set by middleware)
// Note: Middleware should be enabled in routes for this to work
// For now, fall back to direct cookie reading for backward compatibility
cvLength := getPreferenceCookie(r, "cv-length", "short")
cvIcons := getPreferenceCookie(r, "cv-icons", "show")
cvTheme := getPreferenceCookie(r, "cv-theme", "default")
// Get user preferences from context (set by PreferencesMiddleware)
prefs := middleware.GetPreferences(r)
// Migrate old preference values to new ones (one-time auto-migration)
if cvLength == "extended" {
cvLength = "long"
setPreferenceCookie(w, "cv-length", "long")
}
switch cvIcons {
case "true":
cvIcons = "show"
setPreferenceCookie(w, "cv-icons", "show")
case "false":
cvIcons = "hide"
setPreferenceCookie(w, "cv-icons", "hide")
}
// Use preferences from context (already migrated by middleware)
cvLength := prefs.CVLength
cvIcons := prefs.CVIcons
cvTheme := prefs.CVTheme
// Add preference-specific fields to template data
cvLengthClass := "cv-short"