ultisuite-client/lib/web-search/search-provider-catalog.ts
R3D347HR4Y 9e9fd208ad
Some checks are pending
E2E / Playwright e2e (push) Waiting to run
feat(admin-settings): enhance admin settings with new components and layout improvements
- Introduced new components for managing admin settings, including AdminListControls, AdminSettingsCard, and TechBrandSelectLabel.
- Implemented dynamic loading for admin settings sections to optimize performance.
- Enhanced the layout of various admin settings sections for better user experience.
- Updated the AiAssistantSection to include LLM provider management and improved model selection.
- Refactored authentication settings to streamline configuration and improve accessibility.
2026-06-15 00:22:20 +02:00

174 lines
4.7 KiB
TypeScript

import type { ApiSearchProvider, ApiSearchSettings } from "@/lib/contacts/discovery-types"
import { techBrandIcon } from "@/lib/admin-settings/tech-brand-icons"
export type ApiSearchProviderType =
| "brave"
| "bing"
| "duckduckgo"
| "searxng"
| "custom"
export type SearchProviderCatalogEntry = {
type: ApiSearchProviderType
label: string
description: string
icon?: string
docsUrl?: string
requiresApiKey: boolean
requiresBaseURL: boolean
supportsCustomMapping: boolean
}
export const SEARCH_PROVIDER_CATALOG: SearchProviderCatalogEntry[] = [
{
type: "brave",
label: "Brave Search",
description: "API officielle Brave (X-Subscription-Token).",
icon: techBrandIcon("brave"),
docsUrl: "https://api.search.brave.com",
requiresApiKey: true,
requiresBaseURL: false,
supportsCustomMapping: false,
},
{
type: "bing",
label: "Bing Web Search",
description: "Azure Cognitive Services Bing Search v7.",
icon: techBrandIcon("bing"),
docsUrl: "https://learn.microsoft.com/azure/cognitive-services/bing-web-search/",
requiresApiKey: true,
requiresBaseURL: false,
supportsCustomMapping: false,
},
{
type: "duckduckgo",
label: "DuckDuckGo",
description: "Scraping HTML léger, sans clé API (best-effort).",
icon: techBrandIcon("duckduckgo"),
requiresApiKey: false,
requiresBaseURL: false,
supportsCustomMapping: false,
},
{
type: "searxng",
label: "SearXNG",
description: "Instance SearXNG auto-hébergée (format JSON).",
icon: techBrandIcon("searxng"),
docsUrl: "https://docs.searxng.org/",
requiresApiKey: false,
requiresBaseURL: true,
supportsCustomMapping: false,
},
{
type: "custom",
label: "API JSON personnalisée",
description: "Endpoint GET JSON avec mapping des champs résultats.",
icon: techBrandIcon("custom"),
requiresApiKey: false,
requiresBaseURL: true,
supportsCustomMapping: true,
},
]
export function catalogEntry(type: ApiSearchProviderType): SearchProviderCatalogEntry {
return (
SEARCH_PROVIDER_CATALOG.find((entry) => entry.type === type) ??
SEARCH_PROVIDER_CATALOG[0]
)
}
export function emptySearchProvider(type: ApiSearchProviderType = "brave"): ApiSearchProvider {
const entry = catalogEntry(type)
return {
id: crypto.randomUUID(),
name: entry.label,
type,
api_key: "",
base_url: defaultBaseURL(type),
query_param: "q",
auth_header: defaultAuthHeader(type),
results_path: type === "custom" ? "results" : "",
title_field: type === "custom" ? "title" : "",
url_field: type === "custom" ? "url" : "",
description_field: type === "custom" ? "description" : "",
}
}
function defaultBaseURL(type: ApiSearchProviderType): string {
switch (type) {
case "bing":
return "https://api.bing.microsoft.com/v7.0/search"
case "searxng":
return "https://searx.example.org"
case "custom":
return "https://api.example.com/search"
default:
return ""
}
}
function defaultAuthHeader(type: ApiSearchProviderType): string {
switch (type) {
case "bing":
return "Ocp-Apim-Subscription-Key"
default:
return ""
}
}
export function ensureSearchSettingsDefaults(settings: ApiSearchSettings): ApiSearchSettings {
const providers = settings.providers ?? []
let defaultId = settings.default_provider_id ?? ""
if (!defaultId && providers.length === 1) {
defaultId = providers[0]?.id ?? ""
}
if (defaultId && !providers.some((provider) => provider.id === defaultId)) {
defaultId = providers[0]?.id ?? ""
}
return {
...settings,
default_provider_id: defaultId,
providers,
}
}
export function normalizeSearchProviders(raw: ApiSearchSettings | undefined): ApiSearchSettings {
const providers = raw?.providers?.length
? raw.providers.map((provider) => ({
...emptySearchProvider(provider.type ?? "brave"),
...provider,
type: provider.type ?? "brave",
}))
: []
return ensureSearchSettingsDefaults({
default_provider_id: raw?.default_provider_id || providers[0]?.id || "",
providers,
})
}
export function isSearchProviderConfigured(
provider: ApiSearchProvider,
options?: { apiKeyConfigured?: boolean },
): boolean {
switch (provider.type) {
case "brave":
case "bing":
return Boolean(provider.api_key?.trim() || options?.apiKeyConfigured)
case "duckduckgo":
return true
case "searxng":
return Boolean(provider.base_url?.trim())
case "custom":
return Boolean(
provider.base_url?.trim() &&
provider.results_path?.trim() &&
provider.title_field?.trim() &&
provider.url_field?.trim(),
)
default:
return false
}
}