import type { MediaItem } from "@/lib/types"; const TOKEN_KEY = "j621.token"; export function getToken(): string | null { return window.localStorage.getItem(TOKEN_KEY); } export function setToken(token: string | null): void { if (token) { window.localStorage.setItem(TOKEN_KEY, token); } else { window.localStorage.removeItem(TOKEN_KEY); } } export class ApiError extends Error { status: number; data: unknown; constructor(status: number, data: unknown) { super(`Request failed with status ${status}`); this.name = "ApiError"; this.status = status; this.data = data; } } export function errorMessage(error: unknown): string { if (!(error instanceof ApiError)) { return error instanceof Error ? error.message : "Something went wrong."; } const data = error.data; if (data && typeof data === "object") { const record = data as Record; if (typeof record.detail === "string") return record.detail; for (const [field, value] of Object.entries(record)) { if (Array.isArray(value) && value.length > 0) { return `${field}: ${String(value[0])}`; } if (typeof value === "string") { return `${field}: ${value}`; } } } return `Request failed (${error.status}).`; } interface ApiOptions extends Omit { json?: unknown; body?: BodyInit; } export async function api(path: string, options: ApiOptions = {}): Promise { const { json, headers, ...rest } = options; const finalHeaders = new Headers(headers); let body = options.body; if (json !== undefined) { finalHeaders.set("Content-Type", "application/json"); body = JSON.stringify(json); } const token = getToken(); if (token) finalHeaders.set("Authorization", `Token ${token}`); const response = await fetch(path, { ...rest, headers: finalHeaders, body }); if (!response.ok) { let data: unknown = null; try { data = await response.json(); } catch { data = null; } throw new ApiError(response.status, data); } if (response.status === 204) { return undefined as T; } return (await response.json()) as T; } export function uploadFile( file: File, fields: Record = {}, onProgress?: (percent: number) => void, ): Promise { return new Promise((resolve, reject) => { const form = new FormData(); form.append("file", file); for (const [key, value] of Object.entries(fields)) { form.append(key, value); } const request = new XMLHttpRequest(); request.open("POST", "/api/uploads/"); const token = getToken(); if (token) request.setRequestHeader("Authorization", `Token ${token}`); request.upload.addEventListener("progress", (event) => { if (event.lengthComputable && onProgress) { onProgress(Math.round((event.loaded / event.total) * 100)); } }); request.addEventListener("load", () => { let data: unknown = null; try { data = JSON.parse(request.responseText); } catch { data = null; } if (request.status >= 200 && request.status < 300) { resolve(data as MediaItem); } else { reject(new ApiError(request.status, data)); } }); request.addEventListener("error", () => { reject(new ApiError(0, { detail: "Network error" })); }); request.send(form); }); }