部署于Cyclic的NodeJS应用无法通过Brevo发送事务邮件求助
NodeJS部署Cyclic后Brevo事务邮件无法发送(本地正常)
问题描述
使用Brevo(原Sendinblue)发送验证、密码重置类事务邮件,NodeJS应用部署到Cyclic平台后,其他功能正常,但邮件完全无法发送。本地测试时邮件发送一切正常,部署后接口返回200状态码,Cyclic日志无报错,但收件方始终收不到邮件,怀疑问题出在Brevo配置或部署环境适配。
相关代码
sendEmail.js
const nodemailer = require("nodemailer"); const sendEmail = async (email, subject, text) => { try { const transporter = nodemailer.createTransport({ service: "SendinBlue", auth: { user: process.env.SIB_EMAIL, pass: process.env.SIB_PASS, }, }); const mailOptions = { from: process.env.SIB_EMAIL, to: email, subject: subject, html: text, }; transporter.sendMail(mailOptions, (error, info) => { if (error) { console.log("Error:", error); } else { console.log("Email sent:", info.response); } }); } catch (err) { console.log(err); } }; module.exports = sendEmail;
/forgot-password 接口
const forgotPassword = async (req, res) => { try { const { email } = req.body; const user = await User.findOne({ email }); if (!user) { return res.status(404).json({ success: true, message: "User not found" }); } const resetToken = uuid(); user.reset_token = resetToken; user.reset_token_expiry = Date.now() + 3600000; //1 hour await user.save(); const resetLink = `${process.env.FRONTEND_URI}/reset-password?resetToken=${resetToken}`; const emailContent = ` <p>Click on the link below to reset your password</p> <a href=${resetLink}>${resetLink}</a> <br> <h5>Team Sukoon</h5> `; sendEmail(email, "Sukoon: Reset your password", emailContent); res.status(200).json({ success: true, message: "Email sent successfully" }); } catch { res.status(500).json({ success: false, message: "server error" }); } };
排查与修复方案
1. 检查Brevo IP白名单限制
Brevo默认可能拦截陌生IP的API请求,Cyclic服务器IP不在白名单内会导致请求被静默拦截(无错误返回但邮件不发送):
- 登录Brevo后台,进入「设置」→「SMTP & API」→「API密钥」页面,查看是否开启IP白名单
- 若开启,需将Cyclic平台的出站IP添加至白名单(可在Cyclic控制台环境信息中获取IP段)
- 可临时关闭IP白名单做测试,确认是否为该问题
2. 修正Nodemailer的Brevo SMTP配置
当前使用的service: "SendinBlue"为旧兼容配置,Brevo更新后官方推荐直接指定SMTP参数,且SMTP认证密码需使用Brevo API密钥,而非账户登录密码:
修改sendEmail.js的transporter配置:
const transporter = nodemailer.createTransport({ host: 'smtp-relay.brevo.com', // Brevo官方SMTP地址 port: 587, // 非加密端口,465为SSL端口 secure: false, // 587端口设为false,465端口设为true auth: { user: process.env.SIB_EMAIL, pass: process.env.SIB_API_KEY, // 替换为Brevo生成的API密钥 }, });
3. 修复异步调用的错误捕获逻辑
原代码中transporter.sendMail使用回调模式,无法被外层async函数的try/catch捕获,导致错误无法在Cyclic日志中打印:
修改sendEmail.js为Promise风格,确保错误可被捕获:
const sendEmail = async (email, subject, text) => { try { const transporter = nodemailer.createTransport({ host: 'smtp-relay.brevo.com', port: 587, secure: false, auth: { user: process.env.SIB_EMAIL, pass: process.env.SIB_API_KEY, }, }); const mailOptions = { from: process.env.SIB_EMAIL, to: email, subject: subject, html: text, }; // 改用await Promise形式,让错误进入try/catch const info = await transporter.sendMail(mailOptions); console.log("Email sent:", info.response); return info; } catch (err) { console.error("Email send error:", err); throw err; // 抛出错误让上层接口处理 } };
同时在/forgot-password接口中添加await,确保发送失败时能进入catch块:
// ... 其他代码不变 await sendEmail(email, "Sukoon: Reset your password", emailContent); res.status(200).json({ success: true, message: "Email sent successfully" }); // ...
4. 验证Cyclic环境变量
确认Cyclic平台上的SIB_EMAIL、SIB_API_KEY环境变量是否正确设置,无拼写错误或值粘贴错误。
5. 查看Brevo邮件日志
登录Brevo后台,进入「交易邮件」→「日志」页面,查看是否有被拒绝、静默丢弃的请求,这里能看到Brevo端的具体处理结果(如认证失败、IP拦截等)。
内容的提问来源于stack exchange,提问作者SUBS2905
相关产品推荐
相关产品推荐

