import axios, { AxiosInstance, AxiosRequestConfig } from 'axios' import { ApiResponse } from '../../../shared/types' class ApiService { private api: AxiosInstance constructor() { this.api = axios.create({ baseURL: import.meta.env.VITE_API_URL || 'http://localhost:3001', timeout: 10000, headers: { 'Content-Type': 'application/json', }, }) // Request interceptor to add auth token this.api.interceptors.request.use( (config) => { const token = this.getToken() if (token) { config.headers.Authorization = `Bearer ${token}` } return config }, (error) => { return Promise.reject(error) } ) // Response interceptor to handle errors this.api.interceptors.response.use( (response) => { return response }, (error) => { if (error.response?.status === 401) { // Token expired or invalid this.clearToken() window.location.href = '/login' } return Promise.reject(this.handleError(error)) } ) } private getToken(): string | null { try { const authStorage = localStorage.getItem('auth-storage') if (authStorage) { const parsed = JSON.parse(authStorage) return parsed.state?.token || null } } catch (error) { console.error('Error getting token:', error) } return null } private clearToken(): void { try { localStorage.removeItem('auth-storage') } catch (error) { console.error('Error clearing token:', error) } } private handleError(error: any): Error { if (error.response) { // Server responded with error status const message = error.response.data?.message || error.response.data?.error || 'Server error' return new Error(message) } else if (error.request) { // Request was made but no response received return new Error('Network error - please check your connection') } else { // Something else happened return new Error(error.message || 'An unexpected error occurred') } } // Generic request methods async get(url: string, config?: AxiosRequestConfig): Promise { const response = await this.api.get>(url, config) if (!response.data.success) { throw new Error(response.data.error || 'Request failed') } return response.data.data! } async post(url: string, data?: any, config?: AxiosRequestConfig): Promise { const response = await this.api.post>(url, data, config) if (!response.data.success) { throw new Error(response.data.error || 'Request failed') } return response.data.data! } async put(url: string, data?: any, config?: AxiosRequestConfig): Promise { const response = await this.api.put>(url, data, config) if (!response.data.success) { throw new Error(response.data.error || 'Request failed') } return response.data.data! } async patch(url: string, data?: any, config?: AxiosRequestConfig): Promise { const response = await this.api.patch>(url, data, config) if (!response.data.success) { throw new Error(response.data.error || 'Request failed') } return response.data.data! } async delete(url: string, config?: AxiosRequestConfig): Promise { const response = await this.api.delete>(url, config) if (!response.data.success) { throw new Error(response.data.error || 'Request failed') } return response.data.data! } // File upload method async upload(url: string, file: File, onProgress?: (progress: number) => void): Promise { const formData = new FormData() formData.append('file', file) const config: AxiosRequestConfig = { headers: { 'Content-Type': 'multipart/form-data', }, onUploadProgress: (progressEvent) => { if (onProgress && progressEvent.total) { const progress = Math.round((progressEvent.loaded * 100) / progressEvent.total) onProgress(progress) } }, } const response = await this.api.post>(url, formData, config) if (!response.data.success) { throw new Error(response.data.error || 'Upload failed') } return response.data.data! } // Multiple file upload method async uploadMultiple( url: string, files: File[], onProgress?: (progress: number) => void ): Promise { const formData = new FormData() files.forEach((file, index) => { formData.append(`files[${index}]`, file) }) const config: AxiosRequestConfig = { headers: { 'Content-Type': 'multipart/form-data', }, onUploadProgress: (progressEvent) => { if (onProgress && progressEvent.total) { const progress = Math.round((progressEvent.loaded * 100) / progressEvent.total) onProgress(progress) } }, } const response = await this.api.post>(url, formData, config) if (!response.data.success) { throw new Error(response.data.error || 'Upload failed') } return response.data.data! } // Get raw axios instance for custom requests getAxiosInstance(): AxiosInstance { return this.api } } export const apiService = new ApiService()