gohorsejobs/backoffice/src/main.ts
NANDO9322 c339c3fbaf feat(backoffice): corrige erro 500 e implementa seeder de banco
- Remove marcadores de conflito git em admin_service que causavam erro 500 em ListCompanies.
- Implementa SeederService no backend Go com streaming SSE para logs em tempo real.
- Expõe endpoints: GET /api/v1/seeder/seed/stream e POST /api/v1/seeder/reset.
- Atualiza config do frontend para apontar URL do seeder para a API backend.
- Corrige erros de sintaxe na UI do dashboard Backoffice e implementa busca de estatísticas.
- Garante lógica correta de UPSERT no seeder (RETURNING id) usando colunas 'identifier' e 'full_name' para evitar abortar transações.
- Corrige constraint de role em user_companies no seeder para usar 'admin'.
2026-01-09 12:21:56 -03:00

105 lines
3.4 KiB
TypeScript

import { NestFactory } from '@nestjs/core';
import {
FastifyAdapter,
NestFastifyApplication,
} from '@nestjs/platform-fastify';
import { ValidationPipe, Logger } from '@nestjs/common';
import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';
import { AppModule } from './app.module';
import compression from '@fastify/compress';
import helmet from '@fastify/helmet';
import cookie from '@fastify/cookie';
async function bootstrap() {
const dbUrl = process.env.DATABASE_URL || 'NOT_SET';
console.log(`[DEBUG] DATABASE_URL loaded: ${dbUrl.replace(/:[^:]*@/, ':***@')}`); // Mask password
// Create Fastify adapter with Pino logging
const adapter = new FastifyAdapter({
logger: {
level: process.env.NODE_ENV === 'production' ? 'info' : 'debug',
transport:
process.env.NODE_ENV !== 'production'
? { target: 'pino-pretty', options: { colorize: true } }
: undefined,
},
trustProxy: true, // Required for getting real IP behind reverse proxy
bodyLimit: 10 * 1024 * 1024, // 10MB
});
const app = await NestFactory.create<NestFastifyApplication>(
AppModule,
adapter,
{ rawBody: true },
);
// Register Fastify plugins
await app.register(compression, { encodings: ['gzip', 'deflate'] });
await app.register(helmet, {
contentSecurityPolicy: process.env.NODE_ENV === 'production',
});
await app.register(cookie); // Enable cookie parsing for JWT auth
// CORS configuration (Fastify-native)
app.enableCors({
origin: (origin, callback) => {
// Parse CORS_ORIGINS from env (comma-separated)
const envOrigins = process.env.CORS_ORIGINS?.split(',').map(o => o.trim()) || [];
const allowedOrigins = [
'http://localhost:3000',
'http://localhost:8963',
...envOrigins,
].filter(Boolean);
if (!origin || allowedOrigins.includes(origin)) {
callback(null, true);
} else {
callback(new Error('Not allowed by CORS'), false);
}
},
credentials: true,
methods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'],
allowedHeaders: ['Content-Type', 'Authorization', 'X-Requested-With'],
maxAge: 86400, // 24 hours preflight cache
});
// Global validation pipe
app.useGlobalPipes(
new ValidationPipe({
whitelist: true,
transform: true,
forbidNonWhitelisted: true,
transformOptions: { enableImplicitConversion: true },
}),
);
// Swagger documentation
const config = new DocumentBuilder()
.setTitle('GoHorse Backoffice API')
.setDescription('SaaS Administration and Subscription Management')
.setVersion('1.0')
.addTag('Stripe')
.addTag('Plans')
.addTag('Admin Dashboard')
.addBearerAuth()
.build();
SwaggerModule.setup('docs', app, SwaggerModule.createDocument(app, config));
// Health check endpoint
const fastifyInstance = app.getHttpAdapter().getInstance();
fastifyInstance.get('/health', async () => ({ status: 'ok', timestamp: new Date().toISOString() }));
// Start server
const port = process.env.BACKOFFICE_PORT || 3001;
const host = '0.0.0.0'; // Force 0.0.0.0 to allow container binding even if env injects public IP
await app.listen(port, host);
const logger = new Logger('Bootstrap');
logger.log(`🚀 Backoffice API running on: http://${host}:${port}`);
logger.log(`📚 Swagger docs: http://${host}:${port}/api/docs`);
logger.log(`❤️ Health check: http://${host}:${port}/health`);
}
void bootstrap();