fix: append correct file extension based on content-type (CSS/JS need them)
This commit is contained in:
@@ -42,23 +42,47 @@ const CHAT_OVERLAY = `
|
||||
</script>
|
||||
`;
|
||||
|
||||
function urlToSafePath(u) {
|
||||
function mimeExt(ct) {
|
||||
ct = (ct || '').split(';')[0].trim().toLowerCase();
|
||||
if (ct.includes('css')) return '.css';
|
||||
if (ct.includes('javascript') || ct.includes('ecmascript')) return '.js';
|
||||
if (ct === 'image/png') return '.png';
|
||||
if (ct === 'image/jpeg' || ct === 'image/jpg') return '.jpg';
|
||||
if (ct === 'image/gif') return '.gif';
|
||||
if (ct === 'image/svg+xml') return '.svg';
|
||||
if (ct === 'image/webp') return '.webp';
|
||||
if (ct === 'image/x-icon' || ct === 'image/vnd.microsoft.icon') return '.ico';
|
||||
if (ct === 'font/woff2' || ct === 'application/font-woff2') return '.woff2';
|
||||
if (ct === 'font/woff' || ct === 'application/font-woff') return '.woff';
|
||||
if (ct === 'font/ttf' || ct === 'application/x-font-ttf') return '.ttf';
|
||||
if (ct === 'font/otf') return '.otf';
|
||||
if (ct.startsWith('text/html')) return '.html';
|
||||
if (ct.startsWith('application/json')) return '.json';
|
||||
return '';
|
||||
}
|
||||
|
||||
function urlToSafePath(u, contentType) {
|
||||
const parsed = new URL(u);
|
||||
let p = parsed.pathname;
|
||||
if (parsed.search) {
|
||||
let safe = parsed.search.slice(1).replace(/[^a-zA-Z0-9._-]/g, '_');
|
||||
// Hash overly long query strings to avoid ENAMETOOLONG
|
||||
if (safe.length > 80) {
|
||||
safe = crypto.createHash('md5').update(parsed.search).digest('hex').slice(0, 12);
|
||||
}
|
||||
p += '_q_' + safe;
|
||||
}
|
||||
// also hash if path itself is way too long
|
||||
const basename = path.basename(p);
|
||||
if (basename.length > 200) {
|
||||
// Append correct extension if missing — so express.static sends proper Content-Type
|
||||
const baseName = path.basename(p);
|
||||
if (!/\.[a-z0-9]{2,5}$/i.test(baseName) && contentType) {
|
||||
const ext = mimeExt(contentType);
|
||||
if (ext) p += ext;
|
||||
}
|
||||
// Hash if too long
|
||||
const baseName2 = path.basename(p);
|
||||
if (baseName2.length > 200) {
|
||||
const ext = path.extname(p);
|
||||
const dir = path.dirname(p);
|
||||
p = dir + '/' + crypto.createHash('md5').update(basename).digest('hex').slice(0, 16) + ext;
|
||||
p = dir + '/' + crypto.createHash('md5').update(baseName2).digest('hex').slice(0, 16) + ext;
|
||||
}
|
||||
return p.replace(/^\/+/, '');
|
||||
}
|
||||
@@ -99,7 +123,8 @@ async function main() {
|
||||
if (!rurl.startsWith('http')) return;
|
||||
try {
|
||||
const buf = await resp.body();
|
||||
if (buf && buf.length > 0) resources.set(rurl, buf);
|
||||
const ct = resp.headers()['content-type'] || '';
|
||||
if (buf && buf.length > 0) resources.set(rurl, { buf, ct });
|
||||
} catch (e) { /* redirect/204/etc */ }
|
||||
});
|
||||
|
||||
@@ -115,10 +140,10 @@ async function main() {
|
||||
// Write resources
|
||||
fs.mkdirSync(MIRROR, { recursive: true });
|
||||
const mapping = new Map(); // absUrl → /q/mirror/<safepath>
|
||||
for (const [rurl, buf] of resources) {
|
||||
for (const [rurl, { buf, ct }] of resources) {
|
||||
const u = new URL(rurl);
|
||||
if (u.hostname !== HOST) continue; // skip third-party (cdnjs etc) — let browser load them
|
||||
const safe = urlToSafePath(rurl);
|
||||
const safe = urlToSafePath(rurl, ct);
|
||||
const local = path.join(MIRROR, safe);
|
||||
fs.mkdirSync(path.dirname(local), { recursive: true });
|
||||
fs.writeFileSync(local, buf);
|
||||
|
||||
Reference in New Issue
Block a user