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('');
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 = ``;