Spaces:
Running
Running
File size: 3,790 Bytes
90f0300 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 | 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';
}
|