66 lines
2.3 KiB
TypeScript
66 lines
2.3 KiB
TypeScript
// frontend/src/services/api.ts
|
|
|
|
type HttpMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
|
|
|
|
const getBackendHost = (): string => {
|
|
const host: string = window.location.hostname || 'localhost';
|
|
return window.location.protocol === 'https:' ? host : `${host}:8000`;
|
|
};
|
|
|
|
export const API_BASE: string = `${window.location.protocol}//${getBackendHost()}/api`;
|
|
export const WS_URL: string = `${window.location.protocol === 'https:' ? 'wss:' : 'ws:'}//${getBackendHost()}/api/ws`;
|
|
|
|
export const getToken = (): string | null => localStorage.getItem('volleyToken');
|
|
|
|
const api = {
|
|
request: async <T>(
|
|
method: HttpMethod,
|
|
url: string,
|
|
data: unknown = null,
|
|
isFormData: boolean = false
|
|
): Promise<T> => {
|
|
const headers: Record<string, string> = {};
|
|
const token = getToken();
|
|
|
|
if (token) headers['Authorization'] = `Bearer ${token}`;
|
|
if (!isFormData) headers['Content-Type'] = 'application/json';
|
|
|
|
const opts: RequestInit = {
|
|
method,
|
|
headers
|
|
};
|
|
|
|
if (data) {
|
|
opts.body = isFormData ? (data as FormData) : JSON.stringify(data);
|
|
}
|
|
|
|
// Ensure clean URL concatenation
|
|
const baseUrl = API_BASE.replace(/\/$/, '');
|
|
const endpoint = url.startsWith('/') ? url : `/${url}`;
|
|
|
|
const res = await fetch(`${baseUrl}${endpoint}`, opts);
|
|
|
|
if (!res.ok) {
|
|
if (res.status === 401) {
|
|
localStorage.removeItem('volleyToken');
|
|
if (window.location.pathname !== '/login') {
|
|
window.location.href = '/login';
|
|
}
|
|
}
|
|
// Attempt to parse error detail, fallback to generic message
|
|
const errorData = await res.json().catch(() => ({ detail: 'An error occurred' }));
|
|
throw errorData;
|
|
}
|
|
|
|
return res.json() as Promise<T>;
|
|
},
|
|
|
|
get: <T>(url: string) => api.request<T>('GET', url),
|
|
post: <T>(url: string, data?: unknown) => api.request<T>('POST', url, data),
|
|
postForm: <T>(url: string, data: FormData) => api.request<T>('POST', url, data, true),
|
|
put: <T>(url: string, data?: unknown) => api.request<T>('PUT', url, data),
|
|
patch: <T>(url: string, data?: unknown) => api.request<T>('PATCH', url, data),
|
|
delete: <T>(url: string) => api.request<T>('DELETE', url)
|
|
};
|
|
|
|
export default api; |