import { randomUUID } from 'crypto' import { prisma } from '@/lib/prisma' import { sendStyledNotificationEmail, emailDelay } from '@/lib/email' import type { NotificationEmailContext } from '@/lib/email' export type NotificationItem = { email: string name: string type: string // ADVANCEMENT_NOTIFICATION, REJECTION_NOTIFICATION, etc. context: NotificationEmailContext projectId?: string userId?: string roundId?: string } export type BatchResult = { sent: number failed: number batchId: string errors: Array<{ email: string; error: string }> } /** * Send notifications in batches with throttling and per-email logging. * Each email is logged to NotificationLog with SENT or FAILED status. */ export async function sendBatchNotifications( items: NotificationItem[], options?: { batchSize?: number; batchDelayMs?: number } ): Promise { const batchId = randomUUID() const batchSize = options?.batchSize ?? 10 const batchDelayMs = options?.batchDelayMs ?? 500 let sent = 0 let failed = 0 const errors: Array<{ email: string; error: string }> = [] for (let i = 0; i < items.length; i += batchSize) { const chunk = items.slice(i, i + batchSize) for (const item of chunk) { try { await sendStyledNotificationEmail( item.email, item.name, item.type, item.context, ) sent++ // Log success (fire-and-forget) prisma.notificationLog.create({ data: { userId: item.userId || null, channel: 'EMAIL', type: item.type, status: 'SENT', email: item.email, roundId: item.roundId || null, projectId: item.projectId || null, batchId, }, }).catch((err) => console.error('[notification-sender] Log write failed:', err)) } catch (err) { const errorMsg = err instanceof Error ? err.message : String(err) failed++ errors.push({ email: item.email, error: errorMsg }) console.error(`[notification-sender] Failed for ${item.email}:`, err) // Log failure (fire-and-forget) prisma.notificationLog.create({ data: { userId: item.userId || null, channel: 'EMAIL', type: item.type, status: 'FAILED', email: item.email, roundId: item.roundId || null, projectId: item.projectId || null, batchId, errorMsg, }, }).catch((logErr) => console.error('[notification-sender] Log write failed:', logErr)) } await emailDelay() } // Delay between chunks to avoid overwhelming SMTP if (i + batchSize < items.length) { await new Promise((resolve) => setTimeout(resolve, batchDelayMs)) } } return { sent, failed, batchId, errors } }