import { randomUUID } from 'crypto'; import { put } from '../../../lib/object-storage.js'; import { getUserFromRequest } from '../../../lib/permission-middleware'; import { checkUploadRateLimit } from '../../../lib/rate-limit.js'; export const config = { api: { bodyParser: { sizeLimit: '6mb', }, }, }; function parseDataUrlImage(imageData) { const match = imageData.match(/^data:(image\/(?:jpeg|jpg|png|webp));base64,(.+)$/i); if (!match) return null; const contentType = match[1].toLowerCase() === 'image/jpg' ? 'image/jpeg' : match[1].toLowerCase(); const buffer = Buffer.from(match[2], 'base64'); return { contentType, buffer }; } export default async function handler(req, res) { if (req.method !== 'POST') { return res.status(405).json({ error: 'Method not allowed' }); } try { const user = await getUserFromRequest(req); if (!user) { return res.status(401).json({ error: 'Authentication required' }); } const { allowed, reset } = await checkUploadRateLimit(req, user.userId); if (!allowed) { res.setHeader('Retry-After', Math.ceil((reset - Date.now()) / 1000)); return res.status(429).json({ error: 'Too many attempts. Try again later.' }); } const { imageData } = req.body || {}; if (!imageData || typeof imageData !== 'string' || imageData.length > 6_000_000) { return res.status(400).json({ error: 'Valid imageData is required' }); } const parsed = parseDataUrlImage(imageData); if (!parsed) { return res.status(400).json({ error: 'imageData must be a JPEG, PNG, or WebP data URL' }); } if (parsed.buffer.length > 5 * 1024 * 1024) { return res.status(400).json({ error: 'Image must be less than 5MB' }); } const ext = parsed.contentType.split('/')[1] === 'jpeg' ? 'jpg' : parsed.contentType.split('/')[1]; const filename = `scans/${user.userId}/${randomUUID()}.${ext}`; const blob = await put(filename, parsed.buffer, { access: 'public', contentType: parsed.contentType, }); return res.status(200).json({ url: blob.url }); } catch (error) { console.error('[POST /api/scan/upload-image]', error); return res.status(500).json({ error: 'Internal server error' }); } }