import Event from '../model/event.model'; import User from '../model/user.model'; import sendMail from '../util/mailer.util'; import Queue from 'bull'; import { REDIS_URL } from '../config/env.config'; // Create Bull queues for async job processing const emailQueue = new Queue('email-notifications', REDIS_URL || 'redis://localhost:6379'); const reminderQueue = new Queue('event-reminders', REDIS_URL || 'redis://localhost:6379'); // ==================== EMAIL QUEUE PROCESSOR ==================== emailQueue.process(async (job) => { const { to, subject, text, html } = job.data; await sendMail({ to, subject, text, html }); }); // ==================== REMINDER QUEUE PROCESSOR ==================== reminderQueue.process(async (job) => { const { eventId } = job.data; await processEventReminder(eventId); }); // ==================== QUEUE EVENT REMINDER (Stateless) ==================== export const queueEventReminder = async (eventId: string, sendAt: Date) => { const delay = sendAt.getTime() - Date.now(); if (delay > 0) { await reminderQueue.add( { eventId }, { delay, attempts: 3, backoff: { type: 'exponential', delay: 2000 } } ); } }; // ==================== PROCESS EVENT REMINDER ==================== async function processEventReminder(eventId: string) { try { const event = await Event.findById(eventId) .populate('participants.user', 'email username') .populate('organization', 'username'); if (!event || event.reminderSent) return; for (const participant of event.participants) { const user = participant.user as any; await emailQueue.add({ to: user.email, subject: `Reminder: ${event.title} - Tomorrow`, text: `Reminder: ${event.title} starts tomorrow at ${event.startDate.toLocaleString()}`, html: generateReminderHTML(event, user) }); } event.reminderSent = true; await event.save(); console.log(`Reminder sent for event: ${event.title}`); } catch (err) { console.error('Error sending reminder:', err); throw err; // Bull will retry } } function generateReminderHTML(event: any, user: any): string { return `
Hi ${user.username},
This is a reminder that you're registered for:
Code: ${event.code}
Start: ${event.startDate.toLocaleString()}
${updateMessage}
You've been registered for ${event.title}