class APIClient { constructor(baseURL = '/') { this.baseURL = baseURL; this.token = localStorage.getItem('token'); } setToken(token) { this.token = token; if (token) { localStorage.setItem('token', token); } else { localStorage.removeItem('token'); } } getToken() { return this.token; } async request(endpoint, options = {}) { const url = `${this.baseURL}${endpoint}`; const headers = { ...options.headers, }; if (this.token && !options.skipAuth) { headers['Authorization'] = `Bearer ${this.token}`; } if (!(options.body instanceof FormData) && options.body) { headers['Content-Type'] = 'application/json'; } const config = { ...options, headers, }; if (config.body && !(config.body instanceof FormData)) { config.body = JSON.stringify(config.body); } const response = await fetch(url, config); if (response.status === 401) { this.setToken(null); window.location.href = '/'; } if (!response.ok) { const error = await response.json().catch(() => ({ detail: 'Unknown error' })); throw new Error(error.detail || 'Request failed'); } if (response.status === 204) { return null; } return response.json(); } async register(username, email, password) { const data = await this.request('auth/register', { method: 'POST', body: { username, email, password }, skipAuth: true }); this.setToken(data.access_token); return data; } async login(username, password) { const formData = new FormData(); formData.append('username', username); formData.append('password', password); const data = await this.request('auth/token', { method: 'POST', body: formData, skipAuth: true }); this.setToken(data.access_token); return data; } logout() { this.setToken(null); window.location.href = '/'; } async getCurrentUser() { return this.request('users/me'); } async listFolders(parentId = null) { const params = parentId ? `?parent_id=${parentId}` : ''; return this.request(`folders/${params}`); } async createFolder(name, parentId = null) { return this.request('folders/', { method: 'POST', body: { name, parent_id: parentId } }); } async deleteFolder(folderId) { return this.request(`folders/${folderId}`, { method: 'DELETE' }); } async uploadFile(file, folderId = null) { const formData = new FormData(); formData.append('file', file); const endpoint = folderId ? `files/upload?folder_id=${folderId}` : 'files/upload'; return this.request(endpoint, { method: 'POST', body: formData }); } async listFiles(folderId = null) { const params = folderId ? `?folder_id=${folderId}` : ''; return this.request(`files/${params}`); } async downloadFile(fileId) { const url = `${this.baseURL}files/download/${fileId}`; const response = await fetch(url, { headers: { 'Authorization': `Bearer ${this.token}` } }); if (!response.ok) { throw new Error('Download failed'); } return response.blob(); } async deleteFile(fileId) { return this.request(`files/${fileId}`, { method: 'DELETE' }); } async moveFile(fileId, targetFolderId) { return this.request(`files/${fileId}/move`, { method: 'POST', body: { target_folder_id: targetFolderId } }); } async renameFile(fileId, newName) { return this.request(`files/${fileId}/rename`, { method: 'POST', body: { new_name: newName } }); } async copyFile(fileId, targetFolderId) { return this.request(`files/${fileId}/copy`, { method: 'POST', body: { target_folder_id: targetFolderId } }); } async createShare(fileId = null, folderId = null, expiresAt = null, password = null, permissionLevel = 'viewer') { return this.request('shares/', { method: 'POST', body: { file_id: fileId, folder_id: folderId, expires_at: expiresAt, password, permission_level: permissionLevel } }); } async deleteShare(shareId) { return this.request(`shares/${shareId}`, { method: 'DELETE' }); } async searchFiles(query, filters = {}) { const params = new URLSearchParams({ q: query, ...filters }); return this.request(`search/files?${params}`); } async searchFolders(query) { return this.request(`search/folders?q=${encodeURIComponent(query)}`); } async getPhotos() { return this.request('files/photos'); } getThumbnailUrl(fileId) { return `${this.baseURL}files/thumbnail/${fileId}`; } } export const api = new APIClient();