(options: EmailOptions)
| 64 | } |
| 65 | |
| 66 | export async function sendEmail(options: EmailOptions): Promise<SendEmailResult> { |
| 67 | try { |
| 68 | const allowed = await applyBanList(options) |
| 69 | if (!allowed) { |
| 70 | logger.info('Email not sent (recipient on access-control ban list):', { |
| 71 | to: options.to, |
| 72 | subject: options.subject, |
| 73 | emailType: options.emailType, |
| 74 | }) |
| 75 | return SKIPPED_BANNED_RESULT |
| 76 | } |
| 77 | |
| 78 | if (await shouldSkipForUnsubscribe(allowed)) { |
| 79 | logger.info('Email not sent (user unsubscribed):', { |
| 80 | to: allowed.to, |
| 81 | subject: allowed.subject, |
| 82 | emailType: allowed.emailType, |
| 83 | }) |
| 84 | return SKIPPED_UNSUBSCRIBED_RESULT |
| 85 | } |
| 86 | |
| 87 | const data = processEmailData(allowed) |
| 88 | |
| 89 | if (activeProviders.length === 0) { |
| 90 | logger.info('Email not sent (no email service configured):', { |
| 91 | to: data.to, |
| 92 | subject: data.subject, |
| 93 | from: data.senderEmail, |
| 94 | }) |
| 95 | return MOCK_EMAIL_RESULT |
| 96 | } |
| 97 | |
| 98 | return await dispatchWithFallback(data) |
| 99 | } catch (error) { |
| 100 | logger.error('Error sending email:', error) |
| 101 | return { success: false, message: 'Failed to send email' } |
| 102 | } |
| 103 | } |
| 104 | |
| 105 | async function dispatchWithFallback(data: ProcessedEmailData): Promise<SendEmailResult> { |
| 106 | let lastError: unknown |
no test coverage detected