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