Changeset 0.13.1 (#65)
This commit is contained in:
103
server/tools/search/provider.go
Normal file
103
server/tools/search/provider.go
Normal file
@@ -0,0 +1,103 @@
|
||||
package search
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// SearchResult represents a single web search result.
|
||||
type SearchResult struct {
|
||||
Title string `json:"title"`
|
||||
URL string `json:"url"`
|
||||
Snippet string `json:"snippet"`
|
||||
}
|
||||
|
||||
// Provider is the interface for web search backends.
|
||||
type Provider interface {
|
||||
// Search performs a web search and returns up to maxResults results.
|
||||
Search(ctx context.Context, query string, maxResults int) ([]SearchResult, error)
|
||||
|
||||
// Name returns the provider identifier (e.g. "duckduckgo", "searxng").
|
||||
Name() string
|
||||
}
|
||||
|
||||
// ── Provider Registry ───────────────────────
|
||||
|
||||
var (
|
||||
mu sync.RWMutex
|
||||
providers = map[string]Provider{}
|
||||
active string
|
||||
)
|
||||
|
||||
// RegisterProvider adds a search provider to the registry.
|
||||
func RegisterProvider(p Provider) {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
providers[p.Name()] = p
|
||||
log.Printf("🔍 Registered search provider: %s", p.Name())
|
||||
}
|
||||
|
||||
// SetActive sets the active search provider by name.
|
||||
func SetActive(name string) error {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
if _, ok := providers[name]; !ok {
|
||||
return fmt.Errorf("unknown search provider: %s", name)
|
||||
}
|
||||
active = name
|
||||
log.Printf("🔍 Active search provider: %s", name)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Active returns the currently active provider, or nil if none configured.
|
||||
func Active() Provider {
|
||||
mu.RLock()
|
||||
defer mu.RUnlock()
|
||||
if active == "" {
|
||||
return nil
|
||||
}
|
||||
return providers[active]
|
||||
}
|
||||
|
||||
// Available returns the names of all registered providers.
|
||||
func Available() []string {
|
||||
mu.RLock()
|
||||
defer mu.RUnlock()
|
||||
names := make([]string, 0, len(providers))
|
||||
for name := range providers {
|
||||
names = append(names, name)
|
||||
}
|
||||
return names
|
||||
}
|
||||
|
||||
// Config holds the persisted search configuration.
|
||||
type Config struct {
|
||||
Provider string `json:"provider"` // "duckduckgo" or "searxng"
|
||||
Endpoint string `json:"endpoint"` // SearXNG endpoint URL
|
||||
APIKey string `json:"api_key"` // SearXNG API key (optional)
|
||||
MaxResults int `json:"max_results"` // default results per search
|
||||
}
|
||||
|
||||
// DefaultConfig returns the default search config (DuckDuckGo, no key needed).
|
||||
func DefaultConfig() Config {
|
||||
return Config{Provider: "duckduckgo", MaxResults: 5}
|
||||
}
|
||||
|
||||
// ApplyConfig applies a Config to the active provider registry.
|
||||
// Called on startup and when admin saves settings.
|
||||
func ApplyConfig(cfg Config) error {
|
||||
if cfg.Provider == "" {
|
||||
cfg.Provider = "duckduckgo"
|
||||
}
|
||||
|
||||
// Configure SearXNG if selected
|
||||
mu.RLock()
|
||||
if sxng, ok := providers["searxng"].(*SearXNG); ok {
|
||||
sxng.Configure(cfg.Endpoint, cfg.APIKey)
|
||||
}
|
||||
mu.RUnlock()
|
||||
|
||||
return SetActive(cfg.Provider)
|
||||
}
|
||||
Reference in New Issue
Block a user