// SRC-04: dünner Client für die eigene Backend-for-Frontend-Route // app/api/search/route.ts. Kein direkter Zugriff auf Manticore oder eine // externe Mail-API vom Client aus (Rolle: "Datenzugriff ausschließlich // über die bereitgestellte API"). import type { FacetField } from "./facetFields"; export class ApiError extends Error {} export interface SearchHit { messageId: string; subjectSnippet: string; bodySnippet: string; score: number; } export interface SearchResponse { hits: SearchHit[]; } export interface AppliedFilter { field: FacetField; value: string; } function filterParams(filters: AppliedFilter[]): [string, string][] { return filters.map((f) => ["filter", `${f.field}:${f.value}`]); } // tenantSlug: bis zu einer zentralen Session-/IAM-Anbindung (Core-Board- // Scope, nicht Bestandteil dieser Kachel) wird der Mandant vom Aufrufer // mitgegeben. Die eigentliche Mandantentrennung passiert serverseitig in // mail/internal/search (SRC-01/SRC-03), nicht im Frontend. export async function search(tenantSlug: string, query: string, filters: AppliedFilter[] = []): Promise { const params = new URLSearchParams([["tenant", tenantSlug], ["q", query], ...filterParams(filters)]); const res = await fetch(`/api/search?${params.toString()}`); if (!res.ok) { const body = await res.json().catch(() => ({})); throw new ApiError(body.error ?? `Suche fehlgeschlagen (${res.status})`); } return res.json(); } export interface FacetValue { value: string; count: number; } export interface FacetsResponse { values: Record; } // SRC-06: Facettenzählungen (Akzeptanzkriterium 2: Trefferzahl je // Facettenwert live aktualisiert) — läuft mit denselben Filtern wie // search, damit Zählungen und Trefferliste konsistent bleiben. export async function facets(tenantSlug: string, query: string, filters: AppliedFilter[] = []): Promise { const params = new URLSearchParams([["tenant", tenantSlug], ["q", query], ...filterParams(filters)]); const res = await fetch(`/api/facets?${params.toString()}`); if (!res.ok) { const body = await res.json().catch(() => ({})); throw new ApiError(body.error ?? `Facetten fehlgeschlagen (${res.status})`); } return res.json(); }