import Fastify from 'fastify'; import fastifyWebsocket from '@fastify/websocket'; import { loadConfig } from './config.js'; import { getPool, closeDb } from './db.js'; import { registerHealthRoutes } from './routes/health.js'; import { registerTerminalRoutes } from './routes/terminals.js'; import { registerWsAttachRoute } from './ws/attach.js'; async function main(): Promise { const config = loadConfig(); const app = Fastify({ logger: { level: config.LOG_LEVEL }, }); app.removeContentTypeParser(['application/json']); app.addContentTypeParser('application/json', { parseAs: 'string' }, (_req, body, done) => { const str = (body as string) ?? ''; if (str.trim().length === 0) { done(null, {}); return; } try { done(null, JSON.parse(str)); } catch (err) { done(err as Error, undefined); } }); getPool(config.DATABASE_URL); await app.register(fastifyWebsocket); registerHealthRoutes(app); registerTerminalRoutes(app, config.TMUX_CONF_PATH); registerWsAttachRoute(app, config.TMUX_CONF_PATH); const shutdown = async (signal: string) => { app.log.info(`received ${signal}, shutting down`); try { await app.close(); await closeDb(); process.exit(0); } catch (err) { app.log.error(err); process.exit(1); } }; process.on('SIGINT', () => void shutdown('SIGINT')); process.on('SIGTERM', () => void shutdown('SIGTERM')); await app.listen({ port: config.PORT, host: config.HOST }); app.log.info(`booterm listening on http://${config.HOST}:${config.PORT}`); } main().catch((err) => { console.error('Fatal startup error:', err); process.exit(1); });