/** * Custom server entry point. * * Boots Next.js, attaches Socket.io to the same HTTP server, and in * development mode also registers BullMQ recurring jobs and starts the * workers inline (so you don't need the separate crm-worker container). * * In production the workers run in the crm-worker container (Dockerfile.worker * → dist/worker.js) and this file only handles Next.js + Socket.io. */ import { createServer } from 'node:http'; import next from 'next'; import { initSocketServer } from '@/lib/socket/server'; import { logger } from '@/lib/logger'; const dev = process.env.NODE_ENV !== 'production'; const port = parseInt(process.env.PORT ?? '3000', 10); async function main(): Promise { const app = next({ dev, port }); const handle = app.getRequestHandler(); await app.prepare(); const httpServer = createServer((req, res) => { handle(req, res); }); // Attach Socket.io to the HTTP server (uses Redis adapter for pub/sub) initSocketServer(httpServer); logger.info('Socket.io initialized'); // In development, run BullMQ workers inline so a single `pnpm dev` is enough if (dev) { const { registerRecurringJobs } = await import('@/lib/queue/scheduler'); const { emailWorker } = await import('@/lib/queue/workers/email'); const { documentsWorker } = await import('@/lib/queue/workers/documents'); const { notificationsWorker } = await import('@/lib/queue/workers/notifications'); const { importWorker } = await import('@/lib/queue/workers/import'); const { exportWorker } = await import('@/lib/queue/workers/export'); await registerRecurringJobs(); logger.info('BullMQ recurring jobs registered (dev mode)'); // Keep a reference so workers aren't GC'd void [emailWorker, documentsWorker, notificationsWorker, importWorker, exportWorker]; } httpServer.listen(port, () => { logger.info({ port, env: process.env.NODE_ENV }, 'Port Nimara CRM server listening'); }); } main().catch((err) => { logger.error(err, 'Failed to start server'); process.exit(1); });