import { createClient } from '@/lib/supabase/client'; import { handleApiError, handleNetworkError, ErrorContext, ApiError } from './error-handler'; const API_URL = process.env.NEXT_PUBLIC_BACKEND_URL || ''; export interface ApiClientOptions { showErrors?: boolean; errorContext?: ErrorContext; timeout?: number; } export interface ApiResponse { data?: T; error?: ApiError; success: boolean; } export const apiClient = { async request( url: string, options: RequestInit & ApiClientOptions = {} ): Promise> { const { showErrors = true, errorContext, timeout = 30000, ...fetchOptions } = options; try { const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), timeout); const supabase = createClient(); const { data: { session } } = await supabase.auth.getSession(); const headers: Record = { 'Content-Type': 'application/json', ...fetchOptions.headers as Record, }; if (session?.access_token) { headers['Authorization'] = `Bearer ${session.access_token}`; } const response = await fetch(url, { ...fetchOptions, headers, signal: controller.signal, }); clearTimeout(timeoutId); if (!response.ok) { const error: ApiError = new Error(`HTTP ${response.status}: ${response.statusText}`); error.status = response.status; error.response = response; try { const errorData = await response.json(); error.details = errorData; if (errorData.message) { error.message = errorData.message; } } catch { } if (showErrors) { handleApiError(error, errorContext); } return { error, success: false, }; } let data: T; const contentType = response.headers.get('content-type'); if (contentType?.includes('application/json')) { data = await response.json(); } else if (contentType?.includes('text/')) { data = await response.text() as T; } else { data = await response.blob() as T; } return { data, success: true, }; } catch (error: any) { const apiError: ApiError = error instanceof Error ? error : new Error(String(error)); if (error.name === 'AbortError') { apiError.message = 'Request timeout'; apiError.code = 'TIMEOUT'; } if (showErrors) { handleNetworkError(apiError, errorContext); } return { error: apiError, success: false, }; } }, get: async ( url: string, options: Omit = {} ): Promise> => { return apiClient.request(url, { ...options, method: 'GET', }); }, post: async ( url: string, data?: any, options: Omit = {} ): Promise> => { return apiClient.request(url, { ...options, method: 'POST', body: data ? JSON.stringify(data) : undefined, }); }, put: async ( url: string, data?: any, options: Omit = {} ): Promise> => { return apiClient.request(url, { ...options, method: 'PUT', body: data ? JSON.stringify(data) : undefined, }); }, patch: async ( url: string, data?: any, options: Omit = {} ): Promise> => { return apiClient.request(url, { ...options, method: 'PATCH', body: data ? JSON.stringify(data) : undefined, }); }, delete: async ( url: string, options: Omit = {} ): Promise> => { return apiClient.request(url, { ...options, method: 'DELETE', }); }, upload: async ( url: string, formData: FormData, options: Omit = {} ): Promise> => { const { headers, ...restOptions } = options; const uploadHeaders = { ...headers as Record }; delete uploadHeaders['Content-Type']; return apiClient.request(url, { ...restOptions, method: 'POST', body: formData, headers: uploadHeaders, }); }, }; export const supabaseClient = { async execute( queryFn: () => Promise<{ data: T | null; error: any }>, errorContext?: ErrorContext ): Promise> { try { const { data, error } = await queryFn(); if (error) { const apiError: ApiError = new Error(error.message || 'Database error'); apiError.code = error.code; apiError.details = error; handleApiError(apiError, errorContext); return { error: apiError, success: false, }; } return { data: data as T, success: true, }; } catch (error: any) { const apiError: ApiError = error instanceof Error ? error : new Error(String(error)); handleApiError(apiError, errorContext); return { error: apiError, success: false, }; } }, }; export const backendApi = { get: (endpoint: string, options?: Omit) => apiClient.get(`${API_URL}${endpoint}`, options), post: (endpoint: string, data?: any, options?: Omit) => apiClient.post(`${API_URL}${endpoint}`, data, options), put: (endpoint: string, data?: any, options?: Omit) => apiClient.put(`${API_URL}${endpoint}`, data, options), patch: (endpoint: string, data?: any, options?: Omit) => apiClient.patch(`${API_URL}${endpoint}`, data, options), delete: (endpoint: string, options?: Omit) => apiClient.delete(`${API_URL}${endpoint}`, options), upload: (endpoint: string, formData: FormData, options?: Omit) => apiClient.upload(`${API_URL}${endpoint}`, formData, options), };