Search/user-settings.go
partisan 0851e9e9f2
Some checks failed
Run Integration Tests / test (push) Failing after 40s
added secure cookies settings
2025-01-06 18:52:43 +01:00

255 lines
6.7 KiB
Go
Executable file

package main
import (
"net/http"
"strings"
"time"
)
type UserSettings struct {
Theme string
SiteLanguage string
SearchLanguage string
SafeSearch string
IsThemeDark bool
}
func loadUserSettings(w http.ResponseWriter, r *http.Request) UserSettings {
var settings UserSettings
saveRequired := false
for _, cd := range AllCookies {
// Attempt to read the cookie
if cookie, err := r.Cookie(cd.Name); err == nil {
// Use SetValue to update the correct UserSettings field
cd.SetValue(&settings, cookie.Value)
} else {
// If cookie is missing and you want a default value, set it here
switch cd.Name {
case "theme":
// Default theme to "dark" if missing
cd.SetValue(&settings, "dark")
saveRequired = true
case "site_language":
// Fallback to Accept-Language or "en"
acceptLang := r.Header.Get("Accept-Language")
if acceptLang != "" {
cd.SetValue(&settings, normalizeLangCode(acceptLang))
} else {
cd.SetValue(&settings, "en")
}
saveRequired = true
case "safe":
// Default safe to ""
cd.SetValue(&settings, "")
saveRequired = true
// etc. for other cookies if needed
}
}
}
// If theme was set, update IsThemeDark just to be sure
// Alternatively do it inside SetValue for "theme"
settings.IsThemeDark = settings.Theme == "dark" ||
settings.Theme == "night" ||
settings.Theme == "black" ||
settings.Theme == "latte"
// Save any new default cookies that might have been triggered
if saveRequired {
saveUserSettings(w, settings)
}
return settings
}
func saveUserSettings(w http.ResponseWriter, settings UserSettings) {
expiration := time.Now().Add(90 * 24 * time.Hour)
for _, cd := range AllCookies {
http.SetCookie(w, &http.Cookie{
Name: cd.Name,
Value: cd.GetValue(settings),
Path: "/",
Expires: expiration,
Secure: true, // Ensure HTTPS is required
HttpOnly: true,
SameSite: http.SameSiteStrictMode, // Restrict cross-site usage
})
}
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
}
// Update site language if provided
if siteLang := r.FormValue("site_lang"); siteLang != "" {
settings.SiteLanguage = siteLang
} else {
// If site_lang is empty, try to get from Accept-Language header
acceptLang := r.Header.Get("Accept-Language")
if acceptLang != "" {
settings.SiteLanguage = strings.Split(acceptLang, ",")[0]
}
}
// Update search language if provided
if searchLang := r.FormValue("search_lang"); searchLang != "" {
settings.SearchLanguage = searchLang
}
// Update safe search if provided
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)
// Prepare data with user settings and icon paths as a map
data := map[string]interface{}{
"LanguageOptions": languageOptions,
"CurrentSiteLang": settings.SiteLanguage,
"CurrentSearchLang": settings.SearchLanguage,
"Theme": settings.Theme,
"Safe": settings.SafeSearch,
"IsThemeDark": settings.IsThemeDark,
}
printDebug("Rendering settings with data: %+v", data)
// Use renderTemplate to include the icons
renderTemplate(w, "settings.html", data)
}
// 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
}
// CookieDefinition describes how a single cookie is handled
type CookieDefinition struct {
Name string
// GetValue extracts the corresponding field from UserSettings
GetValue func(UserSettings) string
// SetValue updates the corresponding field in UserSettings
SetValue func(*UserSettings, string)
// Description used in privacy table or docs
Description string
}
// AllCookies defines every cookie we handle in a single slice.
// Add or remove entries here, and the rest updates automatically.
var AllCookies = []CookieDefinition{
{
Name: "theme",
Description: "Stores the selected theme (dark, light, etc.)",
GetValue: func(s UserSettings) string {
return s.Theme
},
SetValue: func(s *UserSettings, val string) {
s.Theme = val
s.IsThemeDark = (val == "dark" || val == "night" || val == "black" || val == "latte")
},
},
{
Name: "site_language",
Description: "Stores the preferred site language.",
GetValue: func(s UserSettings) string {
return s.SiteLanguage
},
SetValue: func(s *UserSettings, val string) {
s.SiteLanguage = val
},
},
{
Name: "search_language",
Description: "Stores the preferred language for search results.",
GetValue: func(s UserSettings) string {
return s.SearchLanguage
},
SetValue: func(s *UserSettings, val string) {
s.SearchLanguage = val
},
},
{
Name: "safe",
Description: "Stores the Safe Search setting.",
GetValue: func(s UserSettings) string {
return s.SafeSearch
},
SetValue: func(s *UserSettings, val string) {
s.SafeSearch = val
},
},
}
type CookieRow struct {
Name string
Value string
Description string
Expiration string
}
func generateCookieTable(r *http.Request) []CookieRow {
var rows []CookieRow
for _, cd := range AllCookies {
value := "[Not Set]"
if cookie, err := r.Cookie(cd.Name); err == nil {
value = cookie.Value
}
rows = append(rows, CookieRow{
Name: cd.Name,
Value: value,
Description: cd.Description,
Expiration: "90 days",
})
}
return rows
}