import axios, { AxiosInstance } from 'axios'; import type { User, AuthTokens, Asset, AssetListResponse, CreateUploadRequest, CreateUploadResponse, DownloadUrlResponse, Share, CreateShareRequest, ShareWithAssetResponse, } from '../types'; const API_URL = import.meta.env.VITE_API_URL || 'http://localhost:8000'; class ApiClient { private client: AxiosInstance; constructor() { this.client = axios.create({ baseURL: `${API_URL}/api/v1`, headers: { 'Content-Type': 'application/json', }, }); // Add auth token to requests this.client.interceptors.request.use((config) => { const token = localStorage.getItem('access_token'); if (token) { config.headers.Authorization = `Bearer ${token}`; } return config; }); // Handle 401 errors this.client.interceptors.response.use( (response) => response, (error) => { if (error.response?.status === 401) { localStorage.removeItem('access_token'); localStorage.removeItem('refresh_token'); // Redirect only if not already on login/register page if (!['/login', '/register'].includes(window.location.pathname)) { window.location.href = '/login'; } } return Promise.reject(error); } ); } // Auth async register(email: string, password: string): Promise { const { data } = await this.client.post('/auth/register', { email, password }); return data; } async login(email: string, password: string): Promise { const { data } = await this.client.post('/auth/login', { email, password }); localStorage.setItem('access_token', data.access_token); localStorage.setItem('refresh_token', data.refresh_token); return data; } async getMe(): Promise { const { data } = await this.client.get('/auth/me'); return data; } logout(): void { localStorage.removeItem('access_token'); localStorage.removeItem('refresh_token'); } // Assets async listAssets(params?: { cursor?: string; limit?: number; type?: string; folder_id?: string | null; }): Promise { const { data } = await this.client.get('/assets', { params }); return data; } async getAsset(assetId: string): Promise { const { data } = await this.client.get(`/assets/${assetId}`); return data; } async getDownloadUrl(assetId: string, kind: 'original' | 'thumb' = 'original'): Promise { const { data } = await this.client.get( `/assets/${assetId}/download-url`, { params: { kind } } ); return data.url; } async getMediaBlob(assetId: string, kind: 'original' | 'thumb' = 'original'): Promise { const response = await this.client.get(`/assets/${assetId}/media`, { params: { kind }, responseType: 'blob', }); return response.data; } async deleteAsset(assetId: string): Promise { await this.client.delete(`/assets/${assetId}`); } // Upload async createUpload(request: CreateUploadRequest): Promise { const { data } = await this.client.post('/uploads/create', request); return data; } async uploadToS3(url: string, file: File, fields?: Record): Promise { const formData = new FormData(); // Add fields first (for pre-signed POST) if (fields) { Object.entries(fields).forEach(([key, value]) => { formData.append(key, value); }); } // Add file last formData.append('file', file); await axios.post(url, formData, { headers: { 'Content-Type': 'multipart/form-data', }, }); } async uploadFileToBackend(assetId: string, file: File): Promise { const formData = new FormData(); formData.append('file', file); await this.client.post(`/uploads/${assetId}/file`, formData, { headers: { 'Content-Type': 'multipart/form-data', }, }); } async finalizeUpload(assetId: string, etag?: string, sha256?: string): Promise { const { data } = await this.client.post(`/uploads/${assetId}/finalize`, { etag, sha256 }); return data; } // Shares async createShare(request: CreateShareRequest): Promise { const { data } = await this.client.post('/shares', request); return data; } async getShare(token: string, password?: string): Promise { const { data } = await this.client.get(`/shares/${token}`, { params: password ? { password } : undefined, }); return data; } async getShareDownloadUrl( token: string, assetId: string, kind: 'original' | 'thumb' = 'original', password?: string ): Promise { const { data } = await this.client.get( `/shares/${token}/download-url`, { params: { asset_id: assetId, kind, password }, } ); return data.url; } async revokeShare(token: string): Promise { const { data} = await this.client.post(`/shares/${token}/revoke`); return data; } // Folders async createFolder(name: string, parentFolderId?: string | null): Promise { const { data } = await this.client.post('/folders', { name, parent_folder_id: parentFolderId, }); return data; } async listFolders(parentFolderId?: string | null, all: boolean = false): Promise { const params: any = {}; if (all) { params.all = true; } else if (parentFolderId) { params.parent_folder_id = parentFolderId; } const { data } = await this.client.get('/folders', { params: Object.keys(params).length > 0 ? params : undefined, }); return data; } async getFolder(folderId: string): Promise { const { data } = await this.client.get(`/folders/${folderId}`); return data; } async renameFolder(folderId: string, newName: string): Promise { const { data } = await this.client.patch(`/folders/${folderId}`, { name: newName, }); return data; } async deleteFolder(folderId: string, recursive: boolean = false): Promise { await this.client.delete(`/folders/${folderId}`, { params: { recursive }, }); } async getFolderBreadcrumbs(folderId: string): Promise { const { data } = await this.client.get(`/folders/${folderId}/breadcrumbs`); return data; } // Batch Operations async batchDelete(assetIds: string[]): Promise { const { data } = await this.client.post('/batch/delete', { asset_ids: assetIds, }); return data; } async batchMove(assetIds: string[], folderId: string | null): Promise { const { data } = await this.client.post('/batch/move', { asset_ids: assetIds, folder_id: folderId, }); return data; } async batchDownload(assetIds: string[]): Promise { const response = await this.client.post( '/batch/download', { asset_ids: assetIds }, { responseType: 'blob' } ); return response.data; } async downloadFolder(folderId: string): Promise { const response = await this.client.get(`/batch/folders/${folderId}/download`, { responseType: 'blob', }); return response.data; } } export default new ApiClient();