mirror of
https://github.com/JogmansonReal/Radicade.git
synced 2026-10-25 15:09:26 -07:00
47 lines
4.2 KiB
JavaScript
47 lines
4.2 KiB
JavaScript
import http from 'node:http';
|
|
import fs from 'node:fs/promises';
|
|
import path from 'node:path';
|
|
import { fileURLToPath } from 'node:url';
|
|
import { servePublicCache, bridgeScript } from './public-cache.mjs';
|
|
import { serveRadicade, backendOrigin } from './radicade-adapter.mjs';
|
|
const archiveMode = process.env.RECNET_DATA_MODE === 'archive';
|
|
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../../artifacts/recroom-network-mirror');
|
|
const types = {'.html':'text/html; charset=utf-8','.js':'text/javascript','.css':'text/css','.json':'application/json','.svg':'image/svg+xml','.png':'image/png','.jpg':'image/jpeg','.jpeg':'image/jpeg','.webp':'image/webp','.avif':'image/avif','.ico':'image/x-icon','.woff':'font/woff','.woff2':'font/woff2','.ttf':'font/ttf'};
|
|
http.createServer(async (req, res) => {
|
|
res.setHeader('Content-Security-Policy', "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob:; font-src 'self' data:; connect-src 'self'; media-src 'self' blob:; form-action 'none'; frame-src 'none'; object-src 'none'; base-uri 'self'");
|
|
res.setHeader('X-Content-Type-Options', 'nosniff');
|
|
res.setHeader('Cache-Control', 'no-store');
|
|
if (!['GET','HEAD'].includes(req.method)) { res.writeHead(501, {'Content-Type':'application/json'}); res.end('{"error":"offline_preview","message":"Login and server actions are not connected in this local copy."}'); return; }
|
|
try {
|
|
const url = new URL(req.url, 'http://127.0.0.1');
|
|
if (!archiveMode && url.pathname.startsWith('/_radicade/v2/')) {
|
|
url.pathname = url.pathname.replace('/_radicade/v2/', '/_public/');
|
|
}
|
|
if (archiveMode ? await servePublicCache(url, req, res, root) : await serveRadicade(url, req, res)) return;
|
|
if (url.pathname === '/static/img/NoSearchResult.png') {
|
|
res.writeHead(200, {'Content-Type':'image/svg+xml'});
|
|
res.end('<svg xmlns="http://www.w3.org/2000/svg" width="160" height="120"><text x="80" y="70" fill="#aaa" font-size="42" text-anchor="middle">[ = )</text></svg>');
|
|
return;
|
|
}
|
|
const filename = path.resolve(root, '.' + url.pathname);
|
|
if (filename !== root && !filename.startsWith(root + path.sep)) throw new Error('Invalid path');
|
|
let actual = filename;
|
|
const stat = await fs.stat(actual);
|
|
if (stat.isDirectory()) actual = path.join(actual, 'index.html');
|
|
let content = await fs.readFile(actual);
|
|
const ext = path.extname(actual);
|
|
const dataPrefix = archiveMode ? '/_public/' : '/_radicade/v2/';
|
|
if (ext === '.js' || ext === '.html') content = Buffer.from(content.toString()
|
|
.replaceAll('https://img.recroom.network/', dataPrefix + 'img/')
|
|
.replaceAll('/_public/img/', dataPrefix + 'img/'));
|
|
if (ext === '.html') {
|
|
const previewScript = `<script>document.addEventListener('click',function(e){const a=e.target.closest('a');const b=e.target.closest('button');if(b&&/^log\\s?in$/i.test(b.textContent.trim())){e.preventDefault();e.stopImmediatePropagation();alert('Login is not connected in this local preview.');return;}if(a){const u=new URL(a.href,location.href);if(u.origin===location.origin&&!u.hash){e.preventDefault();e.stopImmediatePropagation();location.assign(u.href);}}},true);</script>`;
|
|
const sourceLabel=archiveMode?'Cached recroom.network data':'Radicade public data · '+backendOrigin.replace(/[&<>"']/g,'');
|
|
const activeBridge = bridgeScript.replaceAll('/_public/', dataPrefix);
|
|
content = Buffer.from(content.toString().replace('<head>', '<head>' + activeBridge + previewScript).replace('</body>', '<div style="position:fixed;bottom:12px;left:12px;z-index:2147483647;background:#202020;color:#fff;padding:10px 16px;border:1px solid #888;border-radius:8px;font:13px Arial;max-width:90vw">Local copy · '+sourceLabel+' · Login not connected</div></body>'));
|
|
}
|
|
res.writeHead(200, {'Content-Type':types[ext] || 'application/octet-stream'});
|
|
res.end(req.method === 'HEAD' ? undefined : content);
|
|
} catch { res.writeHead(404, {'Content-Type':'application/json'}); res.end('{"error":"not_archived","message":"This file or API is not available in the local copy."}'); }
|
|
}).listen(3040, '127.0.0.1', () => console.log('Local website copy: http://127.0.0.1:3040'));
|