Files
cv-site/internal/middleware/preferences.go
T
juanatsap 2c7f8de242 refactor: centralize constants and reorganize documentation
- Create internal/constants package with all hardcoded values
  (environment, cookies, themes, headers, routes, cache)
- Create internal/httputil package for HTTP helper functions
- Update all handlers and middleware to use centralized constants
- Reorganize documentation with numbered prefixes (00-26)
- Remove duplicate docs from validation folder and docs/
- Delete handlers/constants.go (moved to internal/constants)
2025-12-06 16:27:12 +00:00

170 lines
5.2 KiB
Go

package middleware
import (
"context"
"net/http"
"os"
"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, constants.CookieCVLength, constants.CVLengthShort),
CVIcons: getPreferenceCookie(r, constants.CookieCVIcons, constants.CVIconsShow),
CVLanguage: getPreferenceCookie(r, constants.CookieCVLanguage, constants.LangEnglish),
CVTheme: getPreferenceCookie(r, constants.CookieCVTheme, constants.CVThemeDefault),
ColorTheme: getPreferenceCookie(r, constants.CookieColorTheme, constants.ColorThemeLight),
}
// Migrate old preference values (one-time auto-migration)
if prefs.CVLength == "extended" {
prefs.CVLength = constants.CVLengthLong
}
switch prefs.CVIcons {
case "true":
prefs.CVIcons = constants.CVIconsShow
case "false":
prefs.CVIcons = constants.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: constants.CVLengthShort,
CVIcons: constants.CVIconsShow,
CVLanguage: constants.LangEnglish,
CVTheme: constants.CVThemeDefault,
ColorTheme: constants.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) == constants.CVLengthLong
}
// IsShortCV returns true if the user prefers short CV format
func IsShortCV(r *http.Request) bool {
return GetCVLength(r) == constants.CVLengthShort
}
// ShowIcons returns true if icons should be visible
func ShowIcons(r *http.Request) bool {
return GetCVIcons(r) == constants.CVIconsShow
}
// HideIcons returns true if icons should be hidden
func HideIcons(r *http.Request) bool {
return GetCVIcons(r) == constants.CVIconsHide
}
// IsCleanTheme returns true if clean theme is selected
func IsCleanTheme(r *http.Request) bool {
return GetCVTheme(r) == constants.CVThemeClean
}
// IsDefaultTheme returns true if default theme is selected
func IsDefaultTheme(r *http.Request) bool {
return GetCVTheme(r) == constants.CVThemeDefault
}
// IsDarkMode returns true if dark mode is enabled
func IsDarkMode(r *http.Request) bool {
return GetColorTheme(r) == constants.ColorThemeDark
}
// IsLightMode returns true if light mode is enabled
func IsLightMode(r *http.Request) bool {
return GetColorTheme(r) == constants.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: constants.CookiePath,
MaxAge: constants.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(constants.EnvVarGOEnv)
return env == constants.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
}