/** * File upload service using external API * Replaces MINIO with custom file upload API */ import { env } from './env' // File upload result from the API export interface FileUploadResponse { success: boolean url: string filename: string } // File upload result for internal use export interface UploadResult { url: string filename: string originalName: string size: number type: string uploadedAt: string } /** * Upload file to external API */ export async function uploadFile( buffer: Buffer, filename: string, contentType: string, userId?: string ): Promise { console.log('📤 Starting file upload:', { filename, contentType, bufferSize: buffer.length }) try { // Create FormData for multipart upload const formData = new FormData() const blob = new Blob([buffer], { type: contentType }) formData.append('file', blob, filename) // Prepare headers const headers: Record = { 'x-user-data': userId || 'default-user', Authorization: `Bearer ${env.FILE_UPLOAD_TOKEN || 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IlRlc3QgVXNlciIsImlhdCI6MTUxNjIzOTAyMn0.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c'}`, } console.log('📤 Uploading to external API:', { url: process.env.UPLOAD_API_URL, filename, size: buffer.length, userId: userId || 'default-user', }) // Make the upload request const response = await fetch(process.env.UPLOAD_API_URL, { method: 'POST', headers, body: formData, }) if (!response.ok) { const errorText = await response.text() throw new Error(`Upload failed: ${response.status} ${response.statusText} - ${errorText}`) } const result: FileUploadResponse = await response.json() if (!result.success) { throw new Error('Upload API returned success: false') } console.log('✅ File uploaded successfully:', result) return { url: result.url, filename: result.filename, originalName: filename, size: buffer.length, type: contentType, uploadedAt: new Date().toISOString(), } } catch (error) { console.error('❌ Error uploading file:', error) throw error } } /** * Generate unique filename with timestamp and random string */ export function generateUniqueFilename(originalName: string): string { const timestamp = Date.now() const randomString = Math.random().toString(36).substring(2) const extension = originalName.split('.').pop() return `${timestamp}-${randomString}.${extension}` } /** * Validate file type */ export function validateFileType(mimetype: string, allowedTypes: string[]): boolean { return allowedTypes.includes(mimetype) } /** * Validate file size (in bytes) */ export function validateFileSize(size: number, maxSize: number): boolean { return size <= maxSize } /** * Get file URL (files are already publicly accessible) */ export async function getFileUrl(filePath: string): Promise { // Files from the upload API are already publicly accessible // If filePath is already a full URL, return it as is if (filePath.startsWith('http')) { return filePath } // Otherwise, construct the URL using the delivery base URL return `${process.env.DELIVERY_BASE_URL}/${filePath}` } /** * Delete file (not supported by the current API) */ export async function deleteFile(filePath: string): Promise { console.warn('âš ī¸ File deletion not supported by current upload API:', filePath) // The external API doesn't provide a delete endpoint // This is a no-op for now } /** * Move file to permanent storage (not needed with new API) */ export async function moveToPermStorage(tempPath: string, permanentPath: string): Promise { console.log('â„šī¸ Move to permanent storage not needed with new API') // The new API directly uploads to permanent storage // This is a no-op } /** * Initialize bucket (not needed with new API) */ export async function initializeBucket(): Promise { console.log('â„šī¸ Bucket initialization not needed with new API') // The new API handles storage internally // This is a no-op }