package middleware import ( "context" "net/http" "os" c "github.com/juanatsap/cv-site/internal/constants" ) // contextKey is a private type for context keys to avoid collisions type contextKey string const ( // PreferencesKey is the context key for user preferences PreferencesKey contextKey = "preferences" ) // Preferences holds user preference values from cookies type Preferences struct { CVLength string // "short" or "long" CVIcons string // "show" or "hide" CVLanguage string // "en" or "es" CVTheme string // "default" or "clean" ColorTheme string // "light" or "dark" } // PreferencesMiddleware reads user preferences from cookies and stores them in context // This eliminates the need for handlers to manually read cookies func PreferencesMiddleware(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { prefs := &Preferences{ CVLength: getPreferenceCookie(r, c.CookieCVLength, c.CVLengthShort), CVIcons: getPreferenceCookie(r, c.CookieCVIcons, c.CVIconsShow), CVLanguage: getPreferenceCookie(r, c.CookieCVLanguage, c.LangEnglish), CVTheme: getPreferenceCookie(r, c.CookieCVTheme, c.CVThemeDefault), ColorTheme: getPreferenceCookie(r, c.CookieColorTheme, c.ColorThemeLight), } // Migrate old preference values (one-time auto-migration) if prefs.CVLength == "extended" { prefs.CVLength = c.CVLengthLong } switch prefs.CVIcons { case "true": prefs.CVIcons = c.CVIconsShow case "false": prefs.CVIcons = c.CVIconsHide } // Store preferences in context ctx := context.WithValue(r.Context(), PreferencesKey, prefs) next.ServeHTTP(w, r.WithContext(ctx)) }) } // GetPreferences retrieves preferences from request context func GetPreferences(r *http.Request) *Preferences { prefs, ok := r.Context().Value(PreferencesKey).(*Preferences) if !ok { // Return default preferences if not found return &Preferences{ CVLength: c.CVLengthShort, CVIcons: c.CVIconsShow, CVLanguage: c.LangEnglish, CVTheme: c.CVThemeDefault, ColorTheme: c.ColorThemeLight, } } return prefs } // ============================================================================== // CONTEXT HELPER FUNCTIONS // Convenience functions for accessing specific preference values // ============================================================================== // GetLanguage retrieves the user's language preference func GetLanguage(r *http.Request) string { return GetPreferences(r).CVLanguage } // GetCVLength retrieves the user's CV length preference func GetCVLength(r *http.Request) string { return GetPreferences(r).CVLength } // GetCVIcons retrieves the user's icon visibility preference func GetCVIcons(r *http.Request) string { return GetPreferences(r).CVIcons } // GetCVTheme retrieves the user's CV theme preference func GetCVTheme(r *http.Request) string { return GetPreferences(r).CVTheme } // GetColorTheme retrieves the user's color theme preference func GetColorTheme(r *http.Request) string { return GetPreferences(r).ColorTheme } // IsLongCV returns true if the user prefers long CV format func IsLongCV(r *http.Request) bool { return GetCVLength(r) == c.CVLengthLong } // IsShortCV returns true if the user prefers short CV format func IsShortCV(r *http.Request) bool { return GetCVLength(r) == c.CVLengthShort } // ShowIcons returns true if icons should be visible func ShowIcons(r *http.Request) bool { return GetCVIcons(r) == c.CVIconsShow } // HideIcons returns true if icons should be hidden func HideIcons(r *http.Request) bool { return GetCVIcons(r) == c.CVIconsHide } // IsCleanTheme returns true if clean theme is selected func IsCleanTheme(r *http.Request) bool { return GetCVTheme(r) == c.CVThemeClean } // IsDefaultTheme returns true if default theme is selected func IsDefaultTheme(r *http.Request) bool { return GetCVTheme(r) == c.CVThemeDefault } // IsDarkMode returns true if dark mode is enabled func IsDarkMode(r *http.Request) bool { return GetColorTheme(r) == c.ColorThemeDark } // IsLightMode returns true if light mode is enabled func IsLightMode(r *http.Request) bool { return GetColorTheme(r) == c.ColorThemeLight } // SetPreferenceCookie sets a preference cookie (1 year expiry) func SetPreferenceCookie(w http.ResponseWriter, name string, value string) { http.SetCookie(w, &http.Cookie{ Name: name, Value: value, Path: c.CookiePath, MaxAge: c.CookieMaxAge, HttpOnly: true, SameSite: http.SameSiteStrictMode, Secure: isProductionMode(), // Secure in production with HTTPS }) } // isProductionMode checks if the application is running in production func isProductionMode() bool { env := os.Getenv(c.EnvVarGOEnv) return env == c.EnvProduction || env == "prod" } // getPreferenceCookie gets a preference cookie value, returns default if not found func getPreferenceCookie(r *http.Request, name string, defaultValue string) string { cookie, err := r.Cookie(name) if err != nil { return defaultValue } return cookie.Value }