Some checks are pending
E2E / Playwright e2e (push) Waiting to run
- 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.
174 lines
4.7 KiB
TypeScript
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
|
|
}
|
|
}
|