mirror of
https://git.victorphan.net/basketballcantho/ten-project.git
synced 2026-08-17 05:25:59 +07:00
40 lines
1.5 KiB
TypeScript
40 lines
1.5 KiB
TypeScript
|
|
/**
|
||
|
|
* Thin fetch wrapper — always sends cookies (HttpOnly JWT).
|
||
|
|
* All paths are relative so Next.js rewrites proxy them to the backend.
|
||
|
|
*/
|
||
|
|
|
||
|
|
async function request<T>(path: string, init: RequestInit = {}): Promise<T> {
|
||
|
|
const res = await fetch(path, {
|
||
|
|
...init,
|
||
|
|
credentials: "include",
|
||
|
|
headers: {
|
||
|
|
...(init.body instanceof FormData ? {} : { "Content-Type": "application/json" }),
|
||
|
|
...init.headers,
|
||
|
|
},
|
||
|
|
});
|
||
|
|
|
||
|
|
if (!res.ok) {
|
||
|
|
const detail = await res.json().catch(() => ({ detail: res.statusText }));
|
||
|
|
throw new Error(detail?.detail ?? "Request failed");
|
||
|
|
}
|
||
|
|
|
||
|
|
if (res.status === 204) return undefined as T;
|
||
|
|
return res.json();
|
||
|
|
}
|
||
|
|
|
||
|
|
export const api = {
|
||
|
|
// Auth
|
||
|
|
me: () => request<import("@/types").User>("/api/auth/me"),
|
||
|
|
login: (body: { username: string; password: string }) =>
|
||
|
|
request<import("@/types").User>("/api/auth/login", { method: "POST", body: JSON.stringify(body) }),
|
||
|
|
register: (body: { username: string; email: string; password: string }) =>
|
||
|
|
request<import("@/types").User>("/api/auth/register", { method: "POST", body: JSON.stringify(body) }),
|
||
|
|
logout: () => request<void>("/api/auth/logout", { method: "POST" }),
|
||
|
|
|
||
|
|
// PDFs
|
||
|
|
listPdfs: () => request<import("@/types").PDFItem[]>("/api/pdfs"),
|
||
|
|
uploadPdfs: (formData: FormData) =>
|
||
|
|
request<import("@/types").PDFUploadResult[]>("/api/pdfs/upload", { method: "POST", body: formData }),
|
||
|
|
deletePdf: (id: number) => request<void>(`/api/pdfs/${id}`, { method: "DELETE" }),
|
||
|
|
};
|