|
|
import axios, { AxiosInstance, AxiosRequestConfig } from 'axios' |
|
|
import { getSession } from 'next-auth/react' |
|
|
|
|
|
class APIClient { |
|
|
private client: AxiosInstance |
|
|
private baseURL: string |
|
|
|
|
|
constructor() { |
|
|
this.baseURL = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:8000' |
|
|
|
|
|
this.client = axios.create({ |
|
|
baseURL: this.baseURL, |
|
|
timeout: 30000, |
|
|
headers: { |
|
|
'Content-Type': 'application/json', |
|
|
}, |
|
|
}) |
|
|
|
|
|
|
|
|
this.client.interceptors.request.use( |
|
|
async (config) => { |
|
|
const session = await getSession() |
|
|
if (session?.accessToken) { |
|
|
config.headers.Authorization = `Bearer ${session.accessToken}` |
|
|
} |
|
|
return config |
|
|
}, |
|
|
(error) => Promise.reject(error) |
|
|
) |
|
|
|
|
|
|
|
|
this.client.interceptors.response.use( |
|
|
(response) => response, |
|
|
async (error) => { |
|
|
if (error.response?.status === 401) { |
|
|
|
|
|
window.location.href = '/login' |
|
|
} |
|
|
return Promise.reject(error) |
|
|
} |
|
|
) |
|
|
} |
|
|
|
|
|
|
|
|
async request<T>(config: AxiosRequestConfig): Promise<T> { |
|
|
const response = await this.client.request<T>(config) |
|
|
return response.data |
|
|
} |
|
|
|
|
|
|
|
|
async get<T>(url: string, config?: AxiosRequestConfig): Promise<T> { |
|
|
return this.request<T>({ ...config, method: 'GET', url }) |
|
|
} |
|
|
|
|
|
async post<T>(url: string, data?: any, config?: AxiosRequestConfig): Promise<T> { |
|
|
return this.request<T>({ ...config, method: 'POST', url, data }) |
|
|
} |
|
|
|
|
|
async put<T>(url: string, data?: any, config?: AxiosRequestConfig): Promise<T> { |
|
|
return this.request<T>({ ...config, method: 'PUT', url, data }) |
|
|
} |
|
|
|
|
|
async delete<T>(url: string, config?: AxiosRequestConfig): Promise<T> { |
|
|
return this.request<T>({ ...config, method: 'DELETE', url }) |
|
|
} |
|
|
|
|
|
|
|
|
async upload<T>( |
|
|
url: string, |
|
|
file: File, |
|
|
onProgress?: (progress: number) => void |
|
|
): Promise<T> { |
|
|
const formData = new FormData() |
|
|
formData.append('file', file) |
|
|
|
|
|
return this.request<T>({ |
|
|
method: 'POST', |
|
|
url, |
|
|
data: formData, |
|
|
headers: { |
|
|
'Content-Type': 'multipart/form-data', |
|
|
}, |
|
|
onUploadProgress: (progressEvent) => { |
|
|
if (onProgress && progressEvent.total) { |
|
|
const progress = Math.round( |
|
|
(progressEvent.loaded * 100) / progressEvent.total |
|
|
) |
|
|
onProgress(progress) |
|
|
} |
|
|
}, |
|
|
}) |
|
|
} |
|
|
|
|
|
|
|
|
createWebSocket(path: string): WebSocket { |
|
|
const wsURL = this.baseURL.replace(/^http/, 'ws') |
|
|
return new WebSocket(`${wsURL}${path}`) |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
export const apiClient = new APIClient() |
|
|
|
|
|
|
|
|
export const api = { |
|
|
|
|
|
auth: { |
|
|
login: (credentials: { email: string; password: string }) => |
|
|
apiClient.post<{ token: string; user: any }>('/auth/login', credentials), |
|
|
|
|
|
register: (data: { email: string; password: string; name: string }) => |
|
|
apiClient.post<{ token: string; user: any }>('/auth/register', data), |
|
|
|
|
|
logout: () => apiClient.post('/auth/logout'), |
|
|
|
|
|
refreshToken: () => |
|
|
apiClient.post<{ token: string }>('/auth/refresh'), |
|
|
}, |
|
|
|
|
|
|
|
|
processing: { |
|
|
removeBackground: (file: File, options?: any, onProgress?: (p: number) => void) => |
|
|
apiClient.upload<{ id: string; result: string }>('/process/remove-background', file, onProgress), |
|
|
|
|
|
replaceBackground: (imageId: string, backgroundId: string) => |
|
|
apiClient.post<{ result: string }>('/process/replace-background', { |
|
|
imageId, |
|
|
backgroundId, |
|
|
}), |
|
|
|
|
|
batchProcess: (files: File[], options?: any) => { |
|
|
const formData = new FormData() |
|
|
files.forEach((file) => formData.append('files', file)) |
|
|
if (options) { |
|
|
formData.append('options', JSON.stringify(options)) |
|
|
} |
|
|
return apiClient.post<{ jobId: string }>('/process/batch', formData, { |
|
|
headers: { 'Content-Type': 'multipart/form-data' }, |
|
|
}) |
|
|
}, |
|
|
|
|
|
getJobStatus: (jobId: string) => |
|
|
apiClient.get<{ status: string; progress: number; results?: any[] }>( |
|
|
`/process/jobs/${jobId}` |
|
|
), |
|
|
}, |
|
|
|
|
|
|
|
|
projects: { |
|
|
list: (params?: { page?: number; limit?: number; search?: string }) => |
|
|
apiClient.get<{ items: any[]; total: number }>('/projects', { params }), |
|
|
|
|
|
get: (id: string) => |
|
|
apiClient.get<any>(`/projects/${id}`), |
|
|
|
|
|
create: (data: any) => |
|
|
apiClient.post<any>('/projects', data), |
|
|
|
|
|
update: (id: string, data: any) => |
|
|
apiClient.put<any>(`/projects/${id}`, data), |
|
|
|
|
|
delete: (id: string) => |
|
|
apiClient.delete(`/projects/${id}`), |
|
|
}, |
|
|
|
|
|
|
|
|
backgrounds: { |
|
|
list: (category?: string) => |
|
|
apiClient.get<any[]>('/backgrounds', { params: { category } }), |
|
|
|
|
|
upload: (file: File) => |
|
|
apiClient.upload<{ id: string; url: string }>('/backgrounds/upload', file), |
|
|
|
|
|
generate: (prompt: string) => |
|
|
apiClient.post<{ id: string; url: string }>('/backgrounds/generate', { prompt }), |
|
|
}, |
|
|
|
|
|
|
|
|
user: { |
|
|
profile: () => |
|
|
apiClient.get<any>('/user/profile'), |
|
|
|
|
|
updateProfile: (data: any) => |
|
|
apiClient.put<any>('/user/profile', data), |
|
|
|
|
|
usage: () => |
|
|
apiClient.get<{ images: number; videos: number; storage: number }>( |
|
|
'/user/usage' |
|
|
), |
|
|
|
|
|
billing: () => |
|
|
apiClient.get<any>('/user/billing'), |
|
|
}, |
|
|
} |
|
|
|
|
|
export default api |