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>
|
</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);
|
const parsed = new URL(u);
|
||||||
let p = parsed.pathname;
|
let p = parsed.pathname;
|
||||||
if (parsed.search) {
|
if (parsed.search) {
|
||||||
let safe = parsed.search.slice(1).replace(/[^a-zA-Z0-9._-]/g, '_');
|
let safe = parsed.search.slice(1).replace(/[^a-zA-Z0-9._-]/g, '_');
|
||||||
// Hash overly long query strings to avoid ENAMETOOLONG
|
|
||||||
if (safe.length > 80) {
|
if (safe.length > 80) {
|
||||||
safe = crypto.createHash('md5').update(parsed.search).digest('hex').slice(0, 12);
|
safe = crypto.createHash('md5').update(parsed.search).digest('hex').slice(0, 12);
|
||||||
}
|
}
|
||||||
p += '_q_' + safe;
|
p += '_q_' + safe;
|
||||||
}
|
}
|
||||||
// also hash if path itself is way too long
|
// Append correct extension if missing — so express.static sends proper Content-Type
|
||||||
const basename = path.basename(p);
|
const baseName = path.basename(p);
|
||||||
if (basename.length > 200) {
|
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 ext = path.extname(p);
|
||||||
const dir = path.dirname(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(/^\/+/, '');
|
return p.replace(/^\/+/, '');
|
||||||
}
|
}
|
||||||
@@ -99,7 +123,8 @@ async function main() {
|
|||||||
if (!rurl.startsWith('http')) return;
|
if (!rurl.startsWith('http')) return;
|
||||||
try {
|
try {
|
||||||
const buf = await resp.body();
|
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 */ }
|
} catch (e) { /* redirect/204/etc */ }
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -115,10 +140,10 @@ async function main() {
|
|||||||
// Write resources
|
// Write resources
|
||||||
fs.mkdirSync(MIRROR, { recursive: true });
|
fs.mkdirSync(MIRROR, { recursive: true });
|
||||||
const mapping = new Map(); // absUrl → /q/mirror/<safepath>
|
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);
|
const u = new URL(rurl);
|
||||||
if (u.hostname !== HOST) continue; // skip third-party (cdnjs etc) — let browser load them
|
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);
|
const local = path.join(MIRROR, safe);
|
||||||
fs.mkdirSync(path.dirname(local), { recursive: true });
|
fs.mkdirSync(path.dirname(local), { recursive: true });
|
||||||
fs.writeFileSync(local, buf);
|
fs.writeFileSync(local, buf);
|
||||||
|
|||||||
Reference in New Issue
Block a user