在 firebase 函数中使用 gmail 时出现 Nodemailer 错误
Nodemailer error while using gmail in firebase functions
我正在使用 Firebase 云函数。我配置了以下设置。虽然这在我的本地机器上工作得很好,但在服务器上 运行 时却给我带来了问题。我已经在互联网上尝试了无数的解决方法,但没有成功。这有什么问题吗?
'use strict'
const functions = require('firebase-functions');
var admin = require('firebase-admin');
const express = require('express');
const nodemailer = require('nodemailer');
const app = express()
var emailRecepient;
var userName;
const smtpTransport = nodemailer.createTransport({
service: "gmail",
host: 'smtp.gmail.com',
port: 587, // tried enabling and disabling these, but no luck
secure: false, // similar as above
auth: {
user: '<emailid>',
pass: '<password>'
},
tls: {
rejectUnauthorized: false
}
});
var mailOptions = {
from: 'test <hello@example.com>',
to: emailRecepient,
subject: 'Welcome to test',
text: 'welcome ' + userName + ". did you see the new things?"
};
function sendmail() {
smtpTransport.sendMail(mailOptions, function (error, info) {
if (error) {
console.log(error);
}
else {
console.log('Email sent: ' + info.response);
}
});
};
exports.sendEmails = functions.database.ref('/users/{userID}/credentials').onCreate((snap, context) => {
const userID = context.params.userID;
const vals = snap.val()
userName = vals.name;
emailRecepient = vals.email;
smtpTransport.sendMail(mailOptions, function (error, info) {
if (error) {
console.log("Error sending email ---- ",error);
}
else {
console.log('Email sent: ' + info.response);
}
});
return true;
});
我在所有情况下得到的错误是:
Error sending email 2 ---- { Error: Invalid login: 534-5.7.14 <https://accounts.google.com/signin/continue?sarp=1&scc=1&plt=AKgnsbsi
534-5.7.14 qRQLfD9YlFZDsPj7b8QQACro9c41PjhSVo0NZ4i5ZHNlyycFi_FyRp8VdZ_dH5ffWWAABQ
534-5.7.14 8rH2VcXkyZBFu00-YHJUQNOqL-IqxEsZqbFCwCgk4-bo1ZeDaKTdkEPhwMeIM2geChH8av
534-5.7.14 0suN293poXFBAk3TzqKMMI34zCvrZlDio-E6JVmTrxyQ-Vn9Ji26LaojCvdm9Bq_4anc4U
534-5.7.14 SpQrTnR57GNvB0vRX1BihDqKuKiXBJ5bfozV1D1euQq18PZK2m> Please log in via
534-5.7.14 your web browser and then try again.
534-5.7.14 Learn more at
534 5.7.14 https://support.google.com/mail/answer/78754 t2sm3669477iob.7 - gsmtp
at SMTPConnection._formatError (/user_code/node_modules/nodemailer-smtp-transport/node_modules/smtp-connection/lib/smtp-connection.js:528:15)
at SMTPConnection._actionAUTHComplete (/user_code/node_modules/nodemailer-smtp-transport/node_modules/smtp-connection/lib/smtp-connection.js:1231:30)
at SMTPConnection.<anonymous> (/user_code/node_modules/nodemailer-smtp-transport/node_modules/smtp-connection/lib/smtp-connection.js:319:22)
at SMTPConnection._processResponse (/user_code/node_modules/nodemailer-smtp-transport/node_modules/smtp-connection/lib/smtp-connection.js:669:16)
at SMTPConnection._onData (/user_code/node_modules/nodemailer-smtp-transport/node_modules/smtp-connection/lib/smtp-connection.js:493:10)
at emitOne (events.js:96:13)
at TLSSocket.emit (events.js:188:7)
at readableAddChunk (_stream_readable.js:176:18)
at TLSSocket.Readable.push (_stream_readable.js:134:10)
at TLSWrap.onread (net.js:559:20)
code: 'EAUTH',
response: '534-5.7.14 <https://accounts.google.com/signin/continue?sarp=1&scc=1&plt=AKgnsbsi\n534-5.7.14 qRQLfD9YlFZDsPj7b8QQACro9c41PjhSVo0NZ4i5ZHNlyycFi_FyRp8VdZ_dH5ffWWAABQ\n534-5.7.14 8rH2VcXkyZBFu00-YHJUQNOqL-IqxEsZqbFCwCgk4-bo1ZeDaKTdkEPhwMeIM2geChH8av\n534-5.7.14 0suN293poXFBAk3TzqKMMI34zCvrZlDio-E6JVmTrxyQ-Vn9Ji26LaojCvdm9Bq_4anc4U\n534-5.7.14 SpQrTnR57GNvB0vRX1BihDqKuKiXBJ5bfozV1D1euQq18PZK2m> Please log in via\n534-5.7.14 your web browser and then try again.\n534-5.7.14 Learn more at\n534 5.7.14 https://support.google.com/mail/answer/78754 t2sm3669477iob.7 - gsmtp',
responseCode: 534,
command: 'AUTH PLAIN' }
我什至转过allow secure apps in the google settings
。但由于某种原因,这似乎不起作用。非常感谢任何帮助。
按照 Renaud 的建议,我尝试了 firebase-samples/email-confirmation
,但出现以下错误:
TypeError: snapshot.changed is not a function
at exports.sendEmailConfirmation.functions.database.ref.onWrite (/user_code/index.js:38:17)
at Object.<anonymous> (/user_code/node_modules/firebase-functions/lib/cloud-functions.js:112:27)
at next (native)
at /user_code/node_modules/firebase-functions/lib/cloud-functions.js:28:71
at __awaiter (/user_code/node_modules/firebase-functions/lib/cloud-functions.js:24:12)
at cloudFunction (/user_code/node_modules/firebase-functions/lib/cloud-functions.js:82:36)
at /var/tmp/worker/worker.js:758:24
at process._tickDomainCallback (internal/process/next_tick.js:135:7)
干杯
当您在后台触发的 Cloud Function 中执行异步操作时,您必须 return 一个 promise,这样 Cloud Function 会等待该 promise 解决以便终止。
这在官方 Firebase 视频系列中有很好的解释:https://firebase.google.com/docs/functions/video-series/。特别是观看标题为 "Learn JavaScript Promises" 的三个视频(第 2 部分和第 3 部分特别关注后台触发的 Cloud Functions,但在第 1 部分之前确实值得一看)。
所以你应该修改你的代码如下:
exports.sendEmails = functions.database.ref('/users/{userID}/credentials').onCreate((snap, context) => {
const userID = context.params.userID;
const vals = snap.val()
userName = vals.name;
emailRecepient = vals.email;
return smtpTransport.sendMail(mailOptions);
});
如果你想在控制台打印邮件发送的结果,你可以这样做:
return smtpTransport.sendMail(mailOptions)
.then((info) => console.log('Email sent: ' + info.response))
.catch((error) => console.log("Error sending email ---- ", error));
});
实际上有一个官方 Cloud Functions 示例可以做到这一点,请参阅 https://github.com/firebase/functions-samples/blob/master/email-confirmation/functions/index.js
我看到这是一个很长的讨论,让我分享一些代码片段,这些代码片段对我来说是为其他有类似问题的人解决的,这样会更容易理解。
1) function.ts(用 TypeScript 写的)
import * as admin from 'firebase-admin';
import * as functions from 'firebase-functions';
import * as nodemailer from 'nodemailer';
admin.initializeApp();
// I'm taking all these constants as secrets injected dynamically (important when you make `git push`), but you can simply make it as a plaintext.
declare const MAIL_ACCOUNT: string; // Declare mail account secret.
declare const MAIL_HOST: string; // Declare mail account secret.
declare const MAIL_PASSWORD: string; // Declare mail password secret.
declare const MAIL_PORT: number; // Declare mail password secret.
const mailTransport = nodemailer.createTransport({
host: MAIL_HOST,
port: MAIL_PORT, // This must be a number, important! Don't make this as a string!!
auth: {
user: MAIL_ACCOUNT,
pass: MAIL_PASSWORD
}
});
exports.sendMail = functions.https.onRequest(() => {
const mailOptions = {
from: 'ME <SENDER@gmail.com>',
to: 'RECEIVER@gmail.com',
subject: `Information Request from ME`,
html: '<h1>Test</h1>'
};
mailTransport
.sendMail(mailOptions)
.then(() => {
return console.log('Mail sent'); // This log will be shown in Firebase Firebase Cloud Functions logs.
})
.catch(error => {
return console.log('Error: ', error); // This error will be shown in Firebase Cloud Functions logs.
});
});
这样说,你应该会收到一封从SENDER@gmail.com
到RECEIVER@gmail.com
的邮件,当然可以根据自己的需要修改。
注意:我在 localhost
上正确发送邮件时遇到了同样的问题,但在部署时却没有。看起来我的问题是我没有在 createTransport
中使用 port
和 host
,而是我有:
const mailTransport = nodemailer.createTransport({
service: 'gmail',
auth: {
user: MAIL_ACCOUNT,
pass: MAIL_PASSWORD
}
});
除此之外,不要忘记启用 Less secure app access to ON. Also https://accounts.google.com/DisplayUnlockCaptcha 可能会有帮助。
检查上面列出的所有事情都在我的代码中完成后,我解决了登录 Google 帐户的问题(使用我在我的项目中使用的帐户)。我必须识别 activity 形成另一个来源(google 检测到 Firebase 正在尝试访问该帐户),仅此而已。
之后我尝试从 firebase 云发送另一封电子邮件,它工作正常
我正在使用 Firebase 云函数。我配置了以下设置。虽然这在我的本地机器上工作得很好,但在服务器上 运行 时却给我带来了问题。我已经在互联网上尝试了无数的解决方法,但没有成功。这有什么问题吗?
'use strict'
const functions = require('firebase-functions');
var admin = require('firebase-admin');
const express = require('express');
const nodemailer = require('nodemailer');
const app = express()
var emailRecepient;
var userName;
const smtpTransport = nodemailer.createTransport({
service: "gmail",
host: 'smtp.gmail.com',
port: 587, // tried enabling and disabling these, but no luck
secure: false, // similar as above
auth: {
user: '<emailid>',
pass: '<password>'
},
tls: {
rejectUnauthorized: false
}
});
var mailOptions = {
from: 'test <hello@example.com>',
to: emailRecepient,
subject: 'Welcome to test',
text: 'welcome ' + userName + ". did you see the new things?"
};
function sendmail() {
smtpTransport.sendMail(mailOptions, function (error, info) {
if (error) {
console.log(error);
}
else {
console.log('Email sent: ' + info.response);
}
});
};
exports.sendEmails = functions.database.ref('/users/{userID}/credentials').onCreate((snap, context) => {
const userID = context.params.userID;
const vals = snap.val()
userName = vals.name;
emailRecepient = vals.email;
smtpTransport.sendMail(mailOptions, function (error, info) {
if (error) {
console.log("Error sending email ---- ",error);
}
else {
console.log('Email sent: ' + info.response);
}
});
return true;
});
我在所有情况下得到的错误是:
Error sending email 2 ---- { Error: Invalid login: 534-5.7.14 <https://accounts.google.com/signin/continue?sarp=1&scc=1&plt=AKgnsbsi
534-5.7.14 qRQLfD9YlFZDsPj7b8QQACro9c41PjhSVo0NZ4i5ZHNlyycFi_FyRp8VdZ_dH5ffWWAABQ
534-5.7.14 8rH2VcXkyZBFu00-YHJUQNOqL-IqxEsZqbFCwCgk4-bo1ZeDaKTdkEPhwMeIM2geChH8av
534-5.7.14 0suN293poXFBAk3TzqKMMI34zCvrZlDio-E6JVmTrxyQ-Vn9Ji26LaojCvdm9Bq_4anc4U
534-5.7.14 SpQrTnR57GNvB0vRX1BihDqKuKiXBJ5bfozV1D1euQq18PZK2m> Please log in via
534-5.7.14 your web browser and then try again.
534-5.7.14 Learn more at
534 5.7.14 https://support.google.com/mail/answer/78754 t2sm3669477iob.7 - gsmtp
at SMTPConnection._formatError (/user_code/node_modules/nodemailer-smtp-transport/node_modules/smtp-connection/lib/smtp-connection.js:528:15)
at SMTPConnection._actionAUTHComplete (/user_code/node_modules/nodemailer-smtp-transport/node_modules/smtp-connection/lib/smtp-connection.js:1231:30)
at SMTPConnection.<anonymous> (/user_code/node_modules/nodemailer-smtp-transport/node_modules/smtp-connection/lib/smtp-connection.js:319:22)
at SMTPConnection._processResponse (/user_code/node_modules/nodemailer-smtp-transport/node_modules/smtp-connection/lib/smtp-connection.js:669:16)
at SMTPConnection._onData (/user_code/node_modules/nodemailer-smtp-transport/node_modules/smtp-connection/lib/smtp-connection.js:493:10)
at emitOne (events.js:96:13)
at TLSSocket.emit (events.js:188:7)
at readableAddChunk (_stream_readable.js:176:18)
at TLSSocket.Readable.push (_stream_readable.js:134:10)
at TLSWrap.onread (net.js:559:20)
code: 'EAUTH',
response: '534-5.7.14 <https://accounts.google.com/signin/continue?sarp=1&scc=1&plt=AKgnsbsi\n534-5.7.14 qRQLfD9YlFZDsPj7b8QQACro9c41PjhSVo0NZ4i5ZHNlyycFi_FyRp8VdZ_dH5ffWWAABQ\n534-5.7.14 8rH2VcXkyZBFu00-YHJUQNOqL-IqxEsZqbFCwCgk4-bo1ZeDaKTdkEPhwMeIM2geChH8av\n534-5.7.14 0suN293poXFBAk3TzqKMMI34zCvrZlDio-E6JVmTrxyQ-Vn9Ji26LaojCvdm9Bq_4anc4U\n534-5.7.14 SpQrTnR57GNvB0vRX1BihDqKuKiXBJ5bfozV1D1euQq18PZK2m> Please log in via\n534-5.7.14 your web browser and then try again.\n534-5.7.14 Learn more at\n534 5.7.14 https://support.google.com/mail/answer/78754 t2sm3669477iob.7 - gsmtp',
responseCode: 534,
command: 'AUTH PLAIN' }
我什至转过allow secure apps in the google settings
。但由于某种原因,这似乎不起作用。非常感谢任何帮助。
按照 Renaud 的建议,我尝试了 firebase-samples/email-confirmation
,但出现以下错误:
TypeError: snapshot.changed is not a function
at exports.sendEmailConfirmation.functions.database.ref.onWrite (/user_code/index.js:38:17)
at Object.<anonymous> (/user_code/node_modules/firebase-functions/lib/cloud-functions.js:112:27)
at next (native)
at /user_code/node_modules/firebase-functions/lib/cloud-functions.js:28:71
at __awaiter (/user_code/node_modules/firebase-functions/lib/cloud-functions.js:24:12)
at cloudFunction (/user_code/node_modules/firebase-functions/lib/cloud-functions.js:82:36)
at /var/tmp/worker/worker.js:758:24
at process._tickDomainCallback (internal/process/next_tick.js:135:7)
干杯
当您在后台触发的 Cloud Function 中执行异步操作时,您必须 return 一个 promise,这样 Cloud Function 会等待该 promise 解决以便终止。
这在官方 Firebase 视频系列中有很好的解释:https://firebase.google.com/docs/functions/video-series/。特别是观看标题为 "Learn JavaScript Promises" 的三个视频(第 2 部分和第 3 部分特别关注后台触发的 Cloud Functions,但在第 1 部分之前确实值得一看)。
所以你应该修改你的代码如下:
exports.sendEmails = functions.database.ref('/users/{userID}/credentials').onCreate((snap, context) => {
const userID = context.params.userID;
const vals = snap.val()
userName = vals.name;
emailRecepient = vals.email;
return smtpTransport.sendMail(mailOptions);
});
如果你想在控制台打印邮件发送的结果,你可以这样做:
return smtpTransport.sendMail(mailOptions)
.then((info) => console.log('Email sent: ' + info.response))
.catch((error) => console.log("Error sending email ---- ", error));
});
实际上有一个官方 Cloud Functions 示例可以做到这一点,请参阅 https://github.com/firebase/functions-samples/blob/master/email-confirmation/functions/index.js
我看到这是一个很长的讨论,让我分享一些代码片段,这些代码片段对我来说是为其他有类似问题的人解决的,这样会更容易理解。
1) function.ts(用 TypeScript 写的)
import * as admin from 'firebase-admin';
import * as functions from 'firebase-functions';
import * as nodemailer from 'nodemailer';
admin.initializeApp();
// I'm taking all these constants as secrets injected dynamically (important when you make `git push`), but you can simply make it as a plaintext.
declare const MAIL_ACCOUNT: string; // Declare mail account secret.
declare const MAIL_HOST: string; // Declare mail account secret.
declare const MAIL_PASSWORD: string; // Declare mail password secret.
declare const MAIL_PORT: number; // Declare mail password secret.
const mailTransport = nodemailer.createTransport({
host: MAIL_HOST,
port: MAIL_PORT, // This must be a number, important! Don't make this as a string!!
auth: {
user: MAIL_ACCOUNT,
pass: MAIL_PASSWORD
}
});
exports.sendMail = functions.https.onRequest(() => {
const mailOptions = {
from: 'ME <SENDER@gmail.com>',
to: 'RECEIVER@gmail.com',
subject: `Information Request from ME`,
html: '<h1>Test</h1>'
};
mailTransport
.sendMail(mailOptions)
.then(() => {
return console.log('Mail sent'); // This log will be shown in Firebase Firebase Cloud Functions logs.
})
.catch(error => {
return console.log('Error: ', error); // This error will be shown in Firebase Cloud Functions logs.
});
});
这样说,你应该会收到一封从SENDER@gmail.com
到RECEIVER@gmail.com
的邮件,当然可以根据自己的需要修改。
注意:我在 localhost
上正确发送邮件时遇到了同样的问题,但在部署时却没有。看起来我的问题是我没有在 createTransport
中使用 port
和 host
,而是我有:
const mailTransport = nodemailer.createTransport({
service: 'gmail',
auth: {
user: MAIL_ACCOUNT,
pass: MAIL_PASSWORD
}
});
除此之外,不要忘记启用 Less secure app access to ON. Also https://accounts.google.com/DisplayUnlockCaptcha 可能会有帮助。
检查上面列出的所有事情都在我的代码中完成后,我解决了登录 Google 帐户的问题(使用我在我的项目中使用的帐户)。我必须识别 activity 形成另一个来源(google 检测到 Firebase 正在尝试访问该帐户),仅此而已。 之后我尝试从 firebase 云发送另一封电子邮件,它工作正常