Spaces:
Running
Running
| import path from 'node:path'; | |
| const PNG_MAGIC = Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]); | |
| const JPEG_MAGIC = Buffer.from([0xff, 0xd8, 0xff]); | |
| const WEBP_MIN_BYTES = 12; | |
| const WEBP_RIFF_OFFSET = 0; | |
| const WEBP_RIFF_END = 4; | |
| const WEBP_WEBP_OFFSET = 8; | |
| const WEBP_WEBP_END = 12; | |
| const WEBP_RIFF = 'RIFF'; | |
| const WEBP_WEBP = 'WEBP'; | |
| export function sendJson(res, status, payload) { | |
| res.writeHead(status, { | |
| 'content-type': 'application/json; charset=utf-8', | |
| 'cache-control': 'no-store' | |
| }); | |
| res.end(JSON.stringify(payload)); | |
| } | |
| export function sendHtml(res, status, html) { | |
| res.writeHead(status, { | |
| 'content-type': 'text/html; charset=utf-8', | |
| 'cache-control': 'no-store' | |
| }); | |
| res.end(html); | |
| } | |
| export function htmlEscape(value) { | |
| return String(value || '') | |
| .replace(/&/g, '&') | |
| .replace(/</g, '<') | |
| .replace(/>/g, '>') | |
| .replace(/"/g, '"') | |
| .replace(/'/g, '''); | |
| } | |
| export function readBody(req, maxBytes) { | |
| return new Promise((resolve, reject) => { | |
| const chunks = []; | |
| let total = 0; | |
| let settled = false; | |
| req.on('data', (chunk) => { | |
| if (settled) { | |
| return; | |
| } | |
| const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk); | |
| total += buffer.length; | |
| if (total > maxBytes) { | |
| settled = true; | |
| reject(Object.assign(new Error('Request body too large'), { statusCode: 413, status: 413 })); | |
| req.destroy?.(); | |
| return; | |
| } | |
| chunks.push(buffer); | |
| }); | |
| req.on('end', () => { | |
| if (settled) { | |
| return; | |
| } | |
| settled = true; | |
| const body = Buffer.concat(chunks).toString('utf8'); | |
| if (!body) { | |
| resolve({}); | |
| return; | |
| } | |
| try { | |
| resolve(JSON.parse(body)); | |
| } catch { | |
| reject(new Error('Invalid JSON body')); | |
| } | |
| }); | |
| req.on('error', (error) => { | |
| if (!settled) { | |
| settled = true; | |
| reject(error); | |
| } | |
| }); | |
| }); | |
| } | |
| export function readBuffer(req, maxBytes) { | |
| return new Promise((resolve, reject) => { | |
| const chunks = []; | |
| let total = 0; | |
| let settled = false; | |
| req.on('data', (chunk) => { | |
| if (settled) { | |
| return; | |
| } | |
| total += chunk.length; | |
| if (total > maxBytes) { | |
| settled = true; | |
| req.resume(); | |
| reject(new Error('Upload too large')); | |
| return; | |
| } | |
| chunks.push(chunk); | |
| }); | |
| req.on('end', () => { | |
| if (!settled) { | |
| settled = true; | |
| resolve(Buffer.concat(chunks)); | |
| } | |
| }); | |
| req.on('error', (error) => { | |
| if (!settled) { | |
| settled = true; | |
| reject(error); | |
| } | |
| }); | |
| }); | |
| } | |
| export function parseHeaderValue(value, key) { | |
| const match = String(value || '').match(new RegExp(`${key}="([^"]*)"`)); | |
| return match ? match[1] : ''; | |
| } | |
| export function sanitizeFileName(fileName) { | |
| const baseName = path.basename(String(fileName || 'upload.bin')).replace(/[<>:"/\\|?*\u0000-\u001F]/g, '_'); | |
| return baseName || 'upload.bin'; | |
| } | |
| export function hasSupportedImageMagic(data, mimeType) { | |
| if (/png/i.test(mimeType)) { | |
| return data.length >= PNG_MAGIC.length && | |
| data.subarray(0, PNG_MAGIC.length).equals(PNG_MAGIC); | |
| } | |
| if (/jpe?g/i.test(mimeType)) { | |
| return data.length >= JPEG_MAGIC.length && | |
| data.subarray(0, JPEG_MAGIC.length).equals(JPEG_MAGIC); | |
| } | |
| if (/webp/i.test(mimeType)) { | |
| return data.length >= WEBP_MIN_BYTES && | |
| data.subarray(WEBP_RIFF_OFFSET, WEBP_RIFF_END).toString('ascii') === WEBP_RIFF && | |
| data.subarray(WEBP_WEBP_OFFSET, WEBP_WEBP_END).toString('ascii') === WEBP_WEBP; | |
| } | |
| return false; | |
| } | |
| export function classifyUpload(mimeType, data = Buffer.alloc(0)) { | |
| return hasSupportedImageMagic(data, mimeType) ? 'image' : 'file'; | |
| } | |