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();