import http from 'node:http'; import { configFromEnv } from './config.js'; import { AppserviceDaemon } from './server.js'; const cfg = configFromEnv(); const daemon = new AppserviceDaemon(cfg); const MAX_BODY_BYTES = 1024 * 1024; const server = http.createServer((req, res) => { const chunks: Buffer[] = []; let received = 0; let rejected = false; req.on('data', (chunk: Buffer) => { received += chunk.length; if (received > MAX_BODY_BYTES) { rejected = true; res.writeHead(413, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ errcode: 'M_TOO_LARGE', error: 'request body too large' })); req.destroy(); return; } chunks.push(chunk); }); req.on('end', () => { if (rejected) return; void (async () => { const url = new URL(req.url ?? '/', 'http://localhost'); let body: unknown; try { const raw = Buffer.concat(chunks).toString(); body = raw ? JSON.parse(raw) : undefined; } catch { res.writeHead(400, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ errcode: 'M_NOT_JSON', error: 'invalid json' })); return; } const result = await daemon.handle({ method: req.method ?? 'GET', path: url.pathname, searchParams: url.searchParams, authorizationHeader: req.headers.authorization, body, }); res.writeHead(result.status, { 'Content-Type': 'application/json' }); res.end(JSON.stringify(result.body)); })().catch((error: unknown) => { console.error('request failed:', error); if (res.headersSent) { res.destroy(); return; } res.writeHead(500, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ error: 'internal error' })); }); }); }); server.listen(cfg.port, () => { console.log( `mosaic-as listening on :${cfg.port} (homeserver ${cfg.homeserverUrl}, domain ${cfg.domain})`, ); if (cfg.bridgeTokens.length === 0) { console.warn('WARNING: MOSAIC_AS_BRIDGE_TOKENS is empty — bridge API will deny all requests'); } });