added privacy policy page and about section, improved dir check, fixed crash when idexer is disabled
This commit is contained in:
parent
61266c461a
commit
5ae97da6d0
18 changed files with 698 additions and 107 deletions
|
@ -14,8 +14,7 @@ var visitedStore *VisitedStore
|
||||||
|
|
||||||
// webCrawlerInit is called during init on program start
|
// webCrawlerInit is called during init on program start
|
||||||
func webCrawlerInit() {
|
func webCrawlerInit() {
|
||||||
// Initialize the store with, say, batchSize=50
|
store, err := NewVisitedStore(filepath.Join(config.DriveCache.Path, "visited-urls.txt"), config.IndexBatchSize)
|
||||||
store, err := NewVisitedStore(filepath.Join(config.DriveCache.Path, "visited-urls.txt"), 50)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
printErr("Failed to initialize visited store: %v", err)
|
printErr("Failed to initialize visited store: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -170,7 +169,7 @@ func crawlDomainsToFile(domains [][2]string, maxPages int) error {
|
||||||
userAgent, _ := GetUserAgent("crawler-chrome")
|
userAgent, _ := GetUserAgent("crawler-chrome")
|
||||||
title, desc, keywords := fetchPageMetadataChrome(fullURL, userAgent)
|
title, desc, keywords := fetchPageMetadataChrome(fullURL, userAgent)
|
||||||
if title == "" || desc == "" {
|
if title == "" || desc == "" {
|
||||||
printWarn("Skipping %s: unable to get title/desc data", fullURL)
|
printDebug("Skipping %s: unable to get title/desc data", fullURL) // Here is print for all domains that fail to be crawled
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
26
indexer.go
26
indexer.go
|
@ -9,7 +9,6 @@ import (
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/blevesearch/bleve/v2"
|
"github.com/blevesearch/bleve/v2"
|
||||||
"golang.org/x/net/publicsuffix"
|
"golang.org/x/net/publicsuffix"
|
||||||
|
@ -73,17 +72,17 @@ func indexDocImmediately(link, title, tags, desc, rank string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// StartBatchIndexing spawns a goroutine that flushes the buffer every interval.
|
// // StartBatchIndexing spawns a goroutine that flushes the buffer every interval.
|
||||||
func StartBatchIndexing() {
|
// func StartBatchIndexing() {
|
||||||
go func() {
|
// go func() {
|
||||||
ticker := time.NewTicker(config.IndexRefreshInterval)
|
// ticker := time.NewTicker(config.IndexRefreshInterval)
|
||||||
defer ticker.Stop()
|
// defer ticker.Stop()
|
||||||
|
|
||||||
for range ticker.C {
|
// for range ticker.C {
|
||||||
flushDocBuffer()
|
// flushDocBuffer()
|
||||||
}
|
// }
|
||||||
}()
|
// }()
|
||||||
}
|
// }
|
||||||
|
|
||||||
func flushDocBuffer() {
|
func flushDocBuffer() {
|
||||||
docBufferMu.Lock()
|
docBufferMu.Lock()
|
||||||
|
@ -264,6 +263,11 @@ func IndexFile(filePath string) error {
|
||||||
|
|
||||||
// SearchIndex performs a full-text search on the indexed data.
|
// SearchIndex performs a full-text search on the indexed data.
|
||||||
func SearchIndex(queryStr string, page, pageSize int) ([]Document, error) {
|
func SearchIndex(queryStr string, page, pageSize int) ([]Document, error) {
|
||||||
|
// Check if the indexer is enabled
|
||||||
|
if !config.IndexerEnabled {
|
||||||
|
return nil, fmt.Errorf("indexer is disabled")
|
||||||
|
}
|
||||||
|
|
||||||
exactMatch := bleve.NewMatchQuery(queryStr) // Exact match
|
exactMatch := bleve.NewMatchQuery(queryStr) // Exact match
|
||||||
fuzzyMatch := bleve.NewFuzzyQuery(queryStr) // Fuzzy match
|
fuzzyMatch := bleve.NewFuzzyQuery(queryStr) // Fuzzy match
|
||||||
fuzzyMatch.Fuzziness = 2
|
fuzzyMatch.Fuzziness = 2
|
||||||
|
|
18
init.go
18
init.go
|
@ -3,6 +3,7 @@ package main
|
||||||
import (
|
import (
|
||||||
"flag"
|
"flag"
|
||||||
"os"
|
"os"
|
||||||
|
"path/filepath"
|
||||||
)
|
)
|
||||||
|
|
||||||
var config Config
|
var config Config
|
||||||
|
@ -77,9 +78,18 @@ func main() {
|
||||||
|
|
||||||
// Check if the cache directory exists when caching is enabled
|
// Check if the cache directory exists when caching is enabled
|
||||||
if config.DriveCacheEnabled {
|
if config.DriveCacheEnabled {
|
||||||
if _, err := os.Stat(config.DriveCache.Path); os.IsNotExist(err) {
|
cacheDir := config.DriveCache.Path
|
||||||
printErr("Error: Drive cache is enabled, but cache directory '%s' does not exist.\n", config.DriveCache.Path)
|
imagesDir := filepath.Join(cacheDir, "images")
|
||||||
os.Exit(1) // Exit with a non-zero status to indicate an error
|
|
||||||
|
// Check if the directory already exists
|
||||||
|
if _, err := os.Stat(imagesDir); os.IsNotExist(err) {
|
||||||
|
// Try to create the directory since it doesn't exist
|
||||||
|
if err := os.MkdirAll(imagesDir, os.ModePerm); err != nil {
|
||||||
|
printErr("Error: Failed to create cache or images directory '%s': %v", imagesDir, err)
|
||||||
|
os.Exit(1) // Exit with a non-zero status to indicate an error
|
||||||
|
}
|
||||||
|
// Print a warning if the directory had to be created
|
||||||
|
printWarn("Warning: Created missing directory '%s'.", imagesDir)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -109,7 +119,7 @@ func main() {
|
||||||
|
|
||||||
err := InitIndex()
|
err := InitIndex()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
printErr("Failed to initialize index:", err)
|
printErr("Failed to initialize index: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
webCrawlerInit()
|
webCrawlerInit()
|
||||||
|
|
45
main.go
45
main.go
|
@ -221,6 +221,7 @@ func runServer() {
|
||||||
http.HandleFunc("/save-settings", handleSaveSettings)
|
http.HandleFunc("/save-settings", handleSaveSettings)
|
||||||
http.HandleFunc("/image/", handleImageServe)
|
http.HandleFunc("/image/", handleImageServe)
|
||||||
http.HandleFunc("/image_status", handleImageStatus)
|
http.HandleFunc("/image_status", handleImageStatus)
|
||||||
|
http.HandleFunc("/privacy", handlePrivacyPage)
|
||||||
http.HandleFunc("/opensearch.xml", func(w http.ResponseWriter, r *http.Request) {
|
http.HandleFunc("/opensearch.xml", func(w http.ResponseWriter, r *http.Request) {
|
||||||
w.Header().Set("Content-Type", "application/opensearchdescription+xml")
|
w.Header().Set("Content-Type", "application/opensearchdescription+xml")
|
||||||
http.ServeFile(w, r, "static/opensearch.xml")
|
http.ServeFile(w, r, "static/opensearch.xml")
|
||||||
|
@ -235,6 +236,7 @@ func runServer() {
|
||||||
http.HandleFunc("/save-settings", handleWebsiteDisabled)
|
http.HandleFunc("/save-settings", handleWebsiteDisabled)
|
||||||
http.HandleFunc("/image/", handleWebsiteDisabled)
|
http.HandleFunc("/image/", handleWebsiteDisabled)
|
||||||
http.HandleFunc("/image_status", handleWebsiteDisabled)
|
http.HandleFunc("/image_status", handleWebsiteDisabled)
|
||||||
|
http.HandleFunc("/privacy", handleWebsiteDisabled)
|
||||||
http.HandleFunc("/opensearch.xml", handleWebsiteDisabled)
|
http.HandleFunc("/opensearch.xml", handleWebsiteDisabled)
|
||||||
printInfo("Website functionality disabled.")
|
printInfo("Website functionality disabled.")
|
||||||
}
|
}
|
||||||
|
@ -252,3 +254,46 @@ func handleWebsiteDisabled(w http.ResponseWriter, r *http.Request) {
|
||||||
w.WriteHeader(http.StatusServiceUnavailable)
|
w.WriteHeader(http.StatusServiceUnavailable)
|
||||||
_, _ = w.Write([]byte("The website functionality is currently disabled."))
|
_, _ = w.Write([]byte("The website functionality is currently disabled."))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func handlePrivacyPage(w http.ResponseWriter, r *http.Request) {
|
||||||
|
settings := loadUserSettings(w, r)
|
||||||
|
iconPathSVG, iconPathPNG := GetIconPath()
|
||||||
|
|
||||||
|
// Define the data structure for the template
|
||||||
|
data := struct {
|
||||||
|
Theme string
|
||||||
|
IconPathSVG string
|
||||||
|
IconPathPNG string
|
||||||
|
IsThemeDark bool
|
||||||
|
CookieRows []CookieRow
|
||||||
|
CurrentLang string
|
||||||
|
Safe string
|
||||||
|
LanguageOptions []LanguageOption
|
||||||
|
}{
|
||||||
|
Theme: settings.Theme,
|
||||||
|
IconPathSVG: iconPathSVG,
|
||||||
|
IconPathPNG: iconPathPNG,
|
||||||
|
IsThemeDark: settings.IsThemeDark,
|
||||||
|
CookieRows: generateCookieTable(r),
|
||||||
|
CurrentLang: settings.SiteLanguage,
|
||||||
|
Safe: settings.SafeSearch,
|
||||||
|
LanguageOptions: languageOptions,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse the template
|
||||||
|
tmpl, err := template.New("privacy.html").ParseFiles("templates/privacy.html")
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Error parsing template: %v", err)
|
||||||
|
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the response content type
|
||||||
|
w.Header().Set("Content-Type", "text/html; charset=utf-8")
|
||||||
|
|
||||||
|
// Execute the template
|
||||||
|
if err := tmpl.Execute(w, data); err != nil {
|
||||||
|
log.Printf("Error executing template: %v", err)
|
||||||
|
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -60,13 +60,6 @@
|
||||||
gap: 5px; /* Add spacing between buttons */
|
gap: 5px; /* Add spacing between buttons */
|
||||||
}
|
}
|
||||||
|
|
||||||
.image-view-close .btn-nostyle {
|
|
||||||
background-color: inherit;
|
|
||||||
border: none;
|
|
||||||
padding: 0px;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
#viewer-close-button,
|
#viewer-close-button,
|
||||||
#viewer-prev-button,
|
#viewer-prev-button,
|
||||||
#viewer-next-button {
|
#viewer-next-button {
|
||||||
|
@ -128,6 +121,7 @@
|
||||||
|
|
||||||
.full-size:hover,
|
.full-size:hover,
|
||||||
.proxy-size:hover {
|
.proxy-size:hover {
|
||||||
|
transition: all 0.3s ease;
|
||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -136,15 +130,6 @@
|
||||||
visibility: visible;
|
visibility: visible;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Button No Style */
|
|
||||||
.btn-nostyle {
|
|
||||||
background-color: inherit;
|
|
||||||
border: none;
|
|
||||||
padding: 0px;
|
|
||||||
width: fit-content;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Image Navigation Icons */
|
/* Image Navigation Icons */
|
||||||
.image-close,
|
.image-close,
|
||||||
.image-next,
|
.image-next,
|
||||||
|
@ -163,6 +148,7 @@
|
||||||
.image-close:hover,
|
.image-close:hover,
|
||||||
.image-next:hover,
|
.image-next:hover,
|
||||||
.image-before:hover {
|
.image-before:hover {
|
||||||
|
transition: all 0.3s ease;
|
||||||
background-color: var(--image-select);
|
background-color: var(--image-select);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
/* ------------------ Mini-Menu Styles ------------------ */
|
||||||
|
|
||||||
.settings-search-div-search {
|
.settings-search-div-search {
|
||||||
right: 20px;
|
right: 20px;
|
||||||
top: 25px;
|
top: 25px;
|
||||||
|
@ -141,3 +143,105 @@
|
||||||
border-radius: 0;
|
border-radius: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ------------------ About QGato Modal Styles ------------------ */
|
||||||
|
|
||||||
|
#aboutQGatoModal {
|
||||||
|
display: none;
|
||||||
|
position: fixed;
|
||||||
|
/* Center modal */
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
|
||||||
|
/* Keep it on top */
|
||||||
|
z-index: 999;
|
||||||
|
|
||||||
|
/* Match mini-menu background style */
|
||||||
|
background-color: var(--html-bg);
|
||||||
|
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24);
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-radius: 12px;
|
||||||
|
|
||||||
|
/* Spacing & sizing */
|
||||||
|
padding: 32px;
|
||||||
|
max-width: 600px; /* Increased width */
|
||||||
|
max-height: 80vh; /* Optional: restrict height to 80% of viewport */
|
||||||
|
overflow-y: auto; /* Enable scrolling if content exceeds height */
|
||||||
|
color: var(--font-fg);
|
||||||
|
}
|
||||||
|
|
||||||
|
#aboutQGatoModal #close-button {
|
||||||
|
position: absolute;
|
||||||
|
top: 12px;
|
||||||
|
right: 12px; /* Moved close button to top-right */
|
||||||
|
}
|
||||||
|
|
||||||
|
#aboutQGatoModal .modal-content {
|
||||||
|
text-align: center;
|
||||||
|
margin-top: 20px; /* Adjusted spacing */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Logo */
|
||||||
|
#aboutQGatoModal .modal-content img {
|
||||||
|
width: 100px; /* Increased logo size */
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Headings, paragraphs, etc. */
|
||||||
|
#aboutQGatoModal .modal-content h2 {
|
||||||
|
font-size: 2rem; /* Larger heading */
|
||||||
|
margin: 8px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#aboutQGatoModal .modal-content p {
|
||||||
|
font-size: 1.1rem; /* Larger paragraph text */
|
||||||
|
margin: 12px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Container for the Source Code / Privacy Policy buttons */
|
||||||
|
#aboutQGatoModal .button-container {
|
||||||
|
margin-top: 16px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Match mini-menu button style as closely as possible */
|
||||||
|
#aboutQGatoModal .button-container button {
|
||||||
|
background-color: var(--button);
|
||||||
|
color: var(--font-fg);
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-radius: 6px;
|
||||||
|
padding: 12px 16px; /* Larger button padding */
|
||||||
|
font-size: 1rem; /* Larger button text */
|
||||||
|
cursor: pointer;
|
||||||
|
transition: border 0.3s ease, background-color 0.3s ease, color 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
#aboutQGatoModal .button-container button:hover {
|
||||||
|
border: 1px solid var(--font-fg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Close Button Style */
|
||||||
|
.cloase-btn {
|
||||||
|
font-size: 1.5rem; /* Larger close button */
|
||||||
|
color: var(--search-button);
|
||||||
|
border-radius: 50%;
|
||||||
|
padding: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cloase-btn:hover {
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
background-color: var(--image-select);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ------------------ Common Button No Style ------------------ */
|
||||||
|
|
||||||
|
.btn-nostyle {
|
||||||
|
background-color: inherit;
|
||||||
|
border: none;
|
||||||
|
padding: 0px;
|
||||||
|
width: fit-content;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
95
static/css/style-privacy.css
Normal file
95
static/css/style-privacy.css
Normal file
|
@ -0,0 +1,95 @@
|
||||||
|
/* Main content wrapper */
|
||||||
|
.privacy-content-wrapper {
|
||||||
|
max-width: 800px;
|
||||||
|
margin: 80px auto 40px auto;
|
||||||
|
padding: 0 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Header section */
|
||||||
|
.privacy-header {
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.privacy-header h1 {
|
||||||
|
font-size: 2rem;
|
||||||
|
margin: 0;
|
||||||
|
color: var(--font-fg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.privacy-header p {
|
||||||
|
color: var(--fg);
|
||||||
|
margin-top: 10px;
|
||||||
|
font-size: 1.1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Section headings */
|
||||||
|
.privacy-section h2 {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
color: var(--font-fg);
|
||||||
|
border-bottom: 1px solid var(--border);
|
||||||
|
padding-bottom: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Section text */
|
||||||
|
.privacy-section p {
|
||||||
|
font-size: 1rem;
|
||||||
|
line-height: 1.6;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
color: var(--fg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Footer */
|
||||||
|
.privacy-footer {
|
||||||
|
text-align: center;
|
||||||
|
padding: 10px 0;
|
||||||
|
border-top: 1px solid var(--border);
|
||||||
|
color: var(--fg);
|
||||||
|
background-color: var(--html-bg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Links */
|
||||||
|
.privacy-section a {
|
||||||
|
color: var(--link);
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.privacy-section a:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Table styling */
|
||||||
|
.cookie-table {
|
||||||
|
width: 100%;
|
||||||
|
margin: 20px auto;
|
||||||
|
border-collapse: collapse;
|
||||||
|
text-align: left;
|
||||||
|
font-size: 1rem;
|
||||||
|
color: var(--fg);
|
||||||
|
background-color: var(--html-bg);
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
}
|
||||||
|
|
||||||
|
.cookie-table th,
|
||||||
|
.cookie-table td {
|
||||||
|
padding: 12px 15px;
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
}
|
||||||
|
|
||||||
|
.cookie-table th {
|
||||||
|
background-color: var(--search-bg);
|
||||||
|
color: var(--font-fg);
|
||||||
|
text-align: center;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cookie-table tr:nth-child(even) {
|
||||||
|
background-color: var(--snip-background);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Center the table within its section */
|
||||||
|
.privacy-section .cookie-table {
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
}
|
|
@ -13,7 +13,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||||
// Set the innerHTML of viewerOverlay
|
// Set the innerHTML of viewerOverlay
|
||||||
viewerOverlay.innerHTML = `
|
viewerOverlay.innerHTML = `
|
||||||
<div id="image-viewer" class="image_view image_hide">
|
<div id="image-viewer" class="image_view image_hide">
|
||||||
<div class="image-view-close">
|
<div class="btn-nostyle">
|
||||||
<button class="btn-nostyle" id="viewer-prev-button">
|
<button class="btn-nostyle" id="viewer-prev-button">
|
||||||
<div class="material-icons-round icon_visibility clickable image-before"></div> <!-- navigate_before -->
|
<div class="material-icons-round icon_visibility clickable image-before"></div> <!-- navigate_before -->
|
||||||
</button>
|
</button>
|
||||||
|
|
|
@ -44,4 +44,13 @@ document.addEventListener('DOMContentLoaded', function () {
|
||||||
document.getElementById('languageSelect').addEventListener('change', function () {
|
document.getElementById('languageSelect').addEventListener('change', function () {
|
||||||
updateSettings('lang', this.value);
|
updateSettings('lang', this.value);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Show/Hide About QGato
|
||||||
|
document.getElementById('aboutQGatoBtn').addEventListener('click', function() {
|
||||||
|
document.getElementById('aboutQGatoModal').style.display = 'block';
|
||||||
|
});
|
||||||
|
|
||||||
|
document.getElementById('close-button').addEventListener('click', function() {
|
||||||
|
document.getElementById('aboutQGatoModal').style.display = 'none';
|
||||||
|
});
|
||||||
});
|
});
|
|
@ -43,7 +43,7 @@
|
||||||
<option value="{{.Code}}" {{if eq .Code $.CurrentLang}}selected{{end}}>{{.Name}}</option>
|
<option value="{{.Code}}" {{if eq .Code $.CurrentLang}}selected{{end}}>{{.Name}}</option>
|
||||||
{{end}}
|
{{end}}
|
||||||
</select>
|
</select>
|
||||||
<!-- <button id="settingsButton" onclick="window.location.href='/about'">About QGato</button> -->
|
<button id="aboutQGatoBtn">About QGato</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -53,6 +53,27 @@
|
||||||
</div>
|
</div>
|
||||||
</noscript>
|
</noscript>
|
||||||
|
|
||||||
|
<!-- Popup Modal for QGato -->
|
||||||
|
<div id="aboutQGatoModal">
|
||||||
|
<!-- Close Button -->
|
||||||
|
<button class="btn-nostyle" id="close-button">
|
||||||
|
<div class="material-icons-round icon_visibility clickable cloase-btn"></div>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<div class="modal-content">
|
||||||
|
<img
|
||||||
|
src="/static/images/icon.svg"
|
||||||
|
alt="QGato"
|
||||||
|
>
|
||||||
|
<h2>QGato</h2>
|
||||||
|
<p>A open-source private search engine.</p>
|
||||||
|
<div class="button-container">
|
||||||
|
<button onclick="window.location.href='https://weforge.xyz/Spitfire/Search'">Source Code</button>
|
||||||
|
<button onclick="window.location.href='/privacy'">Privacy policy</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<form action="/search" id="prev-next-form" class="results-search-container" method="GET" autocomplete="off">
|
<form action="/search" id="prev-next-form" class="results-search-container" method="GET" autocomplete="off">
|
||||||
<h1 class="logomobile">
|
<h1 class="logomobile">
|
||||||
<div class="logo-container" herf="/">
|
<div class="logo-container" herf="/">
|
||||||
|
|
|
@ -43,7 +43,7 @@
|
||||||
<option value="{{.Code}}" {{if eq .Code $.CurrentLang}}selected{{end}}>{{.Name}}</option>
|
<option value="{{.Code}}" {{if eq .Code $.CurrentLang}}selected{{end}}>{{.Name}}</option>
|
||||||
{{end}}
|
{{end}}
|
||||||
</select>
|
</select>
|
||||||
<!-- <button id="settingsButton" onclick="window.location.href='/about'">About QGato</button> -->
|
<button id="aboutQGatoBtn">About QGato</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -53,6 +53,27 @@
|
||||||
</div>
|
</div>
|
||||||
</noscript>
|
</noscript>
|
||||||
|
|
||||||
|
<!-- Popup Modal for QGato -->
|
||||||
|
<div id="aboutQGatoModal">
|
||||||
|
<!-- Close Button -->
|
||||||
|
<button class="btn-nostyle" id="close-button">
|
||||||
|
<div class="material-icons-round icon_visibility clickable cloase-btn"></div>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<div class="modal-content">
|
||||||
|
<img
|
||||||
|
src="/static/images/icon.svg"
|
||||||
|
alt="QGato"
|
||||||
|
>
|
||||||
|
<h2>QGato</h2>
|
||||||
|
<p>A open-source private search engine.</p>
|
||||||
|
<div class="button-container">
|
||||||
|
<button onclick="window.location.href='https://weforge.xyz/Spitfire/Search'">Source Code</button>
|
||||||
|
<button onclick="window.location.href='/privacy'">Privacy policy</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<form action="/search" id="prev-next-form" class="results-search-container" method="GET" autocomplete="off">
|
<form action="/search" id="prev-next-form" class="results-search-container" method="GET" autocomplete="off">
|
||||||
<h1 class="logomobile">
|
<h1 class="logomobile">
|
||||||
<div class="logo-container" herf="/">
|
<div class="logo-container" herf="/">
|
||||||
|
|
|
@ -52,7 +52,7 @@
|
||||||
<option value="{{.Code}}" {{if eq .Code $.CurrentLang}}selected{{end}}>{{.Name}}</option>
|
<option value="{{.Code}}" {{if eq .Code $.CurrentLang}}selected{{end}}>{{.Name}}</option>
|
||||||
{{end}}
|
{{end}}
|
||||||
</select>
|
</select>
|
||||||
<!-- <button id="settingsButton" onclick="window.location.href='/about'">About QGato</button> -->
|
<button id="aboutQGatoBtn">About QGato</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -62,6 +62,27 @@
|
||||||
</div>
|
</div>
|
||||||
</noscript>
|
</noscript>
|
||||||
|
|
||||||
|
<!-- Popup Modal for QGato -->
|
||||||
|
<div id="aboutQGatoModal">
|
||||||
|
<!-- Close Button -->
|
||||||
|
<button class="btn-nostyle" id="close-button">
|
||||||
|
<div class="material-icons-round icon_visibility clickable cloase-btn"></div>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<div class="modal-content">
|
||||||
|
<img
|
||||||
|
src="/static/images/icon.svg"
|
||||||
|
alt="QGato"
|
||||||
|
>
|
||||||
|
<h2>QGato</h2>
|
||||||
|
<p>A open-source private search engine.</p>
|
||||||
|
<div class="button-container">
|
||||||
|
<button onclick="window.location.href='https://weforge.xyz/Spitfire/Search'">Source Code</button>
|
||||||
|
<button onclick="window.location.href='/privacy'">Privacy policy</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<form action="/search" id="prev-next-form" class="results-search-container" method="GET" autocomplete="off">
|
<form action="/search" id="prev-next-form" class="results-search-container" method="GET" autocomplete="off">
|
||||||
<h1 class="logomobile">
|
<h1 class="logomobile">
|
||||||
<div class="logo-container" herf="/">
|
<div class="logo-container" herf="/">
|
||||||
|
|
|
@ -58,7 +58,7 @@
|
||||||
<option value="{{.Code}}" {{if eq .Code $.CurrentLang}}selected{{end}}>{{.Name}}</option>
|
<option value="{{.Code}}" {{if eq .Code $.CurrentLang}}selected{{end}}>{{.Name}}</option>
|
||||||
{{end}}
|
{{end}}
|
||||||
</select>
|
</select>
|
||||||
<!-- <button id="settingsButton" onclick="window.location.href='/about'">About QGato</button> -->
|
<button id="aboutQGatoBtn">About QGato</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -68,6 +68,27 @@
|
||||||
</div>
|
</div>
|
||||||
</noscript>
|
</noscript>
|
||||||
|
|
||||||
|
<!-- Popup Modal for QGato -->
|
||||||
|
<div id="aboutQGatoModal">
|
||||||
|
<!-- Close Button -->
|
||||||
|
<button class="btn-nostyle" id="close-button">
|
||||||
|
<div class="material-icons-round icon_visibility clickable cloase-btn"></div>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<div class="modal-content">
|
||||||
|
<img
|
||||||
|
src="/static/images/icon.svg"
|
||||||
|
alt="QGato"
|
||||||
|
>
|
||||||
|
<h2>QGato</h2>
|
||||||
|
<p>A open-source private search engine.</p>
|
||||||
|
<div class="button-container">
|
||||||
|
<button onclick="window.location.href='https://weforge.xyz/Spitfire/Search'">Source Code</button>
|
||||||
|
<button onclick="window.location.href='/privacy'">Privacy policy</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<form action="/search" id="prev-next-form" class="results-search-container" method="GET" autocomplete="off">
|
<form action="/search" id="prev-next-form" class="results-search-container" method="GET" autocomplete="off">
|
||||||
<h1 class="logomobile">
|
<h1 class="logomobile">
|
||||||
<div class="logo-container" herf="/">
|
<div class="logo-container" herf="/">
|
||||||
|
|
133
templates/privacy.html
Normal file
133
templates/privacy.html
Normal file
|
@ -0,0 +1,133 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Privacy Policy</title>
|
||||||
|
|
||||||
|
<!-- Include your global or theme-specific CSS -->
|
||||||
|
<link rel="stylesheet" href="/static/css/style-menu.css">
|
||||||
|
<link rel="stylesheet" href="/static/css/style-search.css">
|
||||||
|
<link rel="stylesheet" href="/static/css/{{.Theme}}.css">
|
||||||
|
<link rel="stylesheet" href="/static/css/style-fonts.css">
|
||||||
|
<link rel="stylesheet" href="/static/css/style-privacy.css">
|
||||||
|
|
||||||
|
<!-- Icons -->
|
||||||
|
<link rel="icon" href="{{ .IconPathSVG }}" type="image/svg+xml">
|
||||||
|
<link rel="icon" href="{{ .IconPathPNG }}" type="image/png">
|
||||||
|
<link rel="apple-touch-icon" href="{{ .IconPathPNG }}">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<!-- Menu Button -->
|
||||||
|
<div id="js-enabled">
|
||||||
|
<div class="settings-search-div settings-search-div-search">
|
||||||
|
<button class="material-icons-round clickable settings-icon-link settings-icon-link-search"></button>
|
||||||
|
</div>
|
||||||
|
<div class="search-menu settings-menu-hidden">
|
||||||
|
<h2>Settings</h2>
|
||||||
|
<div class="settings-content">
|
||||||
|
<button id="settingsButton" onclick="window.location.href='/settings'">All settings</button>
|
||||||
|
<div class="theme-settings">
|
||||||
|
<p><span class="highlight">Current theme: </span> <span id="theme_name">{{.Theme}}</span></p>
|
||||||
|
<div class="themes-settings-menu">
|
||||||
|
<div><img class="view-image-search clickable" id="dark_theme" alt="Dark Theme" src="/static/images/dark.webp"></div>
|
||||||
|
<div><img class="view-image-search clickable" id="light_theme" alt="Light Theme" src="/static/images/light.webp"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<select class="lang" name="safe" id="safeSearchSelect">
|
||||||
|
<option value="disabled" {{if eq .Safe "disabled"}}selected{{end}}>Safe Search Off</option>
|
||||||
|
<option value="active" {{if eq .Safe "active"}}selected{{end}}>Safe Search On</option>
|
||||||
|
</select>
|
||||||
|
<select class="lang" name="lang" id="languageSelect">
|
||||||
|
{{range .LanguageOptions}}
|
||||||
|
<option value="{{.Code}}" {{if eq .Code $.CurrentLang}}selected{{end}}>{{.Name}}</option>
|
||||||
|
{{end}}
|
||||||
|
</select>
|
||||||
|
<button id="aboutQGatoBtn">About QGato</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="js-disabled">
|
||||||
|
<a href="/settings" class="material-icons-round settings-icon-link-search"></a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Popup Modal for QGato -->
|
||||||
|
<div id="aboutQGatoModal">
|
||||||
|
<!-- Close Button -->
|
||||||
|
<button class="btn-nostyle" id="close-button">
|
||||||
|
<div class="material-icons-round icon_visibility clickable cloase-btn"></div>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<div class="modal-content">
|
||||||
|
<img
|
||||||
|
src="/static/images/icon.svg"
|
||||||
|
alt="QGato"
|
||||||
|
>
|
||||||
|
<h2>QGato</h2>
|
||||||
|
<p>A open-source private search engine.</p>
|
||||||
|
<div class="button-container">
|
||||||
|
<button onclick="window.location.href='https://weforge.xyz/Spitfire/Search'">Source Code</button>
|
||||||
|
<button onclick="window.location.href='/privacy'">Privacy policy</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Main Content Section -->
|
||||||
|
<main class="privacy-content-wrapper">
|
||||||
|
<header class="privacy-header">
|
||||||
|
<h1>Privacy Policy</h1>
|
||||||
|
<p>Your privacy is important to us. This page outlines our practices.</p>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<section class="privacy-section">
|
||||||
|
<h2>Introduction</h2>
|
||||||
|
<p>This website is a Free and Open Source Software (FOSS) project licensed under the <a href="https://www.gnu.org/licenses/agpl-3.0.html" target="_blank" rel="noopener noreferrer">AGPL-3.0</a> license. The project is committed to providing a private and secure experience for all users.</p>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="privacy-section">
|
||||||
|
<h2>Data Collection</h2>
|
||||||
|
<p>Our servers <b>do not collect any user data</b>, including IP addresses, browsing history, or any other identifiable information. We respect your privacy and ensure that no user information is logged or stored on our servers.</p>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="privacy-section">
|
||||||
|
<h2>Cookies Used</h2>
|
||||||
|
<p>Our cookies are <b>not used to track users</b> or sell user data, they are just used to save your settings.</p>
|
||||||
|
<p>These following cookies are used by this site:</p>
|
||||||
|
<table class="cookie-table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Cookie Name</th>
|
||||||
|
<th>Value</th>
|
||||||
|
<th>Description</th>
|
||||||
|
<th>Expiration</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{{ range .CookieRows }}
|
||||||
|
<tr>
|
||||||
|
<td>{{ .Name }}</td>
|
||||||
|
<td>{{ .Value }}</td>
|
||||||
|
<td>{{ .Description }}</td>
|
||||||
|
<td>{{ .Expiration }}</td>
|
||||||
|
</tr>
|
||||||
|
{{ end }}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<!-- Footer Section, matching site theme
|
||||||
|
<footer class="privacy-footer">
|
||||||
|
<p>© QGato. Licensed under the <a href="https://www.gnu.org/licenses/agpl-3.0.html" target="_blank" rel="noopener noreferrer">AGPL-3.0</a>.</p>
|
||||||
|
</footer> -->
|
||||||
|
|
||||||
|
<!-- Included JavaScript -->
|
||||||
|
<script defer src="/static/js/minimenu.js"></script>
|
||||||
|
<script>
|
||||||
|
// When JS is detected, update the DOM to show the JS-based menu
|
||||||
|
document.getElementById('js-enabled').style.display = 'block';
|
||||||
|
document.getElementById('js-disabled').style.display = 'none';
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -58,7 +58,7 @@
|
||||||
<option value="{{.Code}}" {{if eq .Code $.CurrentLang}}selected{{end}}>{{.Name}}</option>
|
<option value="{{.Code}}" {{if eq .Code $.CurrentLang}}selected{{end}}>{{.Name}}</option>
|
||||||
{{end}}
|
{{end}}
|
||||||
</select>
|
</select>
|
||||||
<!-- <button id="settingsButton" onclick="window.location.href='/about'">About QGato</button> -->
|
<button id="aboutQGatoBtn">About QGato</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -66,6 +66,27 @@
|
||||||
<a href="/settings" class="material-icons-round settings-icon-link-search"></a>
|
<a href="/settings" class="material-icons-round settings-icon-link-search"></a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Popup Modal for QGato -->
|
||||||
|
<div id="aboutQGatoModal">
|
||||||
|
<!-- Close Button -->
|
||||||
|
<button class="btn-nostyle" id="close-button">
|
||||||
|
<div class="material-icons-round icon_visibility clickable cloase-btn"></div>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<div class="modal-content">
|
||||||
|
<img
|
||||||
|
src="/static/images/icon.svg"
|
||||||
|
alt="QGato"
|
||||||
|
>
|
||||||
|
<h2>QGato</h2>
|
||||||
|
<p>A open-source private search engine.</p>
|
||||||
|
<div class="button-container">
|
||||||
|
<button onclick="window.location.href='https://weforge.xyz/Spitfire/Search'">Source Code</button>
|
||||||
|
<button onclick="window.location.href='/privacy'">Privacy policy</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Search Form -->
|
<!-- Search Form -->
|
||||||
<form action="/search" class="search-container" method="get" autocomplete="off">
|
<form action="/search" class="search-container" method="get" autocomplete="off">
|
||||||
<div class="search-page-content">
|
<div class="search-page-content">
|
||||||
|
|
|
@ -43,7 +43,7 @@
|
||||||
<option value="{{.Code}}" {{if eq .Code $.CurrentLang}}selected{{end}}>{{.Name}}</option>
|
<option value="{{.Code}}" {{if eq .Code $.CurrentLang}}selected{{end}}>{{.Name}}</option>
|
||||||
{{end}}
|
{{end}}
|
||||||
</select>
|
</select>
|
||||||
<!-- <button id="settingsButton" onclick="window.location.href='/about'">About QGato</button> -->
|
<button id="aboutQGatoBtn">About QGato</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -53,6 +53,27 @@
|
||||||
</div>
|
</div>
|
||||||
</noscript>
|
</noscript>
|
||||||
|
|
||||||
|
<!-- Popup Modal for QGato -->
|
||||||
|
<div id="aboutQGatoModal">
|
||||||
|
<!-- Close Button -->
|
||||||
|
<button class="btn-nostyle" id="close-button">
|
||||||
|
<div class="material-icons-round icon_visibility clickable cloase-btn"></div>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<div class="modal-content">
|
||||||
|
<img
|
||||||
|
src="/static/images/icon.svg"
|
||||||
|
alt="QGato"
|
||||||
|
>
|
||||||
|
<h2>QGato</h2>
|
||||||
|
<p>A open-source private search engine.</p>
|
||||||
|
<div class="button-container">
|
||||||
|
<button onclick="window.location.href='https://weforge.xyz/Spitfire/Search'">Source Code</button>
|
||||||
|
<button onclick="window.location.href='/privacy'">Privacy policy</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<form action="/search" id="prev-next-form" class="results-search-container" method="GET" autocomplete="off">
|
<form action="/search" id="prev-next-form" class="results-search-container" method="GET" autocomplete="off">
|
||||||
<h1 class="logomobile">
|
<h1 class="logomobile">
|
||||||
<div class="logo-container" href="/">
|
<div class="logo-container" href="/">
|
||||||
|
|
|
@ -43,7 +43,7 @@
|
||||||
<option value="{{.Code}}" {{if eq .Code $.CurrentLang}}selected{{end}}>{{.Name}}</option>
|
<option value="{{.Code}}" {{if eq .Code $.CurrentLang}}selected{{end}}>{{.Name}}</option>
|
||||||
{{end}}
|
{{end}}
|
||||||
</select>
|
</select>
|
||||||
<!-- <button id="settingsButton" onclick="window.location.href='/about'">About QGato</button> -->
|
<button id="aboutQGatoBtn">About QGato</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -53,6 +53,27 @@
|
||||||
</div>
|
</div>
|
||||||
</noscript>
|
</noscript>
|
||||||
|
|
||||||
|
<!-- Popup Modal for QGato -->
|
||||||
|
<div id="aboutQGatoModal">
|
||||||
|
<!-- Close Button -->
|
||||||
|
<button class="btn-nostyle" id="close-button">
|
||||||
|
<div class="material-icons-round icon_visibility clickable cloase-btn"></div>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<div class="modal-content">
|
||||||
|
<img
|
||||||
|
src="/static/images/icon.svg"
|
||||||
|
alt="QGato"
|
||||||
|
>
|
||||||
|
<h2>QGato</h2>
|
||||||
|
<p>A open-source private search engine.</p>
|
||||||
|
<div class="button-container">
|
||||||
|
<button onclick="window.location.href='https://weforge.xyz/Spitfire/Search'">Source Code</button>
|
||||||
|
<button onclick="window.location.href='/privacy'">Privacy policy</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<form action="/search" id="prev-next-form" class="results-search-container" method="GET" autocomplete="off">
|
<form action="/search" id="prev-next-form" class="results-search-container" method="GET" autocomplete="off">
|
||||||
<h1 class="logomobile">
|
<h1 class="logomobile">
|
||||||
<div class="logo-container" herf="/">
|
<div class="logo-container" herf="/">
|
||||||
|
|
189
user-settings.go
189
user-settings.go
|
@ -18,44 +18,44 @@ func loadUserSettings(w http.ResponseWriter, r *http.Request) UserSettings {
|
||||||
var settings UserSettings
|
var settings UserSettings
|
||||||
saveRequired := false
|
saveRequired := false
|
||||||
|
|
||||||
// Load theme
|
for _, cd := range AllCookies {
|
||||||
if cookie, err := r.Cookie("theme"); err == nil {
|
// Attempt to read the cookie
|
||||||
settings.Theme = cookie.Value
|
if cookie, err := r.Cookie(cd.Name); err == nil {
|
||||||
} else {
|
// Use SetValue to update the correct UserSettings field
|
||||||
settings.Theme = "dark"
|
cd.SetValue(&settings, cookie.Value)
|
||||||
saveRequired = true
|
|
||||||
}
|
|
||||||
|
|
||||||
// Determine if the selected theme is dark
|
|
||||||
settings.IsThemeDark = settings.Theme == "dark" || settings.Theme == "night" || settings.Theme == "black" || settings.Theme == "latte"
|
|
||||||
|
|
||||||
// Load site language
|
|
||||||
if cookie, err := r.Cookie("site_language"); err == nil {
|
|
||||||
settings.SiteLanguage = cookie.Value
|
|
||||||
} else {
|
|
||||||
// If no site language is set, use Accept-Language or default to "en"
|
|
||||||
acceptLang := r.Header.Get("Accept-Language")
|
|
||||||
if acceptLang != "" {
|
|
||||||
settings.SiteLanguage = normalizeLangCode(strings.Split(acceptLang, ",")[0])
|
|
||||||
} else {
|
} else {
|
||||||
settings.SiteLanguage = "en" // Default language
|
// 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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
saveRequired = true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load search language (can be empty)
|
// If theme was set, update IsThemeDark just to be sure
|
||||||
if cookie, err := r.Cookie("search_language"); err == nil {
|
// Alternatively do it inside SetValue for "theme"
|
||||||
settings.SearchLanguage = cookie.Value
|
settings.IsThemeDark = settings.Theme == "dark" ||
|
||||||
}
|
settings.Theme == "night" ||
|
||||||
|
settings.Theme == "black" ||
|
||||||
// Load safe search
|
settings.Theme == "latte"
|
||||||
if cookie, err := r.Cookie("safe"); err == nil {
|
|
||||||
settings.SafeSearch = cookie.Value
|
|
||||||
} else {
|
|
||||||
settings.SafeSearch = ""
|
|
||||||
saveRequired = true
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// Save any new default cookies that might have been triggered
|
||||||
if saveRequired {
|
if saveRequired {
|
||||||
saveUserSettings(w, settings)
|
saveUserSettings(w, settings)
|
||||||
}
|
}
|
||||||
|
@ -66,38 +66,16 @@ func loadUserSettings(w http.ResponseWriter, r *http.Request) UserSettings {
|
||||||
func saveUserSettings(w http.ResponseWriter, settings UserSettings) {
|
func saveUserSettings(w http.ResponseWriter, settings UserSettings) {
|
||||||
expiration := time.Now().Add(90 * 24 * time.Hour)
|
expiration := time.Now().Add(90 * 24 * time.Hour)
|
||||||
|
|
||||||
http.SetCookie(w, &http.Cookie{
|
for _, cd := range AllCookies {
|
||||||
Name: "theme",
|
http.SetCookie(w, &http.Cookie{
|
||||||
Value: settings.Theme,
|
Name: cd.Name,
|
||||||
Path: "/",
|
Value: cd.GetValue(settings),
|
||||||
Expires: expiration,
|
Path: "/",
|
||||||
Secure: true,
|
Expires: expiration,
|
||||||
SameSite: http.SameSiteStrictMode,
|
Secure: true,
|
||||||
})
|
SameSite: http.SameSiteStrictMode,
|
||||||
http.SetCookie(w, &http.Cookie{
|
})
|
||||||
Name: "site_language",
|
}
|
||||||
Value: settings.SiteLanguage,
|
|
||||||
Path: "/",
|
|
||||||
Expires: expiration,
|
|
||||||
Secure: true,
|
|
||||||
SameSite: http.SameSiteStrictMode,
|
|
||||||
})
|
|
||||||
http.SetCookie(w, &http.Cookie{
|
|
||||||
Name: "search_language",
|
|
||||||
Value: settings.SearchLanguage,
|
|
||||||
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)
|
printDebug("settings saved: %v", settings)
|
||||||
}
|
}
|
||||||
|
@ -193,3 +171,84 @@ func isValidLangCode(lang string) bool {
|
||||||
}
|
}
|
||||||
return false
|
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
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue