import WebSocket from 'ws'; import { ClientMessage, safeSend } from '../utils/protocol'; import { generateUploadUrl, validateFileRequest, validateFileCount, FileUploadRequest, } from '../services/supabaseService'; import { logger } from '../utils/logger'; /** * Handles type: 'get-upload-url' * * Client sends: * { * files: Array<{ fileName: string, mimeType: string, sizeBytes: number }> * } * * Server responds with: * { * urls: Array<{ signedUrl, objectPath, publicUrl, fileName }> * } * * The client then PUTs each file directly to Supabase using the signedUrl. * HF server never sees any file bytes — memory usage stays near 0. * * After upload, the client sends a type: 'message' with type: 'media' and * the publicUrl as the content field. */ export async function handleFileRequest( ws: WebSocket, uid: string, msg: ClientMessage ): Promise { const files = msg.payload.files as FileUploadRequest[] | undefined; // ── Validate count ──────────────────────────────────────────────────────── if (!Array.isArray(files)) { safeSend(ws, 'error', { message: 'files must be an array' }, msg.requestId); return; } const countError = validateFileCount(files.length); if (countError) { safeSend(ws, 'error', { message: countError }, msg.requestId); return; } // ── Validate each file ──────────────────────────────────────────────────── for (const f of files) { const fileError = validateFileRequest(f); if (fileError) { safeSend(ws, 'error', { message: fileError }, msg.requestId); return; } } // ── Generate signed URLs ────────────────────────────────────────────────── try { const results = await Promise.all( files.map(async (f) => { const result = await generateUploadUrl(uid, f); return { fileName: f.fileName, signedUrl: result.signedUrl, objectPath: result.objectPath, publicUrl: result.publicUrl, }; }) ); safeSend(ws, 'upload-url', { urls: results }, msg.requestId); logger.info('Signed URLs generated', { uid, count: results.length }); } catch (err) { logger.error('generateUploadUrl failed', { uid, error: String(err) }); safeSend(ws, 'error', { message: 'Failed to generate upload URL' }, msg.requestId); } }