mirror of
https://github.com/JogmansonReal/Radicade.git
synced 2026-10-25 15:09:26 -07:00
76 lines
3.7 KiB
JavaScript
76 lines
3.7 KiB
JavaScript
import fs from 'node:fs/promises';
|
|
import path from 'node:path';
|
|
import { createHash } from 'node:crypto';
|
|
|
|
export function upstreamFor(url) {
|
|
const routes = {
|
|
'/_public/rooms/featuredrooms/current': 'https://rooms.recroom.network/featuredrooms/current',
|
|
'/_public/rooms/rooms/bulk': 'https://rooms.recroom.network/rooms/bulk',
|
|
'/_public/accounts/account/bulk': 'https://accounts.recroom.network/account/bulk',
|
|
'/_public/apim/apis/api/images/v3/feed/global': 'https://apim.recroom.network/apis/api/images/v3/feed/global',
|
|
};
|
|
if (routes[url.pathname]) {
|
|
const target = new URL(routes[url.pathname]);
|
|
for (const [key,value] of url.searchParams) {
|
|
if (!['id','ids','skip','take','include'].includes(key) || !/^[0-9,]{1,2000}$/.test(value)) return null;
|
|
target.searchParams.append(key,value);
|
|
}
|
|
return target;
|
|
}
|
|
const image = url.pathname.match(/^\/_public\/img\/([a-zA-Z0-9_.%-]{1,180})$/);
|
|
if (image) {
|
|
return new URL('https://img.recroom.network/' + image[1]);
|
|
}
|
|
return null;
|
|
}
|
|
|
|
export async function servePublicCache(url, req, res, root) {
|
|
if (!url.pathname.startsWith('/_public/')) return false;
|
|
const upstream = upstreamFor(url);
|
|
if (!upstream) { res.writeHead(404); res.end(); return true; }
|
|
const key = createHash('sha256').update(upstream.href).digest('hex');
|
|
const dir = path.join(root, 'public-cache');
|
|
try {
|
|
let bytes, type;
|
|
try {
|
|
bytes = await fs.readFile(path.join(dir,key));
|
|
type = await fs.readFile(path.join(dir,key+'.type'),'utf8');
|
|
} catch {
|
|
const response = await fetch(upstream, {redirect:'error', signal:AbortSignal.timeout(20000)});
|
|
if (response.status === 404 && upstream.hostname === 'img.recroom.network') {
|
|
res.writeHead(200, {'Content-Type':'image/svg+xml','X-Mirror-Placeholder':'source-image-missing'});
|
|
res.end('<svg xmlns="http://www.w3.org/2000/svg" width="480" height="300"><rect width="480" height="300" fill="#303030"/><text x="240" y="155" fill="#aaa" font-family="Arial" font-size="20" text-anchor="middle">Image unavailable in source archive</text></svg>');
|
|
return true;
|
|
}
|
|
if (!response.ok) throw new Error(`Public source returned ${response.status}`);
|
|
type = response.headers.get('content-type') || 'application/octet-stream';
|
|
bytes = Buffer.from(await response.arrayBuffer());
|
|
if (bytes.length > 15 * 1024 * 1024) throw new Error('Response too large');
|
|
await fs.mkdir(dir,{recursive:true});
|
|
await fs.writeFile(path.join(dir,key),bytes);
|
|
await fs.writeFile(path.join(dir,key+'.type'),type);
|
|
}
|
|
res.writeHead(200, {'Content-Type':type,'Cache-Control':'public, max-age=3600'});
|
|
res.end(req.method === 'HEAD' ? undefined : bytes);
|
|
} catch(error) {
|
|
console.error('Public cache:', upstream.href, error.message);
|
|
res.writeHead(502, {'Content-Type':'application/json'});
|
|
res.end(JSON.stringify({error:'public_snapshot_unavailable'}));
|
|
}
|
|
return true;
|
|
}
|
|
|
|
export const bridgeScript = `<script>
|
|
const mirrorFetch=window.fetch.bind(window);
|
|
window.fetch=function(input,init){
|
|
const u=new URL(input instanceof Request?input.url:input,location.href);
|
|
const routes={'rooms.recroom.network':'rooms','apim.recroom.network':'apim','accounts.recroom.network':'accounts'};
|
|
if(routes[u.hostname]){
|
|
const method=(init?.method||(input instanceof Request?input.method:'GET')).toUpperCase();
|
|
if(method!=='GET'&&method!=='HEAD')return Promise.resolve(new Response('{"error":"offline_preview"}',{status:501}));
|
|
return mirrorFetch('/_public/'+routes[u.hostname]+u.pathname+u.search,{method,signal:init?.signal,credentials:'omit',cache:'no-store'});
|
|
}
|
|
return mirrorFetch(input,init);
|
|
};
|
|
</script>`;
|