package main import ( "html/template" "net/http" "strings" "time" ) type UserSettings struct { Theme string Language string SafeSearch string IsThemeDark bool } func loadUserSettings(w http.ResponseWriter, r *http.Request) UserSettings { var settings UserSettings saveRequired := false // Track if we need to save settings back // Load theme if cookie, err := r.Cookie("theme"); err == nil { settings.Theme = cookie.Value } else { settings.Theme = "dark" // Default theme saveRequired = true // No cookie found, need to save this later } // Load language if cookie, err := r.Cookie("language"); err == nil { settings.Language = cookie.Value } else { settings.Language = "" // Set language to empty, handled later } // If language is empty, get it from the Accept-Language header if settings.Language == "" { acceptLang := r.Header.Get("Accept-Language") if acceptLang != "" { // Get the first language from Accept-Language header and normalize settings.Language = normalizeLangCode(strings.Split(acceptLang, ",")[0]) } else { settings.Language = "en" // Default language if Accept-Language is not present } saveRequired = true // No language cookie found, need to save } // Load safe search if cookie, err := r.Cookie("safe"); err == nil { settings.SafeSearch = cookie.Value } else { settings.SafeSearch = "" // Default safe search off saveRequired = true // No safe search cookie found, need to save } // Save settings if required (no cookie found for any of the settings) if saveRequired { saveUserSettings(w, settings) } return settings } func saveUserSettings(w http.ResponseWriter, settings UserSettings) { expiration := time.Now().Add(90 * 24 * time.Hour) // 90 days from now http.SetCookie(w, &http.Cookie{ Name: "theme", Value: settings.Theme, Path: "/", Expires: expiration, // Expiration time needs to be set otherwise it will expire immediately Secure: true, // Ensure cookie is sent over HTTPS only SameSite: http.SameSiteStrictMode, }) http.SetCookie(w, &http.Cookie{ Name: "language", Value: settings.Language, Path: "/", Expires: expiration, Secure: true, SameSite: http.SameSiteStrictMode, }) http.SetCookie(w, &http.Cookie{ Name: "safe", Value: settings.SafeSearch, Path: "/", Expires: expiration, Secure: true, SameSite: http.SameSiteStrictMode, }) printDebug("settings saved: %v", settings) } func handleSaveSettings(w http.ResponseWriter, r *http.Request) { if r.Method == "POST" { // Load current settings settings := loadUserSettings(w, r) // Update only the settings that were submitted in the form if theme := r.FormValue("theme"); theme != "" { settings.Theme = theme } if lang := r.FormValue("lang"); lang != "" { settings.Language = lang } else { // If lang is empty, try to get from Accept-Language header acceptLang := r.Header.Get("Accept-Language") if acceptLang != "" { settings.Language = strings.Split(acceptLang, ",")[0] } } if safe := r.FormValue("safe"); safe != "" { settings.SafeSearch = safe } // Save the updated settings saveUserSettings(w, settings) // Redirect back to the previous page or settings page http.Redirect(w, r, r.FormValue("past"), http.StatusSeeOther) } } func handleSettings(w http.ResponseWriter, r *http.Request) { // Load user settings settings = loadUserSettings(w, r) data := struct { LanguageOptions []LanguageOption CurrentLang string Theme string Safe string IsThemeDark bool }{ LanguageOptions: languageOptions, CurrentLang: settings.Language, Theme: settings.Theme, Safe: settings.SafeSearch, IsThemeDark: settings.IsThemeDark, } printDebug("Rendering settings with data: %+v", data) tmpl, err := template.ParseFiles("templates/settings.html") if err != nil { printErr("Error parsing template: %s", err) http.Error(w, "Internal Server Error", 500) return } err = tmpl.Execute(w, data) if err != nil { printErr("Error executing template: %s", err) http.Error(w, "Internal Server Error", 500) return } } // Helper function to normalize language codes func normalizeLangCode(lang string) string { lang = strings.ToLower(lang) // First, check if the language code is already valid if isValidLangCode(lang) { return lang } // Strip regional codes (e.g., en-US -> en) if strings.Contains(lang, "-") { lang = strings.Split(lang, "-")[0] } // Re-check if the normalized version is valid if isValidLangCode(lang) { return lang } // If the language is not recognized, default to "en" return "en" } // Helper function to check if a language code exists in the language options func isValidLangCode(lang string) bool { for _, opt := range languageOptions { if opt.Code == lang { return true } } return false }