47 lines
1.4 KiB
TypeScript
47 lines
1.4 KiB
TypeScript
import { useAuthStore } from '../auth/auth-store';
|
|
|
|
const API_BASE = '/api';
|
|
|
|
async function apiFetch<T>(path: string, options: RequestInit = {}): Promise<T> {
|
|
const token = useAuthStore.getState().accessToken;
|
|
const headers: Record<string, string> = {
|
|
...(options.headers as Record<string, string> || {}),
|
|
};
|
|
if (token) {
|
|
headers['Authorization'] = `Bearer ${token}`;
|
|
}
|
|
if (!headers['Content-Type'] && !(options.body instanceof FormData)) {
|
|
headers['Content-Type'] = 'application/json';
|
|
}
|
|
|
|
const response = await fetch(`${API_BASE}${path}`, { ...options, headers });
|
|
|
|
if (response.status === 401) {
|
|
useAuthStore.getState().logout();
|
|
window.location.href = '/login';
|
|
throw new Error('Unauthorized');
|
|
}
|
|
|
|
if (!response.ok) {
|
|
const text = await response.text();
|
|
throw new Error(`API error ${response.status}: ${text}`);
|
|
}
|
|
|
|
if (response.status === 204) return undefined as T;
|
|
return response.json();
|
|
}
|
|
|
|
export const api = {
|
|
get: <T>(path: string) => apiFetch<T>(path),
|
|
post: <T>(path: string, body?: unknown) =>
|
|
apiFetch<T>(path, {
|
|
method: 'POST',
|
|
body: body instanceof FormData ? body : JSON.stringify(body),
|
|
}),
|
|
patch: <T>(path: string, body: unknown) =>
|
|
apiFetch<T>(path, { method: 'PATCH', body: JSON.stringify(body) }),
|
|
put: <T>(path: string, body: FormData) =>
|
|
apiFetch<T>(path, { method: 'PUT', body }),
|
|
delete: <T>(path: string) => apiFetch<T>(path, { method: 'DELETE' }),
|
|
};
|