// SRC-06: Backend-for-Frontend-Route für Facettenzählungen. Spiegelt // mail/internal/search/facets.go (Client.Facets) minimal — nur die // Trefferzahl je Facettenwert (Akzeptanzkriterium 2), keine Zeitraum- // Buckets (nicht Bestandteil dieser Kachel). Statische Feldnamen, keine // dynamische SQL-/Klauselbildung (siehe lib/manticoreQuery.ts). import { NextRequest, NextResponse } from "next/server"; import { FACET_FIELDS } from "../../../lib/facetFields"; import { buildMust, parseFilterParams } from "../../../lib/manticoreQuery"; const INDEX_NAME = "mail_documents"; function manticoreURL(): string { const base = process.env.MANTICORE_URL; if (!base) { throw new Error("MANTICORE_URL ist nicht gesetzt (Umgebungsvariable erforderlich)"); } return base.replace(/\/$/, ""); } interface ManticoreFacetResponse { aggregations?: Record; error?: string; } export async function GET(request: NextRequest) { const tenantSlug = request.nextUrl.searchParams.get("tenant"); const query = request.nextUrl.searchParams.get("q") ?? ""; if (!tenantSlug) { return NextResponse.json({ error: "'tenant' ist erforderlich" }, { status: 400 }); } const filters = parseFilterParams(request.nextUrl.searchParams.getAll("filter")); const aggs: Record = {}; for (const field of FACET_FIELDS) { aggs[field] = { terms: { field, size: 100 } }; } const manticorePayload = { index: INDEX_NAME, query: { bool: { must: buildMust(tenantSlug, query, filters) } }, aggs, limit: 0, }; let manticoreResponse: Response; try { manticoreResponse = await fetch(`${manticoreURL()}/search`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(manticorePayload), }); } catch (err) { return NextResponse.json({ error: `Facetten nicht erreichbar: ${(err as Error).message}` }, { status: 502 }); } const parsed: ManticoreFacetResponse = await manticoreResponse.json().catch(() => ({})); if (!manticoreResponse.ok || parsed.error) { return NextResponse.json({ error: parsed.error ?? "Facetten fehlgeschlagen" }, { status: 502 }); } const values: Record = {}; for (const field of FACET_FIELDS) { const buckets = parsed.aggregations?.[field]?.buckets ?? []; values[field] = buckets.filter((b) => b.key !== "").map((b) => ({ value: b.key, count: b.doc_count })); } return NextResponse.json({ values }); }