Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 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 | import { Resend } from 'resend'; import { logger } from '@/lib/logging'; const resend = process.env.RESEND_API_KEY ? new Resend(process.env.RESEND_API_KEY) : null; const ALERT_EMAIL = process.env.ALERT_EMAIL || 'admin@example.com'; const ALERT_FROM = process.env.ALERT_FROM_EMAIL || 'alerts@elite-events.com'; // Rate limiting for alerts const alertCooldowns = new Map<string, number>(); const COOLDOWN_MS = 5 * 60 * 1000; // 5 minutes between same alerts interface AlertData { timestamp: string; level: string; category: string; message: string; error?: unknown; context?: Record<string, unknown>; } export async function sendErrorAlertEmail(data: AlertData): Promise<void> { if (!resend) { logger.warn('Email alerts disabled: RESEND_API_KEY not configured', { category: 'SYSTEM' }); return; } // Check cooldown to prevent alert spam const alertKey = `${data.category}:${data.message}`; const lastAlert = alertCooldowns.get(alertKey); if (lastAlert && Date.now() - lastAlert < COOLDOWN_MS) { return; // Skip, already sent recently } alertCooldowns.set(alertKey, Date.now()); try { await resend.emails.send({ from: ALERT_FROM, to: ALERT_EMAIL, subject: `[ALERT] ${data.category}: ${data.message.substring(0, 50)}`, html: ` <div style="font-family: sans-serif; max-width: 600px;"> <h2 style="color: #dc2626;">Error Alert</h2> <table style="width: 100%; border-collapse: collapse;"> <tr> <td style="padding: 8px; border: 1px solid #ddd; font-weight: bold;">Time</td> <td style="padding: 8px; border: 1px solid #ddd;">${data.timestamp}</td> </tr> <tr> <td style="padding: 8px; border: 1px solid #ddd; font-weight: bold;">Category</td> <td style="padding: 8px; border: 1px solid #ddd;">${data.category}</td> </tr> <tr> <td style="padding: 8px; border: 1px solid #ddd; font-weight: bold;">Level</td> <td style="padding: 8px; border: 1px solid #ddd;">${data.level}</td> </tr> <tr> <td style="padding: 8px; border: 1px solid #ddd; font-weight: bold;">Message</td> <td style="padding: 8px; border: 1px solid #ddd;">${data.message}</td> </tr> </table> ${data.context ? ` <h3>Context</h3> <pre style="background: #f5f5f5; padding: 12px; overflow-x: auto;"> ${JSON.stringify(data.context, null, 2)} </pre> ` : ''} ${data.error ? ` <h3>Error Details</h3> <pre style="background: #fef2f2; padding: 12px; overflow-x: auto; color: #dc2626;"> ${data.error instanceof Error ? data.error.stack : String(data.error)} </pre> ` : ''} <p style="margin-top: 20px; color: #666;"> <a href="${process.env.NEXT_PUBLIC_APP_URL}/admin/monitoring/errors"> View Error Dashboard </a> </p> </div> `}); } catch (error) { logger.error('Failed to send alert email', error instanceof Error ? error : new Error(String(error)), { category: 'SYSTEM' }); } } // Daily summary email export async function sendDailySummaryEmail(): Promise<void> { if (!resend) return; // This would be called by a cron job // Implementation depends on your cron setup } |