import { app } from './app.js'; import { env } from './config/env.js'; import { tenantDb } from './config/database.js'; import { invalidateTenantCache } from './middlewares/plan-limits.middleware.js'; import { startSatSyncJob } from './jobs/sat-sync.job.js'; import { startWeeklyUpdateJob } from './jobs/weekly-update.job.js'; import { startMetricasInvalidationsJob } from './jobs/metricas-invalidations.job.js'; import { startNotificationsJob } from './jobs/notifications.job.js'; import { startSatSyncMonitorJob } from './jobs/sat-sync-monitor.job.js'; import { startRecordatoriosPeriodicosJob } from './jobs/recordatorios-periodicos.job.js'; const PORT = parseInt(env.PORT, 10); const server = app.listen(PORT, '0.0.0.0', () => { console.log(`API Server running on http://0.0.0.0:${PORT}`); console.log(`Environment: ${env.NODE_ENV}`); // Iniciar jobs programados. // En dev, los crons se omiten por default para no consumir recursos ni // disparar efectos (SAT queries, emails). Con ENABLE_CRONS_IN_DEV=1 se // arrancan los crons seguros (SAT sync/retry, métricas); el weekly-update // sigue siendo prod-only porque envía emails a owners reales. const cronsEnabled = env.NODE_ENV === 'production' || process.env.ENABLE_CRONS_IN_DEV === '1'; const sendRealEmails = env.NODE_ENV === 'production'; if (cronsEnabled) { startSatSyncJob(); startMetricasInvalidationsJob(); startSatSyncMonitorJob(); startRecordatoriosPeriodicosJob(); if (sendRealEmails) { startWeeklyUpdateJob(); startNotificationsJob(); } else { console.log('[Cron] weekly-update + notifications omitidos en dev (evita emails reales)'); } console.log(`[Cron] SAT + metricas + SAT monitor activos (NODE_ENV=${env.NODE_ENV}, ENABLE_CRONS_IN_DEV=${process.env.ENABLE_CRONS_IN_DEV ?? 'unset'})`); } else { console.log('[Cron] Jobs omitidos en dev (usar ENABLE_CRONS_IN_DEV=1 para activar)'); } }); // Graceful shutdown — close all tenant DB pools before exiting const gracefulShutdown = async (signal: string) => { console.log(`${signal} received. Shutting down gracefully...`); server.close(() => { console.log('HTTP server closed'); }); await tenantDb.shutdown(); process.exit(0); }; process.on('SIGTERM', () => gracefulShutdown('SIGTERM')); process.on('SIGINT', () => gracefulShutdown('SIGINT')); // PM2 cluster: cross-worker cache invalidation process.on('message', (msg: any) => { if (msg?.type === 'invalidate-tenant-cache' && msg.tenantId) { tenantDb.invalidatePool(msg.tenantId); invalidateTenantCache(msg.tenantId); } });