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) { let errorData; try { errorData = await response.json(); } catch (e) { errorData = { message: 'Unknown error' }; } const errorMessage = errorData.detail || errorData.message || 'Request failed'; document.dispatchEvent(new CustomEvent('show-toast', { detail: { message: errorMessage, type: 'error' } })); throw new Error(errorMessage); } 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 getFolderPath(folderId) { return this.request(`folders/${folderId}/path`); } 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'); } async getThumbnailUrl(fileId) { return `${this.baseURL}files/thumbnail/${fileId}`; } async listDeletedFiles() { return this.request('files/deleted'); } async restoreFile(fileId) { return this.request(`files/${fileId}/restore`, { method: 'POST' }); } async listUsers() { return this.request('admin/users'); } async createUser(userData) { return this.request('admin/users', { method: 'POST', body: userData }); } async updateUser(userId, userData) { return this.request(`admin/users/${userId}`, { method: 'PUT', body: userData }); } async deleteUser(userId) { return this.request(`admin/users/${userId}`, { method: 'DELETE' }); } async starFile(fileId) { return this.request(`files/${fileId}/star`, { method: 'POST' }); } async unstarFile(fileId) { return this.request(`files/${fileId}/unstar`, { method: 'POST' }); } async starFolder(folderId) { return this.request(`folders/${folderId}/star`, { method: 'POST' }); } async unstarFolder(folderId) { return this.request(`folders/${folderId}/unstar`, { method: 'POST' }); } async listStarredFiles() { return this.request('starred/files'); } async listStarredFolders() { return this.request('starred/folders'); } async listRecentFiles() { return this.request('files/recent'); } async listMyShares() { return this.request('shares/my'); } async updateShare(shareId, shareData) { return this.request(`shares/${shareId}`, { method: 'PUT', body: shareData }); } async batchFileOperations(operation, fileIds, targetFolderId = null) { const payload = { file_ids: fileIds, operation: operation }; if (targetFolderId !== null) { payload.target_folder_id = targetFolderId; } return this.request('files/batch', { method: 'POST', body: payload }); } async batchFolderOperations(operation, folderIds, targetFolderId = null) { const payload = { folder_ids: folderIds, operation: operation }; if (targetFolderId !== null) { payload.target_folder_id = targetFolderId; } return this.request('folders/batch', { method: 'POST', body: payload }); } async updateFile(fileId, content) { return this.request(`files/${fileId}/content`, { method: 'PUT', body: { content } }); } } export const api = new APIClient();