diff --git a/cmd/kafka/main.go b/cmd/kafka/main.go
index f16aa96..cdc81b5 100644
--- a/cmd/kafka/main.go
+++ b/cmd/kafka/main.go
@@ -77,7 +77,7 @@ func main() {
acSvc := autocomplete.NewService(cfg.Upstream.URL, cfg.HTTPTimeout())
- h := httpapi.NewHandler(svc, acSvc.Suggestions)
+ h := httpapi.NewHandler(svc, acSvc.Suggestions, cfg.Server.SourceURL)
mux := http.NewServeMux()
mux.HandleFunc("/", h.Index)
diff --git a/config.example.toml b/config.example.toml
index 34f60a6..042bb63 100644
--- a/config.example.toml
+++ b/config.example.toml
@@ -14,6 +14,11 @@ http_timeout = "10s"
# Example: "https://search.example.com"
base_url = ""
+# Link to the source code (shown in footer as "Source" link)
+# Defaults to the upstream kafka repo if not set.
+# Example: "https://git.example.com/my-kafka-fork"
+source_url = ""
+
[upstream]
# URL of an upstream metasearch instance for unported engines (env: UPSTREAM_SEARXNG_URL)
# Leave empty to run without an upstream proxy.
diff --git a/internal/config/config.go b/internal/config/config.go
index 2644c70..e5d1fbb 100644
--- a/internal/config/config.go
+++ b/internal/config/config.go
@@ -40,7 +40,8 @@ type Config struct {
type ServerConfig struct {
Port int `toml:"port"`
HTTPTimeout string `toml:"http_timeout"`
- BaseURL string `toml:"base_url"` // Public URL for OpenSearch XML (e.g. "https://search.example.com")
+ BaseURL string `toml:"base_url"` // Public URL for OpenSearch XML (e.g. "https://search.example.com")
+ SourceURL string `toml:"source_url"` // Link to the source code (e.g. "https://git.example.com/fork/kafka")
}
type UpstreamConfig struct {
diff --git a/internal/httpapi/handlers.go b/internal/httpapi/handlers.go
index 9e62c1b..cc19b4b 100644
--- a/internal/httpapi/handlers.go
+++ b/internal/httpapi/handlers.go
@@ -30,12 +30,14 @@ import (
type Handler struct {
searchSvc *search.Service
autocompleteSvc func(ctx context.Context, query string) ([]string, error)
+ sourceURL string
}
-func NewHandler(searchSvc *search.Service, autocompleteSuggestions func(ctx context.Context, query string) ([]string, error)) *Handler {
+func NewHandler(searchSvc *search.Service, autocompleteSuggestions func(ctx context.Context, query string) ([]string, error), sourceURL string) *Handler {
return &Handler{
searchSvc: searchSvc,
autocompleteSvc: autocompleteSuggestions,
+ sourceURL: sourceURL,
}
}
@@ -51,7 +53,7 @@ func (h *Handler) Index(w http.ResponseWriter, r *http.Request) {
http.NotFound(w, r)
return
}
- if err := views.RenderIndex(w); err != nil {
+ if err := views.RenderIndex(w, h.sourceURL); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
}
@@ -80,7 +82,7 @@ func (h *Handler) Search(w http.ResponseWriter, r *http.Request) {
if err != nil {
// For HTML, render error on the results page.
if req.Format == contracts.FormatHTML || r.FormValue("format") == "html" {
- pd := views.PageData{Query: r.FormValue("q")}
+ pd := views.PageData{SourceURL: h.sourceURL, Query: r.FormValue("q")}
if views.IsHTMXRequest(r) {
views.RenderSearchFragment(w, pd)
} else {
@@ -95,7 +97,7 @@ func (h *Handler) Search(w http.ResponseWriter, r *http.Request) {
resp, err := h.searchSvc.Search(r.Context(), req)
if err != nil {
if req.Format == contracts.FormatHTML {
- pd := views.PageData{Query: req.Query}
+ pd := views.PageData{SourceURL: h.sourceURL, Query: req.Query}
if views.IsHTMXRequest(r) {
views.RenderSearchFragment(w, pd)
} else {
diff --git a/internal/views/templates/base.html b/internal/views/templates/base.html
index 5b58487..d2fec79 100644
--- a/internal/views/templates/base.html
+++ b/internal/views/templates/base.html
@@ -35,7 +35,7 @@
diff --git a/internal/views/views.go b/internal/views/views.go
index 50cec56..4d7289c 100644
--- a/internal/views/views.go
+++ b/internal/views/views.go
@@ -35,6 +35,7 @@ var staticFS embed.FS
// PageData holds all data passed to templates.
type PageData struct {
+ SourceURL string
Query string
Pageno int
PrevPage int
@@ -187,9 +188,9 @@ func FromResponse(resp contracts.SearchResponse, query string, pageno int) PageD
}
// RenderIndex renders the homepage (search box only).
-func RenderIndex(w http.ResponseWriter) error {
+func RenderIndex(w http.ResponseWriter, sourceURL string) error {
w.Header().Set("Content-Type", "text/html; charset=utf-8")
- return tmplIndex.ExecuteTemplate(w, "base", PageData{ShowHeader: true})
+ return tmplIndex.ExecuteTemplate(w, "base", PageData{ShowHeader: true, SourceURL: sourceURL})
}
// RenderSearch renders the full search results page (with base layout).