Files
Stardream e3bf77296c
Build and Push Docker Image / build (push) Successful in 17s
Initial release
2026-06-16 20:27:36 +10:00

92 lines
2.9 KiB
JavaScript

'use strict';
const path = require('node:path');
const zlib = require('node:zlib');
const { URL } = require('node:url');
const MAX_BODY_BYTES = 256 * 1024 * 1024;
function isCompressible(contentType) {
const t = String(contentType || '');
return /^text\//.test(t) || /application\/(json|javascript)/.test(t);
}
function send(res, status, body = '', contentType = 'text/plain; charset=utf-8', headers = {}) {
let bytes = Buffer.isBuffer(body) ? body : Buffer.from(String(body), 'utf8');
const outHeaders = {
'Content-Type': contentType,
'Cache-Control': 'no-store',
...headers
};
// Gzip text/JSON responses when the client accepts it (single-user app: sync gzip is fine).
if (res._acceptGzip && isCompressible(contentType) && bytes.length > 1024) {
bytes = zlib.gzipSync(bytes);
outHeaders['Content-Encoding'] = 'gzip';
outHeaders['Vary'] = 'Accept-Encoding';
}
outHeaders['Content-Length'] = bytes.length;
res.writeHead(status, outHeaders);
res.end(bytes);
}
function json(res, status, data, headers = {}) {
send(res, status, JSON.stringify(data), 'application/json; charset=utf-8', headers);
}
async function readBody(req) {
const chunks = [];
let size = 0;
for await (const chunk of req) {
size += chunk.length;
if (size > MAX_BODY_BYTES) throw new Error('request body too large');
chunks.push(chunk);
}
return Buffer.concat(chunks);
}
async function readJson(req) {
const body = await readBody(req);
if (!body.length) return {};
return JSON.parse(body.toString('utf8'));
}
function safeName(name, fallback = 'file') {
return String(name || fallback)
.replace(/[<>:"/\\|?*\x00-\x1f]/g, '_')
.replace(/\s+/g, ' ')
.trim()
.slice(0, 180) || fallback;
}
function mimeByExt(file) {
const ext = path.extname(file).toLowerCase();
if (ext === '.html') return 'text/html; charset=utf-8';
if (ext === '.js' || ext === '.mjs') return 'text/javascript; charset=utf-8';
if (ext === '.css') return 'text/css; charset=utf-8';
if (ext === '.json') return 'application/json; charset=utf-8';
if (ext === '.png') return 'image/png';
if (ext === '.webp') return 'image/webp';
if (ext === '.gif') return 'image/gif';
if (ext === '.svg') return 'image/svg+xml';
if (ext === '.woff2') return 'font/woff2';
if (ext === '.woff') return 'font/woff';
if (ext === '.ttf') return 'font/ttf';
if (ext === '.otf') return 'font/otf';
return 'image/jpeg';
}
function extFromContentType(type, sourceUrl) {
const contentType = String(type || '').toLowerCase();
if (contentType.includes('png')) return '.png';
if (contentType.includes('webp')) return '.webp';
if (contentType.includes('gif')) return '.gif';
if (contentType.includes('jpeg') || contentType.includes('jpg')) return '.jpg';
try {
return path.extname(new URL(sourceUrl).pathname) || '.jpg';
} catch {
return '.jpg';
}
}
module.exports = { send, json, readBody, readJson, safeName, mimeByExt, extFromContentType, MAX_BODY_BYTES };