1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
|
/**
* 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<void> {
// 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);
});
|