Download src/handlers/fileHandler.ts from Collabos/chatapi: direct link, hf CLI and curl.
- Browser
- Download file 2.67 kB
-
https://huggingface.co/spaces/Collabos/chatapi/resolve/main/src/handlers/fileHandler.ts
- Command line
-
hf download hf://spaces/Collabos/chatapi/src/handlers/fileHandler.ts
-
curl -L -o fileHandler.ts https://huggingface.co/spaces/Collabos/chatapi/resolve/main/src/handlers/fileHandler.ts
2.67 kB
| 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<void> { | |
| 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); | |
| } | |
| } | |