Spaces:
Running
Running
File size: 4,154 Bytes
26a0c00 88e04a9 26a0c00 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 | /**
* API client for the FastAPI backend.
* Handles authentication headers and base URL configuration.
*/
const API_BASE = process.env.NEXT_PUBLIC_API_URL || "";
interface FetchOptions extends RequestInit {
token?: string;
}
class ApiClient {
private baseUrl: string;
constructor(baseUrl: string) {
this.baseUrl = baseUrl;
}
private getToken(): string | null {
if (typeof window === "undefined") return null;
return localStorage.getItem("token");
}
private getHeaders(token?: string): HeadersInit {
const headers: HeadersInit = {
"Content-Type": "application/json",
};
const authToken = token || this.getToken();
if (authToken) {
headers["Authorization"] = `Bearer ${authToken}`;
}
return headers;
}
async get<T>(path: string, options?: FetchOptions): Promise<T> {
const res = await fetch(`${this.baseUrl}${path}`, {
method: "GET",
headers: this.getHeaders(options?.token),
...options,
});
if (!res.ok) {
const error = await res.json().catch(() => ({ detail: res.statusText }));
throw new Error(error.detail || "Request failed");
}
return res.json();
}
async post<T>(path: string, body?: unknown, options?: FetchOptions): Promise<T> {
const res = await fetch(`${this.baseUrl}${path}`, {
method: "POST",
headers: this.getHeaders(options?.token),
body: body ? JSON.stringify(body) : undefined,
...options,
});
if (!res.ok) {
const error = await res.json().catch(() => ({ detail: res.statusText }));
throw new Error(error.detail || "Request failed");
}
return res.json();
}
async postForm<T>(path: string, formData: FormData, options?: FetchOptions): Promise<T> {
const token = options?.token || this.getToken();
const headers: HeadersInit = {};
if (token) {
headers["Authorization"] = `Bearer ${token}`;
}
// Don't set Content-Type — browser sets multipart boundary automatically
const res = await fetch(`${this.baseUrl}${path}`, {
method: "POST",
headers,
body: formData,
...options,
});
if (!res.ok) {
const error = await res.json().catch(() => ({ detail: res.statusText }));
throw new Error(error.detail || "Upload failed");
}
return res.json();
}
async delete<T>(path: string, options?: FetchOptions): Promise<T> {
const res = await fetch(`${this.baseUrl}${path}`, {
method: "DELETE",
headers: this.getHeaders(options?.token),
...options,
});
if (!res.ok) {
const error = await res.json().catch(() => ({ detail: res.statusText }));
throw new Error(error.detail || "Delete failed");
}
return res.json();
}
/**
* Stream a POST request as Server-Sent Events.
* Yields parsed SSE data objects.
*/
async *streamPost(path: string, body: unknown): AsyncGenerator<{ type: string; data?: unknown }> {
const res = await fetch(`${this.baseUrl}${path}`, {
method: "POST",
headers: this.getHeaders(),
body: JSON.stringify(body),
});
if (!res.ok) {
const error = await res.json().catch(() => ({ detail: res.statusText }));
throw new Error(error.detail || "Stream request failed");
}
const reader = res.body?.getReader();
if (!reader) throw new Error("No response body");
const decoder = new TextDecoder();
let buffer = "";
while (true) {
const { done, value } = await reader.read();
if (done) break;
buffer += decoder.decode(value, { stream: true });
const lines = buffer.split("\n");
buffer = lines.pop() || "";
for (const line of lines) {
if (line.startsWith("data: ")) {
try {
const data = JSON.parse(line.slice(6));
yield data;
} catch {
// Skip malformed lines
}
}
}
}
}
getPdfUrl(documentId: string): string {
const token = this.getToken();
return `${this.baseUrl}/api/v1/documents/${documentId}/pdf?token=${token}`;
}
}
export const api = new ApiClient(API_BASE);
export { API_BASE };
|