import { Job, Worker, type ConnectionOptions } from 'bullmq'; import { validateConfig } from '@hexahost/config'; import { prisma } from '@hexahost/database'; import { startHealthServer } from './health'; import { logger } from './logger'; import { WORKER_QUEUES, type WorkerQueueName } from './queues'; function createQueueWorker( queueName: WorkerQueueName, connection: ConnectionOptions, ): Worker { return new Worker( queueName, async (job: Job) => { logger.info( { queue: queueName, jobId: job.id, jobName: job.name }, 'Processing job', ); return { acknowledged: true, queue: queueName }; }, { connection }, ); } async function bootstrap(): Promise { const config = validateConfig(); const healthServer = startHealthServer(); const connection: ConnectionOptions = { url: config.REDIS_URL, maxRetriesPerRequest: null, }; const workers = WORKER_QUEUES.map((queueName) => createQueueWorker(queueName, connection), ); for (const worker of workers) { worker.on('completed', (job: Job | undefined) => { logger.info({ queue: worker.name, jobId: job?.id }, 'Job completed'); }); worker.on('failed', (job: Job | undefined, error: Error) => { logger.error( { queue: worker.name, jobId: job?.id, err: error }, 'Job failed', ); }); } logger.info({ queues: WORKER_QUEUES }, 'Worker started'); const shutdown = async (signal: string): Promise => { logger.info({ signal }, 'Shutting down worker'); await Promise.all(workers.map((worker) => worker.close())); await prisma.$disconnect(); healthServer.close(() => { process.exit(0); }); }; process.on('SIGINT', () => { void shutdown('SIGINT'); }); process.on('SIGTERM', () => { void shutdown('SIGTERM'); }); } void bootstrap();