Files
orgchain/server.js
T

23 lines
1.8 KiB
JavaScript
Raw Normal View History

// Static production server; no wallet, signing, chain state, RPC, DB, or secrets on the server.
import http from 'node:http';
import { readFile } from 'node:fs/promises';
import { resolve, extname, sep } from 'node:path';
const root = resolve('dist');
const types = { '.html':'text/html; charset=utf-8', '.js':'text/javascript; charset=utf-8', '.css':'text/css; charset=utf-8', '.svg':'image/svg+xml', '.txt':'text/plain; charset=utf-8', '.json':'application/json' };
const security = {
'Content-Security-Policy': "default-src 'self'; script-src 'self'; style-src 'self'; img-src 'self' data: blob:; frame-src blob:; connect-src 'self'; object-src 'none'; base-uri 'none'; frame-ancestors 'none'; form-action 'none'",
'X-Content-Type-Options':'nosniff', 'Referrer-Policy':'no-referrer', 'X-Frame-Options':'DENY',
'Permissions-Policy':'camera=(), microphone=(), geolocation=()',
};
http.createServer(async (req,res) => {
const send = (status, body, headers={}) => { res.writeHead(status, {...security,...headers}); res.end(req.method==='HEAD' ? undefined : body); };
if (!['GET','HEAD'].includes(req.method)) return send(405,'Method not allowed',{'Allow':'GET, HEAD'});
try {
const path = decodeURIComponent(new URL(req.url,'http://localhost').pathname);
if (path==='/healthz') return send(200,'ok',{'Content-Type':'text/plain'});
const target=resolve(root, '.'+(path==='/' ? '/index.html' : path));
if (!target.startsWith(root+sep)) return send(403,'Forbidden');
const data=await readFile(target);
send(200,data,{'Content-Type':types[extname(target)]??'application/octet-stream','Cache-Control':path.startsWith('/assets/')?'public, max-age=31536000, immutable':'no-cache'});
} catch { send(404,'Not found'); }
}).listen(Number(process.env.PORT || 8080),'0.0.0.0',()=>console.log(`Super Testnet static server listening on ${process.env.PORT || 8080}`));