- Introduced new endpoints for contact discovery, including scanning, listing, and managing discovered contacts. - Implemented retry logic for handling missing DAV credentials during contact operations. - Added public share functionality for drive API, allowing users to manage public shares, including upload, delete, and rename operations. - Updated Nextcloud configuration to support public share links and improved error handling for public share permissions. - Enhanced logging and validation across contact and drive APIs for better error tracking and user feedback. - Added tests for new contact matching and ranking functionalities to ensure accuracy and reliability.
115 lines
2.6 KiB
Go
115 lines
2.6 KiB
Go
package websearch
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
"net/url"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
var braveSearchURL = "https://api.search.brave.com/res/v1/web/search"
|
|
|
|
type Result struct {
|
|
Title string `json:"title"`
|
|
URL string `json:"url"`
|
|
Description string `json:"description"`
|
|
}
|
|
|
|
type Client struct {
|
|
http *http.Client
|
|
}
|
|
|
|
func NewClient() *Client {
|
|
return &Client{http: &http.Client{Timeout: 15 * time.Second}}
|
|
}
|
|
|
|
type braveSearchResponse struct {
|
|
Web *struct {
|
|
Results []struct {
|
|
Title string `json:"title"`
|
|
URL string `json:"url"`
|
|
Description string `json:"description"`
|
|
} `json:"results"`
|
|
} `json:"web"`
|
|
}
|
|
|
|
func (c *Client) Search(ctx context.Context, provider Provider, query string, count int) ([]Result, error) {
|
|
query = strings.TrimSpace(query)
|
|
if query == "" {
|
|
return nil, fmt.Errorf("search query is required")
|
|
}
|
|
if count <= 0 {
|
|
count = 5
|
|
}
|
|
if count > 20 {
|
|
count = 20
|
|
}
|
|
|
|
switch provider.Type {
|
|
case ProviderBrave:
|
|
return c.searchBrave(ctx, provider.APIKey, query, count)
|
|
default:
|
|
return nil, fmt.Errorf("unsupported search provider type: %s", provider.Type)
|
|
}
|
|
}
|
|
|
|
func (c *Client) searchBrave(ctx context.Context, apiKey, query string, count int) ([]Result, error) {
|
|
apiKey = strings.TrimSpace(apiKey)
|
|
if apiKey == "" {
|
|
return nil, fmt.Errorf("brave api key is required")
|
|
}
|
|
|
|
u, err := url.Parse(braveSearchURL)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
q := u.Query()
|
|
q.Set("q", query)
|
|
q.Set("count", fmt.Sprintf("%d", count))
|
|
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")
|
|
req.Header.Set("Accept-Encoding", "gzip")
|
|
req.Header.Set("X-Subscription-Token", apiKey)
|
|
|
|
resp, err := c.http.Do(req)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
body, err := io.ReadAll(io.LimitReader(resp.Body, 1<<20))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if resp.StatusCode >= 400 {
|
|
return nil, fmt.Errorf("brave search failed (%d): %s", resp.StatusCode, string(body))
|
|
}
|
|
|
|
var parsed braveSearchResponse
|
|
if err := json.Unmarshal(body, &parsed); err != nil {
|
|
return nil, err
|
|
}
|
|
if parsed.Web == nil || len(parsed.Web.Results) == 0 {
|
|
return []Result{}, nil
|
|
}
|
|
|
|
results := make([]Result, 0, len(parsed.Web.Results))
|
|
for _, item := range parsed.Web.Results {
|
|
results = append(results, Result{
|
|
Title: strings.TrimSpace(item.Title),
|
|
URL: strings.TrimSpace(item.URL),
|
|
Description: strings.TrimSpace(item.Description),
|
|
})
|
|
}
|
|
return results, nil
|
|
}
|