// Static server for the built site behind the demos.githubnext.com gateway. // The gateway strips the /iamnbutler/stylize prefix before forwarding and sends it as X-Forwarded-Prefix. import { createReadStream, statSync } from 'node:fs'; import { createServer } from 'node:http'; import { extname, join, normalize, resolve } from 'node:path'; const ROOT = resolve(process.env.SITE_DIR ?? 'site'); const PORT = Number(process.env.PORT ?? 3214); const TYPES = { '.html': 'text/html; charset=utf-8', '.js': 'text/javascript; charset=utf-8', '.mjs': 'text/javascript; charset=utf-8', '.css': 'text/css; charset=utf-8', '.json': 'application/json; charset=utf-8', '.svg': 'image/svg+xml', '.png': 'image/png', '.jpg': 'image/jpeg', '.webp': 'image/webp', '.woff2': 'font/woff2', }; function stat(path) { try { return statSync(path); } catch { return null; } } createServer((req, res) => { const url = new URL(req.url, 'http://x'); if (url.pathname === '/healthz') return res.end('ok'); if (req.method !== 'GET' && req.method !== 'HEAD') return res.writeHead(405).end(); const path = normalize(decodeURIComponent(url.pathname)); let file = join(ROOT, path); if (!file.startsWith(ROOT)) return res.writeHead(404).end(); let info = stat(file); if (info?.isDirectory()) { // Directions load ../content/posts.json, so directory URLs need the trailing slash. if (!path.endsWith('/')) { const prefix = (req.headers['x-forwarded-prefix'] ?? '').replace(/\/$/, ''); return res.writeHead(301, { location: `${prefix}${path}/${url.search}` }).end(); } file = join(file, 'index.html'); info = stat(file); } if (!info?.isFile()) return res.writeHead(404, { 'content-type': 'text/plain' }).end('not found'); res.writeHead(200, { 'content-type': TYPES[extname(file)] ?? 'application/octet-stream', 'content-length': info.size, 'cache-control': 'private, no-cache', }); if (req.method === 'HEAD') return res.end(); createReadStream(file).pipe(res); }).listen(PORT, '0.0.0.0', () => console.log(`serving ${ROOT} on :${PORT}`));