79 lines
2.6 KiB
TypeScript
79 lines
2.6 KiB
TypeScript
import * as _cluster from 'node:cluster';
|
|
import * as os from 'node:os';
|
|
import { NestFactory } from '@nestjs/core';
|
|
import { ValidationPipe } from '@nestjs/common';
|
|
import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';
|
|
import { AppModule } from './app.module';
|
|
|
|
const cluster = _cluster as any; // Cast to 'any' bypasses the missing property errors
|
|
const isProduction = process.env.NODE_ENV === 'production';
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Clustering — fork one worker per CPU core in production
|
|
// ---------------------------------------------------------------------------
|
|
|
|
const WORKERS = isProduction
|
|
? Math.min(os.cpus().length, 4) // cap at 4 workers to stay within DB pool
|
|
: 1; // single process in dev
|
|
|
|
if (WORKERS > 1 && cluster.isPrimary) {
|
|
console.log(`Primary ${process.pid} forking ${WORKERS} workers ...`);
|
|
for (let i = 0; i < WORKERS; i++) {
|
|
cluster.fork();
|
|
}
|
|
cluster.on('exit', (worker: any, code: number) => {
|
|
console.warn(`Worker ${worker.process.pid} exited (code ${code}), restarting ...`);
|
|
cluster.fork();
|
|
});
|
|
} else {
|
|
bootstrap();
|
|
}
|
|
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// NestJS bootstrap
|
|
// ---------------------------------------------------------------------------
|
|
|
|
async function bootstrap() {
|
|
const app = await NestFactory.create(AppModule, {
|
|
logger: isProduction ? ['error', 'warn', 'log'] : ['error', 'warn', 'log', 'debug', 'verbose'],
|
|
});
|
|
|
|
app.setGlobalPrefix('api');
|
|
|
|
// Request logging — only in development (too noisy / slow for prod)
|
|
if (!isProduction) {
|
|
app.use((req: any, _res: any, next: any) => {
|
|
console.log(`[REQ] ${req.method} ${req.url} auth=${req.headers.authorization ? 'yes' : 'no'}`);
|
|
next();
|
|
});
|
|
}
|
|
|
|
app.useGlobalPipes(
|
|
new ValidationPipe({
|
|
whitelist: false,
|
|
forbidNonWhitelisted: false,
|
|
transform: true,
|
|
}),
|
|
);
|
|
|
|
// CORS — in production nginx handles this; accept all origins behind the proxy
|
|
app.enableCors({
|
|
origin: isProduction ? true : ['http://localhost', 'http://localhost:5173'],
|
|
credentials: true,
|
|
});
|
|
|
|
// Swagger docs — available in all environments
|
|
const config = new DocumentBuilder()
|
|
.setTitle('HOA LedgerIQ API')
|
|
.setDescription('API for the HOA LedgerIQ')
|
|
.setVersion('2026.3.7')
|
|
.addBearerAuth()
|
|
.build();
|
|
const document = SwaggerModule.createDocument(app, config);
|
|
SwaggerModule.setup('api/docs', app, document);
|
|
|
|
await app.listen(3000);
|
|
console.log(`Backend worker ${process.pid} listening on port 3000`);
|
|
}
|