// Static host for Learning Lab Express. Proxies /api to the learning-lab engine // so the browser stays same-origin (no CORS setup needed on the engine). import http from 'node:http'; import { readFile } from 'node:fs/promises'; import { extname, join, normalize } from 'node:path'; import { fileURLToPath } from 'node:url'; const ROOT = fileURLToPath(new URL('.', import.meta.url)); const PORT = Number(process.env.PORT || 3000); const ENGINE = new URL(process.env.ENGINE_URL || 'http://localhost:8000'); const TYPES = { '.html': 'text/html; charset=utf-8', '.js': 'text/javascript; charset=utf-8', '.css': 'text/css', '.svg': 'image/svg+xml' }; function proxy(req, res) { const upstream = http.request({ host: ENGINE.hostname, port: ENGINE.port, path: req.url, method: req.method, headers: { ...req.headers, host: ENGINE.host }, }, up => { res.writeHead(up.statusCode, up.headers); up.pipe(res); }); upstream.on('error', () => { res.writeHead(502, { 'content-type': 'application/json' }); res.end(JSON.stringify({ detail: `Engine learning-lab tidak dapat dihubungi di ${ENGINE.origin}` })); }); req.pipe(upstream); } // Clerk publishable keys are public; default to the engine's own dev key so both share one Clerk instance. async function clerkKey() { if (process.env.CLERK_PUBLISHABLE_KEY) return process.env.CLERK_PUBLISHABLE_KEY; try { const env = await readFile(join(ROOT, '../../learning-lab/backend/.env'), 'utf8'); return env.match(/^VITE_CLERK_PUBLISHABLE_KEY=["']?([^"'\s]+)/m)?.[1] || ''; } catch { return ''; } } async function serve(req, res) { const path = new URL(req.url, 'http://x').pathname; if (path === '/config.js') { res.writeHead(200, { 'content-type': TYPES['.js'] }); return res.end(`window.LLE_CONFIG=${JSON.stringify({ clerkKey: await clerkKey() })};`); } const name = path === '/' ? 'index.html' : normalize(path).replace(/^([/\\])+/, ''); if (name.startsWith('..')) { res.writeHead(403); return res.end(); } try { const body = await readFile(join(ROOT, name)); res.writeHead(200, { 'content-type': TYPES[extname(name)] || 'application/octet-stream' }); res.end(body); } catch { res.writeHead(404); res.end('Not found'); } } http.createServer((req, res) => (req.url.startsWith('/api/') ? proxy(req, res) : serve(req, res))) .listen(PORT, () => console.log(`Learning Lab Express: http://localhost:${PORT} → engine ${ENGINE.origin}`));