refactor: remove SearXNG references and rename binary to kafka

- Rename cmd/searxng-go to cmd/kafka
- Remove all SearXNG references from source comments while keeping
  "SearXNG-compatible API" in user-facing docs
- Update binary paths in README, CLAUDE.md, and Dockerfile
- Update log message to "kafka starting"

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
ashisgreat22 2026-03-22 01:47:03 +01:00
parent 4482cb4dde
commit fcd9be16df
18 changed files with 47 additions and 48 deletions

View file

@ -11,7 +11,7 @@ import (
"time"
)
// Service fetches search suggestions from an upstream SearXNG instance
// Service fetches search suggestions from an upstream metasearch instance
// or falls back to Wikipedia's OpenSearch API.
type Service struct {
upstreamURL string
@ -40,7 +40,7 @@ func (s *Service) Suggestions(ctx context.Context, query string) ([]string, erro
return s.wikipediaSuggestions(ctx, query)
}
// upstreamSuggestions proxies to an upstream SearXNG /autocompleter endpoint.
// upstreamSuggestions proxies to an upstream /autocompleter endpoint.
func (s *Service) upstreamSuggestions(ctx context.Context, query string) ([]string, error) {
u := s.upstreamURL + "/autocompleter?" + url.Values{"q": {query}}.Encode()
req, err := http.NewRequestWithContext(ctx, http.MethodGet, u, nil)
@ -64,7 +64,7 @@ func (s *Service) upstreamSuggestions(ctx context.Context, query string) ([]stri
return nil, err
}
// SearXNG /autocompleter returns a plain JSON array of strings.
// The /autocompleter endpoint returns a plain JSON array of strings.
var out []string
if err := json.Unmarshal(body, &out); err != nil {
return nil, err

View file

@ -5,15 +5,15 @@ import (
"encoding/json"
)
// MainResult represents one element of SearXNG's `results` array.
// MainResult represents one element of the `results` array.
//
// SearXNG returns many additional keys beyond what templates use. To keep the
// The API returns many additional keys beyond what templates use. To keep the
// contract stable for proxying/merging, we preserve all unknown keys in
// `raw` and re-emit them via MarshalJSON.
type MainResult struct {
raw map[string]any
// Common fields used by SearXNG templates (RSS uses: title, url, content, pubdate).
// Common fields used by templates (RSS uses: title, url, content, pubdate).
Template string `json:"template"`
Title string `json:"title"`
Content string `json:"content"`
@ -28,12 +28,12 @@ type MainResult struct {
Positions []int `json:"positions"`
Engines []string `json:"engines"`
// These fields exist in SearXNG's MainResult base; keep them so downstream
// These fields exist in the MainResult base; keep them so downstream
// callers can generate richer output later.
OpenGroup bool `json:"open_group"`
CloseGroup bool `json:"close_group"`
// parsed_url in SearXNG is emitted as a tuple; we preserve it as-is.
// parsed_url is emitted as a tuple; we preserve it as-is.
ParsedURL any `json:"parsed_url"`
}

View file

@ -1,6 +1,6 @@
package contracts
// OutputFormat matches SearXNG's `/search?format=...` values.
// OutputFormat matches the `/search?format=...` values.
type OutputFormat string
const (
@ -28,7 +28,7 @@ type SearchRequest struct {
Engines []string
Categories []string
// EngineData matches SearXNG's `engine_data-<engine>-<key>=<value>` parameters.
// EngineData matches the `engine_data-<engine>-<key>=<value>` parameters.
EngineData map[string]map[string]string
// AccessToken is an optional request token used to gate paid/limited engines.
@ -36,7 +36,7 @@ type SearchRequest struct {
AccessToken string
}
// SearchResponse matches the JSON schema returned by SearXNG's `webutils.get_json_response()`.
// SearchResponse matches the JSON schema used by `webutils.get_json_response()`.
type SearchResponse struct {
Query string `json:"query"`
NumberOfResults int `json:"number_of_results"`

View file

@ -14,7 +14,7 @@ import (
"github.com/metamorphosis-dev/kafka/internal/contracts"
)
// BraveEngine implements the SearXNG `braveapi` engine (Brave Web Search API).
// BraveEngine implements the `braveapi` engine (Brave Web Search API).
//
// Config / gating:
// - BRAVE_API_KEY: required to call Brave
@ -35,8 +35,8 @@ func (e *BraveEngine) Search(ctx context.Context, req contracts.SearchRequest) (
return contracts.SearchResponse{}, errors.New("brave engine not initialized")
}
// Gate / config checks should not be treated as fatal errors; SearXNG
// treats misconfigured engines as unresponsive.
// Gate / config checks should not be treated as fatal errors; the reference
// implementation treats misconfigured engines as unresponsive.
if strings.TrimSpace(e.apiKey) == "" {
return contracts.SearchResponse{
Query: req.Query,
@ -93,7 +93,7 @@ func (e *BraveEngine) Search(ctx context.Context, req contracts.SearchRequest) (
}
}
// SearXNG's python checks `if params["safesearch"]:` which treats any
// The reference implementation checks `if params["safesearch"]:` which treats any
// non-zero (moderate/strict) as strict.
if req.Safesearch > 0 {
args.Set("safesearch", "strict")

View file

@ -6,7 +6,7 @@ import (
"github.com/metamorphosis-dev/kafka/internal/contracts"
)
// Engine is a Go-native implementation of a SearXNG engine.
// Engine is a Go-native implementation of a search engine.
//
// Implementations should return a SearchResponse containing only the results
// for that engine subset; the caller will merge multiple engine responses.

View file

@ -48,7 +48,7 @@ func NewPlanner(portedEngines []string) *Planner {
// Plan returns:
// - localEngines: engines that are configured as ported for this service
// - upstreamEngines: engines that should be executed by upstream SearXNG
// - upstreamEngines: engines that should be executed by the upstream instance
// - requestedEngines: the (possibly inferred) requested engines list
//
// If the request provides an explicit `engines` parameter, we use it.
@ -80,7 +80,7 @@ func (p *Planner) Plan(req contracts.SearchRequest) (localEngines, upstreamEngin
func inferFromCategories(categories []string) []string {
// Minimal mapping for the initial porting subset.
// This mirrors the idea of selecting from SearXNG categories without
// This mirrors the idea of selecting from engine categories without
// embedding the whole engine registry.
set := map[string]bool{}
for _, c := range categories {

View file

@ -14,11 +14,11 @@ import (
"github.com/PuerkitoBio/goquery"
)
// QwantEngine implements a SearXNG-like `qwant` (web) adapter using
// QwantEngine implements a `qwant` (web) adapter using
// Qwant v3 endpoint: https://api.qwant.com/v3/search/web.
//
// Qwant's API is not fully documented; this mirrors SearXNG's parsing logic
// for the `web` category from `.agent/searxng/searx/engines/qwant.py`.
// Qwant's API is not fully documented; this implements parsing logic
// for the `web` category.
type QwantEngine struct {
client *http.Client
category string // "web" (JSON API) or "web-lite" (HTML fallback)
@ -37,7 +37,7 @@ func (e *QwantEngine) Search(ctx context.Context, req contracts.SearchRequest) (
return contracts.SearchResponse{Query: req.Query}, nil
}
// For API parity we use SearXNG web defaults: count=10, offset=(pageno-1)*count.
// For API parity we use web defaults: count=10, offset=(pageno-1)*count.
// The engine's config field exists so we can expand to news/images/videos later.
count := e.resultsPerPage
if count <= 0 {
@ -262,7 +262,7 @@ func (e *QwantEngine) searchWebLite(ctx context.Context, req contracts.SearchReq
return
}
// In SearXNG: "./span[contains(@class, 'url partner')]"
// Selector: "./span[contains(@class, 'url partner')]"
urlText := strings.TrimSpace(item.Find("span.url.partner").First().Text())
if urlText == "" {
// fallback: any span with class containing both 'url' and 'partner'

View file

@ -8,7 +8,7 @@ import (
"github.com/metamorphosis-dev/kafka/internal/contracts"
)
// MergeResponses merges multiple SearXNG-compatible JSON responses.
// MergeResponses merges multiple compatible JSON responses.
//
// MVP merge semantics:
// - results are concatenated with a simple de-dup key (engine|title|url)

View file

@ -11,7 +11,7 @@ import (
var languageCodeRe = regexp.MustCompile(`^[a-z]{2,3}(-[a-zA-Z]{2})?$`)
func ParseSearchRequest(r *http.Request) (SearchRequest, error) {
// SearXNG supports both GET and POST and relies on form values for routing.
// Supports both GET and POST and relies on form values for routing.
if err := r.ParseForm(); err != nil {
return SearchRequest{}, errors.New("invalid request: cannot parse form")
}
@ -90,7 +90,7 @@ func ParseSearchRequest(r *http.Request) (SearchRequest, error) {
// engines is an explicit list of engine names.
engines := splitCSV(strings.TrimSpace(r.FormValue("engines")))
// categories and category_<name> params mirror SearXNG's webadapter parsing.
// categories and category_<name> params mirror the webadapter parsing.
// We don't validate against a registry here; we just preserve the requested values.
catSet := map[string]bool{}
if catsParam := strings.TrimSpace(r.FormValue("categories")); catsParam != "" {

View file

@ -38,7 +38,7 @@ func WriteSearchResponse(w http.ResponseWriter, format OutputFormat, resp Search
}
}
// csvRowHeader matches the SearXNG CSV writer key order.
// csvRowHeader matches the CSV writer key order.
var csvRowHeader = []string{"title", "url", "content", "host", "engine", "score", "type"}
func writeCSV(w http.ResponseWriter, resp SearchResponse) error {
@ -111,14 +111,14 @@ func writeCSV(w http.ResponseWriter, resp SearchResponse) error {
func writeRSS(w http.ResponseWriter, resp SearchResponse) error {
q := resp.Query
escapedTitle := xmlEscape("SearXNG search: " + q)
escapedDesc := xmlEscape("Search results for \"" + q + "\" - SearXNG")
escapedTitle := xmlEscape("kafka search: " + q)
escapedDesc := xmlEscape("Search results for \"" + q + "\" - kafka")
escapedQueryTerms := xmlEscape(q)
link := "/search?q=" + url.QueryEscape(q)
opensearchQuery := fmt.Sprintf(`<opensearch:Query role="request" searchTerms="%s" startPage="1" />`, escapedQueryTerms)
// SearXNG template uses the number of results for both totalResults and itemsPerPage.
// The template uses the number of results for both totalResults and itemsPerPage.
nr := resp.NumberOfResults
var items bytes.Buffer

View file

@ -50,7 +50,7 @@ func NewService(cfg ServiceConfig) *Service {
}
// Search executes the request against local engines (in parallel) and
// optionally upstream SearXNG for unported engines.
// optionally the upstream instance for unported engines.
//
// Individual engine failures are reported as unresponsive_engines rather
// than aborting the entire search.

View file

@ -68,7 +68,7 @@ func (c *Client) SearchJSON(ctx context.Context, req contracts.SearchRequest, en
for engineName, kv := range req.EngineData {
for key, value := range kv {
// Mirror SearXNG's naming: `engine_data-<engine>-<key>=<value>`
// Mirror the naming convention: `engine_data-<engine>-<key>=<value>`
form.Set(fmt.Sprintf("engine_data-%s-%s", engineName, key), value)
}
}

View file

@ -1,5 +1,4 @@
/* kafka — clean, minimal search engine CSS */
/* Inspired by SearXNG's simple theme class conventions */
:root {
--color-base: #f5f5f5;