// CODING-RULES §09 (PGD-OBS-001): tracing PRECISA ser o primeiro import. // OTel NodeSDK faz monkey-patch dos módulos node:http/pg/etc. antes do Nest. import './tracing'; import { NestFactory } from '@nestjs/core'; import { VersioningType, type INestApplication } from '@nestjs/common'; import { ConfigService } from '@nestjs/config'; import { Logger } from 'nestjs-pino'; import helmet from 'helmet'; import compression from 'compression'; import { AppModule } from './app/app.module'; import type { Env } from './app/config/env.schema'; async function bootstrap(): Promise { const app: INestApplication = await NestFactory.create(AppModule, { bufferLogs: true, // logs do boot vão pro Pino quando ele subir }); // Logger Pino global (substitui o NestLogger padrão). app.useLogger(app.get(Logger)); const config = app.get(ConfigService) as ConfigService; // CORS — origens vindas do EnvSchema (já parseadas em array). app.enableCors({ origin: config.get('CORS_ORIGINS', { infer: true }), credentials: true, methods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'], allowedHeaders: [ 'Content-Type', 'Authorization', 'X-Request-Id', 'X-Correlation-Id', 'Idempotency-Key', ], exposedHeaders: ['X-Request-Id'], maxAge: 600, }); // Helmet — headers de segurança canônicos. app.use( helmet({ contentSecurityPolicy: false, // SPA serve CSP via Nginx; API JSON dispensa. crossOriginEmbedderPolicy: false, }), ); // Compression — gzip/br. app.use(compression()); // Versionamento via URI: /api/v1/... const globalPrefix = config.get('API_GLOBAL_PREFIX', { infer: true }); const apiVersion = config.get('API_VERSION', { infer: true }).replace(/^v/, ''); app.setGlobalPrefix(globalPrefix); app.enableVersioning({ type: VersioningType.URI, defaultVersion: apiVersion, prefix: 'v', }); // Graceful shutdown — Nest emite SIGTERM/SIGINT → hooks rodam → dreno de conexões. // Compose: stop_grace_period: 30s + HAProxy drain 30s (CODING-RULES §18). app.enableShutdownHooks(); const port = config.get('API_PORT', { infer: true }); const host = config.get('API_HOST', { infer: true }); await app.listen(port, host); app .get(Logger) .log( `SAR API pronta em http://${host}:${port}/${globalPrefix}/${apiVersion === '0' ? '' : `v${apiVersion}`}`, 'Bootstrap', ); } bootstrap().catch((err) => { // Antes do Logger Pino estar disponível, qualquer erro de boot cai aqui. console.error('[bootstrap] crash fatal — encerrando:', err); process.exit(1); });