import { api } from '$lib/convex/_generated/api'; import { error } from '@sveltejs/kit'; import type { RequestHandler } from './$types'; function detectImageType(bytes: Uint8Array): string | null { if (bytes[0] === 0xff && bytes[1] === 0xd8 && bytes[2] === 0xff) { return 'image/jpeg'; } if (bytes[0] === 0x89 && bytes[1] === 0x50 && bytes[2] === 0x4e && bytes[3] === 0x47) { return 'image/png'; } if (bytes[0] === 0x47 && bytes[1] === 0x49 && bytes[2] === 0x46) { return 'image/gif'; } if (bytes[0] === 0x52 && bytes[1] === 0x49 && bytes[2] === 0x46 && bytes[3] === 0x46) { return 'image/webp'; } return null; } export const POST: RequestHandler = async ({ params, request, locals }) => { const mnemonic = params.mnemonic; const chatData = await locals.convex.query(api.chats.getByMnemonic, { mnemonic }); if (!chatData) { throw error(404, 'Chat not found'); } const rawContentType = request.headers.get('content-type') || ''; const caption = request.headers.get('x-caption') || ''; console.log('[POST /{mnemonic}] headers:', Object.fromEntries(request.headers.entries())); console.log('[POST /{mnemonic}] content-type:', rawContentType); let base64: string; let mediaType: string; if (rawContentType.includes('multipart/form-data')) { const formData = await request.formData(); const keys = [...formData.keys()]; console.log('[POST /{mnemonic}] formData keys:', keys); let file: File | null = null; for (const key of ['file', 'image', 'photo', 'upload', 'attachment', ...keys]) { const value = formData.get(key); if (value instanceof File) { file = value; console.log('[POST /{mnemonic}] found file in field:', key); break; } } if (!file) { throw error(400, `No file found in form data. Keys: ${keys.join(', ')}`); } const buffer = await file.arrayBuffer(); const bytes = new Uint8Array(buffer); base64 = Buffer.from(buffer).toString('base64'); mediaType = detectImageType(bytes) || file.type || 'image/jpeg'; console.log('[POST /{mnemonic}] file:', file.name, file.type, 'size:', buffer.byteLength); } else if (rawContentType.includes('application/x-www-form-urlencoded')) { throw error(400, 'Use Form with File field, not URL-encoded form'); } else { const buffer = await request.arrayBuffer(); const bytes = new Uint8Array(buffer); base64 = Buffer.from(buffer).toString('base64'); mediaType = detectImageType(bytes) || rawContentType || 'image/jpeg'; console.log('[POST /{mnemonic}] raw bytes size:', buffer.byteLength); console.log('[POST /{mnemonic}] detected type:', mediaType); } if (!base64 || base64.length === 0) { throw error(400, 'Empty image data'); } await locals.convex.mutation(api.messages.create, { chatId: chatData._id, role: 'user', content: caption, source: 'web', imageBase64: base64, imageMediaType: mediaType }); return new Response(JSON.stringify({ ok: true, mediaType, size: base64.length }), { headers: { 'Content-Type': 'application/json' } }); };