You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Nodemailer适配Firebase云函数异常:无报错但无法发送邮件

问题排查与修复方案

1. 云函数核心问题:未正确处理异步操作与返回值

你的sendVerificationCode是HTTPS Callable类型函数,这类函数要求返回Promise来确保Firebase等待异步操作完成。原代码使用回调式的transporter.sendMail,函数会直接返回undefined,导致云函数提前终止,邮件发送流程被中断。同时,你错误使用了res.send——Callable函数没有res对象,需通过返回值或抛出错误来传递结果。

修复后的云函数代码:

const functions = require("firebase-functions");
const admin = require("firebase-admin");
const nodemailer = require("nodemailer");

admin.initializeApp();

// 使用Firebase环境变量存储敏感信息(推荐)
const SENDER_EMAIL = functions.config().gmail.email;
const SENDER_PASSWORD = functions.config().gmail.password;

// 简化Gmail配置:指定service后无需手动设置host等参数
const transporter = nodemailer.createTransport({
  service: 'gmail',
  auth: {
    user: SENDER_EMAIL,
    pass: SENDER_PASSWORD
  }
});

exports.sendVerificationCode = functions.https.onCall(async (data, context) => {
  // 从前端接收目标邮箱(原代码硬编码to地址,需改成动态接收)
  const targetEmail = data.email;
  if (!targetEmail) {
    throw new functions.https.HttpsError('invalid-argument', '目标邮箱不能为空');
  }

  const mailOptions = {
    from: SENDER_EMAIL, // 必须与auth中的user一致
    to: targetEmail,
    subject: 'Email from Firebase',
    text: 'TEST TEST'
  };

  try {
    const info = await transporter.sendMail(mailOptions);
    console.log('Email sent:', info.response);
    return { message: '邮件发送成功' };
  } catch (error) {
    console.error('发送失败:', error);
    throw new functions.https.HttpsError('internal', '邮件发送失败', error.message);
  }
});

2. Gmail SMTP权限配置

Gmail默认禁止普通密码通过第三方应用登录,你需要:

  • 开启两步验证(2FA),然后生成应用专用密码(App Password),用这个密码代替原密码。
  • 如果未开启2FA,需在Google账号设置中开启“不太安全的应用访问”(但此方式已逐步被Google禁用,优先推荐App Password)。

3. Swift调用代码修复

原Swift代码未传递目标邮箱参数,需修改为传递email参数:

func sendVerificationCode(to email: String, uid: String) {
    let functions = Functions.functions()
    
    functions.httpsCallable("sendVerificationCode").call(["email": email]) { result, error in
        if let error = error as NSError? {
            if error.domain == FunctionsErrorDomain {
                let code = FunctionsErrorCode(rawValue: error.code)
                let message = error.localizedDescription
                let details = error.userInfo[FunctionsErrorDetailsKey]
                print("邮件发送失败: \(code ?? .unknown) - \(message), 详情: \(details ?? "")")
            } else {
                print("未知错误: \(error.localizedDescription)")
            }
        } else {
            print("验证码发送成功")
        }
    }
}

4. 部署与测试注意事项

  • 设置Firebase环境变量:
    firebase functions:config:set gmail.email="你的邮箱地址" gmail.password="应用专用密码"
    
  • 重新部署云函数:
    firebase deploy --only functions
    
  • 查看云函数日志:通过Firebase控制台的“函数”→“日志”查看详细错误信息,这是排查发送失败的关键。

内容的提问来源于stack exchange,提问作者Nexteon

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.19 11:28:20