mirror of
https://github.com/JogmansonReal/Radicade.git
synced 2026-10-25 15:09:26 -07:00
57 lines
3.1 KiB
JavaScript
57 lines
3.1 KiB
JavaScript
import fs from 'node:fs/promises';
|
|
import path from 'node:path';
|
|
import { fileURLToPath } from 'node:url';
|
|
|
|
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../../artifacts/recroom-network-mirror');
|
|
const origin = 'https://recroom.network';
|
|
const allowed = new Set(['recroom.network', 'cdn.recroom.network']);
|
|
const queue = ['/', '/rooms', '/events', '/news', '/creator', '/shop', '/download'].map(p => origin + p);
|
|
const seen = new Set();
|
|
const manifest = [];
|
|
await fs.mkdir(root, { recursive: true });
|
|
function localPath(url) {
|
|
return (url.hostname === 'cdn.recroom.network' ? '/mirror-cdn' : '') + url.pathname;
|
|
}
|
|
function rewrite(text) {
|
|
return text.replaceAll('https://cdn.recroom.network', '/mirror-cdn')
|
|
.replaceAll('https://recroom.network', '');
|
|
}
|
|
function discover(text, base) {
|
|
const add = raw => {
|
|
try {
|
|
const value = raw.replaceAll('&', '&').replaceAll('\\/', '/');
|
|
const url = new URL(value, base);
|
|
if (!allowed.has(url.hostname) || url.protocol !== 'https:') return;
|
|
if (!/\.(?:js|css|woff2?|ttf|png|jpe?g|webp|svg|ico|avif)(?:$|\?)/i.test(url.pathname + url.search)) return;
|
|
url.hash = ''; if (!seen.has(url.href)) queue.push(url.href);
|
|
} catch {}
|
|
};
|
|
for (const m of text.matchAll(/(?:src|href|poster)=["']([^"']+)["']/g)) add(m[1]);
|
|
for (const m of text.matchAll(/url\(["']?([^\s)'";]+)["']?\)/g)) add(m[1]);
|
|
for (const m of text.matchAll(/["'`](https:\/\/[^"'`\s<>]+|\/(?:_next|static|logos|images)\/[^"'`\s<>]+)["'`]/g)) add(m[1]);
|
|
for (const m of text.matchAll(/["'](static\/(?:chunks|media)\/[^"']+)["']/g)) add('/_next/' + m[1]);
|
|
}
|
|
while (queue.length && seen.size < 350) {
|
|
const source = queue.shift(); if (seen.has(source)) continue; seen.add(source);
|
|
try {
|
|
const response = await fetch(source, { signal: AbortSignal.timeout(25000) });
|
|
if (!response.ok) throw new Error(`HTTP ${response.status}`);
|
|
const type = response.headers.get('content-type') || '';
|
|
const url = new URL(source);
|
|
let output = localPath(url);
|
|
if (type.includes('text/html')) output = output.replace(/\/$/, '') + '/index.html';
|
|
const filename = path.resolve(root, '.' + output);
|
|
if (!filename.startsWith(root + path.sep)) throw new Error('Invalid output path');
|
|
const bytes = Buffer.from(await response.arrayBuffer());
|
|
await fs.mkdir(path.dirname(filename), { recursive: true });
|
|
if (/javascript|text\/css|text\/html/.test(type)) {
|
|
const sourceText = bytes.toString('utf8'); discover(sourceText, source);
|
|
await fs.writeFile(filename, rewrite(sourceText));
|
|
} else await fs.writeFile(filename, bytes);
|
|
manifest.push({ source, file: output, type, bytes: bytes.length });
|
|
console.log(`${manifest.length}: ${output} (${bytes.length})`);
|
|
} catch (error) { manifest.push({ source, error: error.message }); console.log(`FAILED ${source}: ${error.message}`); }
|
|
}
|
|
await fs.writeFile(path.join(root, 'manifest.json'), JSON.stringify({ capturedAt: new Date().toISOString(), source: origin, files: manifest }, null, 2));
|
|
console.log(`Saved ${manifest.filter(x => !x.error).length} files; ${manifest.filter(x => x.error).length} failed; ${queue.length} queued. ${root}`);
|