| 7 | import crypto from 'crypto'; |
| 8 | |
| 9 | export default async function handler(req: any, res: any) { |
| 10 | if (req.method !== 'POST') { |
| 11 | res.setHeader('Allow', 'POST'); |
| 12 | return res.status(405).json({ error: `Method ${req.method} not allowed` }); |
| 13 | } |
| 14 | |
| 15 | try { |
| 16 | const contentType = req.headers['content-type'] || ''; |
| 17 | const isJson = contentType.includes('application/json'); |
| 18 | |
| 19 | let repo = ''; |
| 20 | let version = ''; |
| 21 | let stage = ''; |
| 22 | let sha256 = ''; |
| 23 | let size = 0; |
| 24 | let bundleMetadata: any = {}; |
| 25 | let displaySize = ''; |
| 26 | let fileBuffer = Buffer.alloc(0); |
| 27 | |
| 28 | if (isJson) { |
| 29 | // Read JSON body |
| 30 | const chunks: Buffer[] = []; |
| 31 | for await (const chunk of req) { |
| 32 | chunks.push(chunk); |
| 33 | } |
| 34 | const jsonText = Buffer.concat(chunks).toString('utf-8'); |
| 35 | let body: any = {}; |
| 36 | try { |
| 37 | body = JSON.parse(jsonText); |
| 38 | } catch (err) { |
| 39 | return res.status(400).json({ error: "Invalid JSON body payload." }); |
| 40 | } |
| 41 | |
| 42 | repo = body.repo; |
| 43 | version = body.version; |
| 44 | stage = (req.headers['x-publish-stage'] || body.stage || '').toLowerCase(); |
| 45 | sha256 = body.sha256; |
| 46 | size = body.size; |
| 47 | bundleMetadata = body.bundleMetadata || {}; |
| 48 | displaySize = body.displaySize || 'unknown'; |
| 49 | |
| 50 | if (!stage || !['handshake', 'commit'].includes(stage)) { |
| 51 | return res.status(400).json({ error: "Invalid or missing 'X-Publish-Stage' header. Expected 'handshake' or 'commit'." }); |
| 52 | } |
| 53 | if (!sha256 || !size) { |
| 54 | return res.status(400).json({ error: "SHA256 hash and file size are required for the two-stage flow." }); |
| 55 | } |
| 56 | } else { |
| 57 | // Legacy direct binary payload stream |
| 58 | repo = req.query.repo || ''; |
| 59 | version = req.query.version || ''; |
| 60 | |
| 61 | const chunks: Buffer[] = []; |
| 62 | let receivedBytes = 0; |
| 63 | const MAX_SIZE = 100 * 1024 * 1024; // 100MB limit for legacy (though Vercel limits to 4.5MB) |
| 64 | |
| 65 | for await (const chunk of req) { |
| 66 | receivedBytes += chunk.length; |