Search/common.go
partisan 66414952e8
Some checks failed
Run Integration Tests / test (push) Failing after 39s
Fixed 'Error parsing template: template: privacy.html:27: function "translate"'
2025-05-30 17:04:57 +02:00

184 lines
5.4 KiB
Go
Executable file
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package main
import (
"crypto/rand"
"encoding/base64"
"encoding/json"
"fmt"
"html/template"
mathrand "math/rand"
"net/http"
"net/url"
"strings"
"time"
)
var (
funcs = template.FuncMap{
"sub": func(a, b int) int {
return a - b
},
"add": func(a, b int) int {
return a + b
},
"translate": Translate,
"toJSON": func(v interface{}) (string, error) {
jsonBytes, err := json.Marshal(v)
if err != nil {
return "", err
}
return string(jsonBytes), nil
},
}
)
type SearchEngine struct {
Name string
Func func(string, string, string, int) ([]SearchResult, time.Duration, error)
}
type LinkParts struct {
Domain template.HTML
Path template.HTML
RootURL string // used by getFaviconProxyURL()
}
// Helper function to render templates without elapsed time measurement
func renderTemplate(w http.ResponseWriter, tmplName string, data map[string]interface{}) {
// Generate icon paths for SVG and PNG, including a 1/10 chance for an alternate icon
iconPathSVG, iconPathPNG := GetIconPath()
// Add icon paths to data map so they are available in all templates
if data == nil {
data = make(map[string]interface{})
}
data["IconPathSVG"] = iconPathSVG
data["IconPathPNG"] = iconPathPNG
// Parse and execute the template with shared functions
tmpl, err := template.New(tmplName).Funcs(funcs).ParseFiles("templates/" + tmplName)
if err != nil {
printErr("Error parsing template: %v", err)
http.Error(w, Translate("internal_server_error"), http.StatusInternalServerError)
return
}
// Execute the template
err = tmpl.Execute(w, data)
if err != nil {
printErr("Error executing template: %v", err)
http.Error(w, Translate("internal_server_error"), http.StatusInternalServerError)
}
}
// Randoms string generator used for auth code
func generateStrongRandomString(length int) string {
bytes := make([]byte, length)
_, err := rand.Read(bytes)
if err != nil {
printErr("Error generating random string: %v", err)
}
return base64.URLEncoding.EncodeToString(bytes)[:length]
}
// Checks if the URL already includes a protocol
func hasProtocol(url string) bool {
return strings.HasPrefix(url, "http://") || strings.HasPrefix(url, "https://")
}
// Checks if the domain is a local address
func isLocalAddress(domain string) bool {
return domain == "localhost" || strings.HasPrefix(domain, "127.") || strings.HasPrefix(domain, "192.168.") || strings.HasPrefix(domain, "10.")
}
// Ensures that HTTP or HTTPS is before the address if needed
func addProtocol(domain string) string {
if hasProtocol(domain) {
return domain
}
if isLocalAddress(domain) {
return "http://" + domain
}
return "https://" + domain
}
// GetIconPath returns both SVG and PNG icon paths, with a 1/10 chance for a randomly generated "alt" icon.
func GetIconPath() (string, string) {
// 1 in 10 chance to select an alt icon
if mathrand.Intn(10) == 0 {
// Generate a random number between 2 and 4
altIconNumber := 2 + mathrand.Intn(3) // mathrand.Intn(3) generates 0, 1, or 2
selectedAltIcon := "icon-alt" + fmt.Sprint(altIconNumber)
return "/static/images/" + selectedAltIcon + ".svg", "/static/images/" + selectedAltIcon + ".png"
}
// Default paths
return "/static/images/icon.svg", "/static/images/icon.png"
}
// FormatElapsedTime formats elapsed time as a string,
// using:
// - "> 0.01 ms" if under 49µs
// - "0.xx ms" if under 1ms
// - "xxx ms" if under 300ms
// - "x.xx seconds" otherwise
func FormatElapsedTime(elapsed time.Duration) string {
if elapsed < 49*time.Microsecond {
return fmt.Sprintf("> 0.01 %s", Translate("milliseconds"))
} else if elapsed < time.Millisecond {
ms := float64(elapsed.Microseconds()) / 1000.0
return fmt.Sprintf("%.2f %s", ms, Translate("milliseconds"))
} else if elapsed < 300*time.Millisecond {
return fmt.Sprintf("%d %s", elapsed.Milliseconds(), Translate("milliseconds"))
}
return fmt.Sprintf("%.2f %s", elapsed.Seconds(), Translate("seconds"))
}
func FormatURLParts(rawURL string) (domain, path, rootURL string) {
parsed, err := url.Parse(rawURL)
if err != nil || parsed.Host == "" {
return "", "", ""
}
domain = parsed.Host
if strings.HasPrefix(domain, "www.") {
domain = domain[4:]
}
rootURL = parsed.Scheme + "://" + parsed.Host
path = strings.Trim(parsed.Path, "/")
pathSegments := strings.Split(path, "/")
var cleanSegments []string
for _, seg := range pathSegments {
if seg != "" {
cleanSegments = append(cleanSegments, seg)
}
}
path = strings.Join(cleanSegments, "/")
return domain, path, rootURL
}
func FormatLinkHTML(rawURL string) LinkParts {
domain, path, root := FormatURLParts(rawURL)
lp := LinkParts{
RootURL: root,
}
lp.Domain = template.HTML(fmt.Sprintf(`<span class="result-domain">%s</span>`, template.HTMLEscapeString(domain)))
if path != "" {
pathDisplay := strings.ReplaceAll(path, "/", " ")
lp.Path = template.HTML(fmt.Sprintf(`<span class="result-path"> %s</span>`, template.HTMLEscapeString(pathDisplay)))
}
return lp
}
// Converts any struct to a map[string]interface{} using JSON round-trip.
// Useful for rendering templates with generic map input.
func toMap(data interface{}) map[string]interface{} {
jsonBytes, _ := json.Marshal(data)
var result map[string]interface{}
_ = json.Unmarshal(jsonBytes, &result)
return result
}