fix(mail): harden mailer and fix missing notification recipients
This commit is contained in:
@@ -86,6 +86,7 @@ export async function sendContactFormAction(formData: FormData) {
|
||||
);
|
||||
|
||||
if (!isTestSubmission) {
|
||||
logger.info('Sending internal notification', { recipients: config.mail.recipients });
|
||||
const notificationResult = await sendEmail({
|
||||
replyTo: email,
|
||||
subject: notificationSubject,
|
||||
@@ -97,14 +98,18 @@ export async function sendContactFormAction(formData: FormData) {
|
||||
messageId: notificationResult.messageId,
|
||||
});
|
||||
} else {
|
||||
logger.error('Notification email FAILED', {
|
||||
logger.error('Notification email DELIVERY FAILED', {
|
||||
error: notificationResult.error,
|
||||
subject: notificationSubject,
|
||||
email,
|
||||
recipients: config.mail.recipients,
|
||||
});
|
||||
services.errors.captureException(
|
||||
new Error(`Notification email failed: ${notificationResult.error}`),
|
||||
{ action: 'sendContactFormAction_notification', email },
|
||||
{
|
||||
action: 'sendContactFormAction_notification',
|
||||
email,
|
||||
recipients: config.mail.recipients
|
||||
},
|
||||
);
|
||||
}
|
||||
} else {
|
||||
@@ -121,6 +126,7 @@ export async function sendContactFormAction(formData: FormData) {
|
||||
);
|
||||
|
||||
if (!isTestSubmission) {
|
||||
logger.info('Sending customer confirmation', { to: email });
|
||||
const confirmationResult = await sendEmail({
|
||||
to: email,
|
||||
subject: confirmationSubject,
|
||||
@@ -132,7 +138,7 @@ export async function sendContactFormAction(formData: FormData) {
|
||||
messageId: confirmationResult.messageId,
|
||||
});
|
||||
} else {
|
||||
logger.error('Confirmation email FAILED', {
|
||||
logger.error('Confirmation email DELIVERY FAILED', {
|
||||
error: confirmationResult.error,
|
||||
subject: confirmationSubject,
|
||||
to: email,
|
||||
|
||||
@@ -42,7 +42,7 @@ const envExtension = {
|
||||
MAIL_USERNAME: z.string().optional(),
|
||||
MAIL_PASSWORD: z.string().optional(),
|
||||
MAIL_FROM: z.string().optional(),
|
||||
MAIL_RECIPIENTS: z.string().optional(),
|
||||
MAIL_RECIPIENTS: z.string().trim().optional(),
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -32,16 +32,27 @@ interface SendEmailOptions {
|
||||
}
|
||||
|
||||
export async function sendEmail({ to, replyTo, subject, html }: SendEmailOptions) {
|
||||
const recipients = to || config.mail.recipients;
|
||||
const logger = getServerAppServices().logger.child({ component: 'mailer' });
|
||||
|
||||
// Resolve recipients: priority to 'to' override, fallback to global MAIL_RECIPIENTS
|
||||
const resolvedTo = to || config.mail.recipients;
|
||||
|
||||
// Normalize recipients (handle arrays or comma-strings)
|
||||
const recipients = Array.isArray(resolvedTo)
|
||||
? resolvedTo.join(', ')
|
||||
: (resolvedTo?.toString() || '');
|
||||
|
||||
if (!recipients) {
|
||||
logger.error('No email recipients configured (MAIL_RECIPIENTS is empty and no "to" provided)', { subject });
|
||||
if (!recipients || recipients.trim() === '') {
|
||||
logger.error('Email delivery ABORTED: No recipients configured', {
|
||||
subject,
|
||||
providedTo: to,
|
||||
configRecipients: config.mail.recipients
|
||||
});
|
||||
return { success: false as const, error: 'No recipients configured' };
|
||||
}
|
||||
|
||||
if (!config.mail.from) {
|
||||
logger.error('MAIL_FROM is not configured — cannot send email', { subject, recipients });
|
||||
logger.error('Email delivery ABORTED: MAIL_FROM is missing', { subject, recipients });
|
||||
return { success: false as const, error: 'MAIL_FROM is not configured' };
|
||||
}
|
||||
|
||||
@@ -53,14 +64,36 @@ export async function sendEmail({ to, replyTo, subject, html }: SendEmailOptions
|
||||
html,
|
||||
};
|
||||
|
||||
|
||||
try {
|
||||
const info = await getTransporter().sendMail(mailOptions);
|
||||
logger.info('Email sent successfully', { messageId: info.messageId, subject, recipients });
|
||||
const transporter = getTransporter();
|
||||
logger.info('Attempting to send email via SMTP', {
|
||||
host: config.mail.host,
|
||||
subject,
|
||||
recipients,
|
||||
hasReplyTo: !!replyTo
|
||||
});
|
||||
|
||||
const info = await transporter.sendMail(mailOptions);
|
||||
|
||||
logger.info('Email sent successfully', {
|
||||
messageId: info.messageId,
|
||||
subject,
|
||||
recipients,
|
||||
response: info.response
|
||||
});
|
||||
|
||||
return { success: true, messageId: info.messageId };
|
||||
} catch (error) {
|
||||
const errorMsg = error instanceof Error ? error.message : String(error);
|
||||
logger.error('Error sending email', { error: errorMsg, subject, recipients });
|
||||
logger.error('SMTP Transport failed', {
|
||||
error: errorMsg,
|
||||
subject,
|
||||
recipients,
|
||||
config: {
|
||||
host: config.mail.host,
|
||||
user: config.mail.user ? '***' : 'not set'
|
||||
}
|
||||
});
|
||||
return { success: false, error: errorMsg };
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user