import { apiClient } from '@/lib/api/client'; import { StorageFileSchema, StorageBucketSchema, ListObjectsResponseSchema, } from '@insforge/shared-schemas'; export interface ListObjectsParams { prefix?: string; limit?: number; offset?: number; } export const storageService = { // List all buckets async listBuckets(): Promise { const response = await apiClient.request('/storage/buckets', { headers: apiClient.withAccessToken(), }); // Traditional REST: API returns array directly return response; }, // List objects in a bucket async listObjects( bucketName: string, params?: ListObjectsParams, searchQuery?: string ): Promise { const searchParams = new URLSearchParams(); if (params?.prefix) { searchParams.append('prefix', params.prefix); } if (params?.limit) { searchParams.append('limit', params.limit.toString()); } if (params?.offset) { searchParams.append('offset', params.offset.toString()); } if (searchQuery && searchQuery.trim()) { searchParams.append('search', searchQuery.trim()); } const url = `/storage/buckets/${encodeURIComponent(bucketName)}/objects${searchParams.toString() ? `?${searchParams}` : ''}`; const response: { data: StorageFileSchema[]; pagination: { offset: number; limit: number; total: number }; } = await apiClient.request(url, { headers: apiClient.withAccessToken(), }); return { objects: response.data, pagination: response.pagination, }; }, // Upload an object to bucket async uploadObject( bucketName: string, objectKey: string, object: File ): Promise { const formData = new FormData(); formData.append('file', object); // Use fetch directly for object uploads to avoid Content-Type header issues const response = await fetch( `/api/storage/buckets/${encodeURIComponent(bucketName)}/objects/${encodeURIComponent(objectKey)}`, { method: 'PUT', headers: { Authorization: `Bearer ${apiClient.getToken()}`, }, body: formData, } ); if (!response.ok) { const error = await response.json(); // Traditional REST error format throw new Error(error.message || error.error || 'Upload failed'); } const result = await response.json(); // Traditional REST: response returned directly return result; }, // Get download URL for an object getDownloadUrl(bucketName: string, objectKey: string): string { return `/api/storage/buckets/${encodeURIComponent(bucketName)}/objects/${encodeURIComponent(objectKey)}`; }, // Download an object (returns blob) async downloadObject(bucketName: string, objectKey: string): Promise { const response = await fetch(storageService.getDownloadUrl(bucketName, objectKey), { headers: { Authorization: `Bearer ${apiClient.getToken()}`, }, }); if (!response.ok) { throw new Error(`Failed to download object: ${response.statusText}`); } return await response.blob(); }, // Delete an object async deleteObject(bucketName: string, objectKey: string): Promise { await apiClient.request( `/storage/buckets/${encodeURIComponent(bucketName)}/objects/${encodeURIComponent(objectKey)}`, { method: 'DELETE', headers: apiClient.withAccessToken(), } ); }, async deleteObjects( bucketName: string, objectKeys: string[] ): Promise<{ success: string[]; failures: { key: string; error: Error }[] }> { const results = await Promise.allSettled( objectKeys.map((key) => this.deleteObject(bucketName, key)) ); const success: string[] = []; const failures: { key: string; error: Error }[] = []; results.forEach((result, index) => { if (result.status === 'fulfilled') { success.push(objectKeys[index]); } else { failures.push({ key: objectKeys[index], error: result.reason as Error }); } }); return { success, failures }; }, // Create a new bucket async createBucket(bucketName: string, isPublic: boolean = true): Promise { await apiClient.request('/storage/buckets', { method: 'POST', headers: apiClient.withAccessToken(), body: JSON.stringify({ bucketName: bucketName, isPublic: isPublic }), }); }, // Delete entire bucket async deleteBucket(bucketName: string): Promise { await apiClient.request(`/storage/buckets/${encodeURIComponent(bucketName)}`, { method: 'DELETE', headers: apiClient.withAccessToken(), }); }, // Edit bucket (update visibility or other config) async editBucket(bucketName: string, config: { isPublic: boolean }): Promise { await apiClient.request(`/storage/buckets/${encodeURIComponent(bucketName)}`, { method: 'PATCH', headers: apiClient.withAccessToken(), body: JSON.stringify(config), }); }, };