import { config } from 'dotenv'; import { resolve } from 'node:path'; // Load .env from monorepo root (cwd is apps/gateway when run via pnpm filter) config({ path: resolve(process.cwd(), '../../.env') }); config(); // Also load apps/gateway/.env if present (overrides) import './tracing.js'; import 'reflect-metadata'; import { NestFactory } from '@nestjs/core'; import { Logger, ValidationPipe } from '@nestjs/common'; import { FastifyAdapter, type NestFastifyApplication } from '@nestjs/platform-fastify'; import helmet from '@fastify/helmet'; import { AppModule } from './app.module.js'; import { mountAuthHandler } from './auth/auth.controller.js'; import { mountMcpHandler } from './mcp/mcp.controller.js'; import { McpService } from './mcp/mcp.service.js'; async function bootstrap(): Promise { const logger = new Logger('Bootstrap'); if (!process.env['BETTER_AUTH_SECRET']) { throw new Error('BETTER_AUTH_SECRET is required'); } if ( process.env['AUTHENTIK_CLIENT_ID'] && (!process.env['AUTHENTIK_CLIENT_SECRET'] || !process.env['AUTHENTIK_ISSUER']) ) { console.warn( '[warn] AUTHENTIK_CLIENT_ID is set but AUTHENTIK_CLIENT_SECRET or AUTHENTIK_ISSUER is missing — Authentik SSO will not work', ); } const app = await NestFactory.create( AppModule, new FastifyAdapter({ bodyLimit: 1_048_576 }), ); app.enableCors({ origin: process.env['GATEWAY_CORS_ORIGIN'] ?? 'http://localhost:3000', credentials: true, }); await app.register(helmet as never, { contentSecurityPolicy: false }); app.useGlobalPipes( new ValidationPipe({ whitelist: true, forbidNonWhitelisted: true, transform: true, }), ); mountAuthHandler(app); mountMcpHandler(app, app.get(McpService)); const port = Number(process.env['GATEWAY_PORT'] ?? 4000); await app.listen(port, '0.0.0.0'); logger.log(`Gateway listening on port ${port}`); } bootstrap().catch((err: unknown) => { const logger = new Logger('Bootstrap'); logger.error('Fatal startup error', err instanceof Error ? err.stack : String(err)); process.exit(1); });