package websearch import ( "context" "encoding/json" "fmt" "net/http" "net/url" "strings" ) type searxNGResponse struct { Results []struct { Title string `json:"title"` URL string `json:"url"` Content string `json:"content"` } `json:"results"` } func (c *Client) searchSearXNG(ctx context.Context, provider Provider, query string, count int) ([]Result, error) { base := strings.TrimRight(strings.TrimSpace(provider.BaseURL), "/") if base == "" { return nil, fmt.Errorf("searxng base url is required") } u, err := url.Parse(base + "/search") if err != nil { return nil, err } q := u.Query() q.Set(queryParamName(provider), query) q.Set("format", "json") q.Set("categories", "general") u.RawQuery = q.Encode() req, err := http.NewRequestWithContext(ctx, http.MethodGet, u.String(), nil) if err != nil { return nil, err } req.Header.Set("Accept", "application/json") if apiKey := strings.TrimSpace(provider.APIKey); apiKey != "" { header := strings.TrimSpace(provider.AuthHeader) if header == "" { header = "Authorization" } if strings.EqualFold(header, "Authorization") { req.Header.Set(header, "Bearer "+apiKey) } else { req.Header.Set(header, apiKey) } } body, status, err := c.doRequest(req) if err != nil { return nil, err } if status >= 400 { return nil, fmt.Errorf("searxng search failed (%d): %s", status, string(body)) } var parsed searxNGResponse if err := json.Unmarshal(body, &parsed); err != nil { return nil, err } if len(parsed.Results) == 0 { return []Result{}, nil } limit := count if len(parsed.Results) < limit { limit = len(parsed.Results) } results := make([]Result, 0, limit) for _, item := range parsed.Results[:limit] { results = append(results, Result{ Title: strings.TrimSpace(item.Title), URL: strings.TrimSpace(item.URL), Description: strings.TrimSpace(item.Content), }) } return results, nil }