import type { ChatResponse, DeleteResponse, DocumentsResponse, IngestResponse, } from "./types"; // FastAPI backend base URL for the admin endpoints (called directly from the // browser; the backend enables CORS for this origin). Exposed to the client, // so it must be prefixed with NEXT_PUBLIC_. const API_BASE = ( process.env.NEXT_PUBLIC_API_URL || "http://127.0.0.1:8080" ).replace(/\/+$/, ""); const configuredIngestTimeoutMs = Number( process.env.NEXT_PUBLIC_INGEST_TIMEOUT_MS || 600_000, ); const INGEST_TIMEOUT_MS = Number.isFinite(configuredIngestTimeoutMs) && configuredIngestTimeoutMs > 0 ? configuredIngestTimeoutMs : 600_000; /** Parse a JSON response, throwing a readable error on failure. */ async function jsonOrThrow(res: Response): Promise { if (!res.ok) { let message = `Yêu cầu thất bại (mã ${res.status})`; try { const body = await res.json(); // Next.js routes use { error }, FastAPI uses { detail }. message = body?.error || body?.detail || message; } catch { // non-JSON error body — keep the status message } throw new Error(message); } return res.json() as Promise; } /** Chat goes through the Next.js proxy (`/api/chat` → backend `POST /chat`). * Accepts an optional AbortSignal so the store can cancel an in-flight request * when its session is deleted. */ export async function sendChat( input: { user_id: string; session_id: string; query: string; }, signal?: AbortSignal, ): Promise { const res = await fetch("/api/chat", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(input), signal, }); return jsonOrThrow(res); } export async function listDocuments(): Promise { const res = await fetch(`${API_BASE}/documents`, { cache: "no-store" }); return jsonOrThrow(res); } export async function ingestPdf(file: File): Promise { const form = new FormData(); form.append("file", file); try { const res = await fetch(`${API_BASE}/ingest`, { method: "POST", body: form, signal: AbortSignal.timeout(INGEST_TIMEOUT_MS), }); return jsonOrThrow(res); } catch (err) { if (err instanceof Error && err.name === "TimeoutError") { const minutes = Math.round(INGEST_TIMEOUT_MS / 60_000); throw new Error( `Backend xử lý upload quá lâu (quá ${minutes} phút). Thử tăng NEXT_PUBLIC_INGEST_TIMEOUT_MS nếu tệp lớn hoặc Qdrant Cloud chậm.`, ); } throw err; } } export async function deleteDocument(id: string): Promise { const res = await fetch(`${API_BASE}/documents/${encodeURIComponent(id)}`, { method: "DELETE", }); return jsonOrThrow(res); }