import { Worker } from 'bullmq'; import { ensureSchema } from './db/schema.js'; import { db } from './db/client.js'; import { runBirdMilestoneReminders, runMedicationReminders, startBirdMilestoneReminderScheduler, startMedicationReminderScheduler, } from './app.js'; import { adoptionReportQueueName, closeAdoptionReportQueue, type AdoptionReportJobData, type AdoptionReportJobResult, } from './queues/adoptionReportQueue.js'; import { birdMilestoneReminderQueueName, closeBirdMilestoneReminderQueue, type BirdMilestoneReminderJobData, type BirdMilestoneReminderJobResult, } from './queues/birdMilestoneReminderQueue.js'; import { closeMedicationReminderQueue, medicationReminderQueueName, type MedicationReminderJobData, type MedicationReminderJobResult, } from './queues/medicationReminderQueue.js'; import { redisConnection } from './queues/redisConnection.js'; import { renderAdoptionReportForBird } from './reports/adoptionReportJob.js'; let birdMilestoneWorker: Worker | null = null; let medicationReminderWorker: Worker | null = null; let adoptionReportWorker: Worker | null = null; const startWorker = async () => { await ensureSchema(); birdMilestoneWorker = new Worker( birdMilestoneReminderQueueName, async (job) => { const result = await runBirdMilestoneReminders(job.data.runDate); console.log( `Bird milestone reminder job completed for ${result.runDate}: checked=${result.checked}, sent=${result.sent}, skipped=${result.skipped}, failed=${result.failed}`, ); return result; }, { connection: redisConnection, concurrency: 1, }, ); birdMilestoneWorker.on('failed', (job, error) => { console.error(`Bird milestone reminder job failed: id=${job?.id ?? 'unknown'}`, error); }); medicationReminderWorker = new Worker( medicationReminderQueueName, async (job) => { const result = await runMedicationReminders(job.data.runDate, job.data.currentTime); console.log( `Medication reminder job completed for ${result.runDate} ${result.currentTime}: checked=${result.checked}, sent=${result.sent}, skipped=${result.skipped}, failed=${result.failed}`, ); return result; }, { connection: redisConnection, concurrency: 1, }, ); medicationReminderWorker.on('failed', (job, error) => { console.error(`Medication reminder job failed: id=${job?.id ?? 'unknown'}`, error); }); adoptionReportWorker = new Worker( adoptionReportQueueName, async (job) => { const pdf = await renderAdoptionReportForBird(job.data); console.log(`Adoption report job completed: id=${job.id ?? 'unknown'}, birdId=${job.data.birdId}, bytes=${pdf.length}`); return { pdfBase64: pdf.toString('base64'), }; }, { connection: redisConnection, concurrency: 1, }, ); adoptionReportWorker.on('failed', (job, error) => { console.error(`Adoption report job failed: id=${job?.id ?? 'unknown'}, birdId=${job?.data.birdId ?? 'unknown'}`, error); }); startBirdMilestoneReminderScheduler(); startMedicationReminderScheduler(); console.log('FlockPal worker started.'); }; const shutdown = async (signal: string) => { console.log(`FlockPal worker received ${signal}; shutting down.`); await birdMilestoneWorker?.close(); await medicationReminderWorker?.close(); await adoptionReportWorker?.close(); await closeBirdMilestoneReminderQueue(); await closeMedicationReminderQueue(); await closeAdoptionReportQueue(); await db.close(); process.exit(0); }; process.on('SIGINT', () => { void shutdown('SIGINT'); }); process.on('SIGTERM', () => { void shutdown('SIGTERM'); }); startWorker().catch((error) => { console.error('Failed to start FlockPal worker', error); process.exit(1); });