diff --git a/src/server/routers/message.ts b/src/server/routers/message.ts index 92ed51f..d036fdc 100644 --- a/src/server/routers/message.ts +++ b/src/server/routers/message.ts @@ -3,6 +3,8 @@ import { TRPCError } from '@trpc/server' import { router, protectedProcedure, adminProcedure } from '../trpc' import { logAudit } from '@/server/utils/audit' import { sendStyledNotificationEmail, getEmailPreviewHtml } from '@/lib/email' +import { sendBatchNotifications } from '../services/notification-sender' +import type { NotificationItem } from '../services/notification-sender' export const messageRouter = router({ /** @@ -68,7 +70,7 @@ export const messageRouter = router({ }, }) - // If not scheduled, deliver immediately for EMAIL channel + // If not scheduled, deliver immediately for EMAIL channel (batched to avoid overloading SMTP) if (!isScheduled && input.deliveryChannels.includes('EMAIL')) { const users = await ctx.prisma.user.findMany({ where: { id: { in: recipientUserIds } }, @@ -77,23 +79,25 @@ export const messageRouter = router({ const baseUrl = process.env.NEXTAUTH_URL || 'https://portal.monaco-opc.com' - for (const user of users) { - try { - await sendStyledNotificationEmail( - user.email, - user.name || '', - 'MESSAGE', - { - name: user.name || undefined, - title: input.subject, - message: input.body, - linkUrl: `${baseUrl}/messages`, - } - ) - } catch (error) { - console.error(`[Message] Failed to send email to ${user.email}:`, error) - } - } + const items: NotificationItem[] = users.map((user) => ({ + email: user.email, + name: user.name || '', + type: 'MESSAGE', + userId: user.id, + context: { + name: user.name || undefined, + title: input.subject, + message: input.body, + linkUrl: `${baseUrl}/messages`, + }, + })) + + // Fire-and-forget: batch send in background so the mutation returns quickly + sendBatchNotifications(items).then((result) => { + console.log(`[Message] Batch ${result.batchId}: ${result.sent} sent, ${result.failed} failed`) + }).catch((err) => { + console.error('[Message] Batch send error:', err) + }) } try {