File size: 4,370 Bytes
9a92a42
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
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 `
    <div style="font-family: Arial, sans-serif; padding: 20px;">
      <h2>Event Reminder</h2>
      <p>Hi ${user.username},</p>
      <p>This is a reminder that you're registered for:</p>
      <div style="background: #f5f5f5; padding: 15px; border-radius: 8px; margin: 20px 0;">
        <h3 style="margin: 0 0 10px 0;">${event.title}</h3>
        <p><strong>Code:</strong> ${event.code}</p>
        <p><strong>Start:</strong> ${event.startDate.toLocaleString()}</p>
      </div>
    </div>
  `;
}

// ==================== SCHEDULE ALL UPCOMING REMINDERS (Run on startup) ====================
export async function scheduleUpcomingReminders() {
  const now = new Date();
  const sevenDaysFromNow = new Date(now.getTime() + 7 * 24 * 60 * 60 * 1000);
  
  const upcomingEvents = await Event.find({
    status: 'published',
    reminderSent: false,
    startDate: { $gte: now, $lte: sevenDaysFromNow }
  });
  
  for (const event of upcomingEvents) {
    const reminderTime = new Date(event.startDate.getTime() - 24 * 60 * 60 * 1000);
    await queueEventReminder(event._id.toString(), reminderTime);
  }
  
  console.log(`Scheduled ${upcomingEvents.length} event reminders`);
}

export const sendEventUpdateNotification = async (eventId: string, updateMessage: string) => {
  const event = await Event.findById(eventId).populate('participants.user', 'email username');
  
  if (!event) return;
  
  for (const participant of event.participants) {
    const user = participant.user as any;
    await emailQueue.add({
      to: user.email,
      subject: `Update: ${event.title}`,
      text: updateMessage,
      html: `<div><h2>Event Update</h2><p>${updateMessage}</p></div>`
    });
  }
};

export const sendWaitlistPromotionNotification = async (eventId: string, userId: string) => {
  const event = await Event.findById(eventId);
  const user = await User.findById(userId);
  
  if (!event || !user) return;
  
  await emailQueue.add({
    to: user.email,
    subject: `Spot Available: ${event.title}`,
    text: `Good news! A spot has opened up for ${event.title}`,
    html: `<div><h2>Good News!</h2><p>You've been registered for ${event.title}</p></div>`
  });
};