itcloud/frontend/src/services/api.ts

196 lines
5.2 KiB
TypeScript

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');
window.location.href = '/login';
}
return Promise.reject(error);
}
);
}
// Auth
async register(email: string, password: string): Promise<User> {
const { data } = await this.client.post('/auth/register', { email, password });
return data;
}
async login(email: string, password: string): Promise<AuthTokens> {
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<User> {
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;
}): Promise<AssetListResponse> {
const { data } = await this.client.get('/assets', { params });
return data;
}
async getAsset(assetId: string): Promise<Asset> {
const { data } = await this.client.get(`/assets/${assetId}`);
return data;
}
async getDownloadUrl(assetId: string, kind: 'original' | 'thumb' = 'original'): Promise<string> {
const { data } = await this.client.get<DownloadUrlResponse>(
`/assets/${assetId}/download-url`,
{ params: { kind } }
);
return data.url;
}
async getMediaBlob(assetId: string, kind: 'original' | 'thumb' = 'original'): Promise<Blob> {
const response = await this.client.get(`/assets/${assetId}/media`, {
params: { kind },
responseType: 'blob',
});
return response.data;
}
async deleteAsset(assetId: string): Promise<Asset> {
const { data } = await this.client.delete(`/assets/${assetId}`);
return data;
}
async restoreAsset(assetId: string): Promise<Asset> {
const { data } = await this.client.post(`/assets/${assetId}/restore`);
return data;
}
async purgeAsset(assetId: string): Promise<void> {
await this.client.delete(`/assets/${assetId}/purge`);
}
// Upload
async createUpload(request: CreateUploadRequest): Promise<CreateUploadResponse> {
const { data } = await this.client.post('/uploads/create', request);
return data;
}
async uploadToS3(url: string, file: File, fields?: Record<string, string>): Promise<void> {
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<void> {
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<Asset> {
const { data } = await this.client.post(`/uploads/${assetId}/finalize`, { etag, sha256 });
return data;
}
// Shares
async createShare(request: CreateShareRequest): Promise<Share> {
const { data } = await this.client.post('/shares', request);
return data;
}
async getShare(token: string, password?: string): Promise<ShareWithAssetResponse> {
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<string> {
const { data } = await this.client.get<DownloadUrlResponse>(
`/shares/${token}/download-url`,
{
params: { asset_id: assetId, kind, password },
}
);
return data.url;
}
async revokeShare(token: string): Promise<Share> {
const { data } = await this.client.post(`/shares/${token}/revoke`);
return data;
}
}
export default new ApiClient();