/** * server.ts * * Entry point do backend Helpdesk. * * Responsabilidades (em ordem): * 1. Executar migrações do banco no boot * 2. Criar e configurar a instância Fastify * 3. Registrar plugins globais * 4. Registrar módulos de rotas * 5. Iniciar o servidor HTTP */ import 'dotenv/config'; // Carrega .env antes de qualquer outro import import Fastify from 'fastify'; import cors from '@fastify/cors'; import sensible from '@fastify/sensible'; import { validatorCompiler } from 'fastify-type-provider-zod'; import { runMigrations } from '@/db/migrate.js'; import dbPlugin from '@/plugins/db.plugin.js'; import authPlugin from '@/plugins/auth.plugin.js'; import authRoutes from '@/modules/auth/auth.routes.js'; import ticketsRoutes from '@/modules/tickets/tickets.routes.js'; import interactionsRoutes from '@/modules/interactions/interactions.routes.js'; import categoriesRoutes from '@/modules/categories/categories.routes.js'; import settingsRoutes from '@/modules/settings/settings.routes.js'; import statsRoutes from '@/modules/stats/stats.routes.js'; import usersRoutes from '@/modules/users/users.routes.js'; import eventsPlugin from '@/plugins/events.plugin.js'; import eventsRoutes from '@/plugins/events.routes.js'; // ─── Configuração ───────────────────────────────────────────────────────────── const PORT = Number(process.env['PORT'] ?? 3000); const HOST = process.env['HOST'] ?? '0.0.0.0'; const IS_DEV = process.env['NODE_ENV'] !== 'production'; // ─── Factory da aplicação ───────────────────────────────────────────────────── // Exportar como função facilita testes de integração (cada teste cria uma instância) export async function buildApp() { const fastify = Fastify({ logger: { level: IS_DEV ? 'info' : 'warn', // Formatação legível no terminal em desenvolvimento transport: IS_DEV ? { target: 'pino-pretty', options: { colorize: true } } : undefined, }, }); // Configura o Fastify para usar validação com Zod fastify.setValidatorCompiler(validatorCompiler); // ── Plugins Globais ─────────────────────────────────────────────────────── // CORS: permite requisições do frontend (Vite roda na porta 5173) await fastify.register(cors, { origin: IS_DEV ? 'http://localhost:5173' : process.env['FRONTEND_URL'], methods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE'], }); // Sensible: adiciona helpers como reply.notFound(), reply.badRequest(), etc. await fastify.register(sensible); // Banco de dados: disponibiliza fastify.db em todas as rotas await fastify.register(dbPlugin); // JWT: disponibiliza fastify.authenticate em todas as rotas await fastify.register(authPlugin); // ── Plugins Locais ──────────────────────────────────────────────────────── await fastify.register(eventsPlugin); // ── Rotas ───────────────────────────────────────────────────────────────── // Auth: rotas públicas (sem authenticate) await fastify.register(authRoutes, { prefix: '/api/v1/auth' }); await fastify.register(statsRoutes, { prefix: '/api/v1/stats' }); await fastify.register(eventsRoutes, { prefix: '/api/v1/events' }); await fastify.register(categoriesRoutes, { prefix: '/api/v1/categories' }); await fastify.register(settingsRoutes, { prefix: '/api/v1/settings' }); await fastify.register(usersRoutes, { prefix: '/api/v1/users' }); await fastify.register(ticketsRoutes, { prefix: '/api/v1/tickets' }); await fastify.register(interactionsRoutes, { prefix: '/api/v1' }); // Health check — útil para monitoramento e load balancer fastify.get('/health', async () => ({ status: 'ok', timestamp: new Date().toISOString() })); return fastify; } // ─── Bootstrap ─────────────────────────────────────────────────────────────── async function start(): Promise { // 1. Aplica migrações pendentes antes de abrir o servidor para tráfego await runMigrations(); // 2. Cria a instância configurada const app = await buildApp(); // 3. Graceful shutdown — garante que requests em andamento terminem antes de fechar const gracefulShutdown = async (signal: string) => { app.log.info(`Sinal ${signal} recebido. Encerrando servidor...`); await app.close(); process.exit(0); }; process.on('SIGTERM', () => gracefulShutdown('SIGTERM')); // Kubernetes / systemd process.on('SIGINT', () => gracefulShutdown('SIGINT')); // Ctrl+C no terminal // 4. Inicia o servidor await app.listen({ port: PORT, host: HOST }); } start().catch((err) => { console.error('❌ Falha ao iniciar o servidor:', err); process.exit(1); });