package middleware import ( "context" "net/http" "os" ) // 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, "cv-length", "short"), CVIcons: getPreferenceCookie(r, "cv-icons", "show"), CVLanguage: getPreferenceCookie(r, "cv-language", "en"), CVTheme: getPreferenceCookie(r, "cv-theme", "default"), ColorTheme: getPreferenceCookie(r, "color-theme", "light"), } // Migrate old preference values (one-time auto-migration) if prefs.CVLength == "extended" { prefs.CVLength = "long" } switch prefs.CVIcons { case "true": prefs.CVIcons = "show" case "false": prefs.CVIcons = "hide" } // 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: "short", CVIcons: "show", CVLanguage: "en", CVTheme: "default", ColorTheme: "light", } } 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) == "long" } // IsShortCV returns true if the user prefers short CV format func IsShortCV(r *http.Request) bool { return GetCVLength(r) == "short" } // ShowIcons returns true if icons should be visible func ShowIcons(r *http.Request) bool { return GetCVIcons(r) == "show" } // HideIcons returns true if icons should be hidden func HideIcons(r *http.Request) bool { return GetCVIcons(r) == "hide" } // IsCleanTheme returns true if clean theme is selected func IsCleanTheme(r *http.Request) bool { return GetCVTheme(r) == "clean" } // IsDefaultTheme returns true if default theme is selected func IsDefaultTheme(r *http.Request) bool { return GetCVTheme(r) == "default" } // IsDarkMode returns true if dark mode is enabled func IsDarkMode(r *http.Request) bool { return GetColorTheme(r) == "dark" } // IsLightMode returns true if light mode is enabled func IsLightMode(r *http.Request) bool { return GetColorTheme(r) == "light" } // 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: "/", MaxAge: 365 * 24 * 60 * 60, // 1 year 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("GO_ENV") return env == "production" || 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 }