最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Nodemailer Custom SMTP not working on Firebase Functions - Stack Overflow

programmeradmin0浏览0评论

I have an error. I am using nodemailer to send emails from my Firebase application.

This what my code looks like:

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

admin.initializeApp();

//THESE SETTINGS WORK ON LOCAL AND LIVE. BUT I DONT WANT TO USE THEM
// const transporter = nodemailer.createTransport({
//     service: 'gmail',
//     auth: {
//         user: 'GMAIL HERE',
//         pass:  'GMAIL PW HERE'
//     },
// })

//THESE SETTINGS WORK ON LOCAL, BUT NOT ON LIVE.
const transporter = nodemailer.createTransport({
    host: "smtp.mypanyname",
    port: 25,
    secureConnection: false, // TLS requires secureConnection to be false
    logger: true,
    debug: true,
    secure: false,
    requireTLS: true,
    auth: {
        user: "USERNAME HERE",
        pass: "PASSWORD HERE"
    },
    tls: { rejectUnauthorized: false }
})

exports.sendConfirmationEmail = functions.https.onCall((data, context) => {

    var email_adress        = data.data.email;

    var email = {
        from: 'E-Mail Adress Goes Here',
        to: email_adress,
        subject: 'BlaBlaBla',
        text: 'BlaBlaBla',
        html: 'BlaBlaBla'
    };
    // Function to send e-mail to the user
    transporter.sendMail(email, function(err, info) {
        if (err) {
            console.log(err);
            return { success: false };
        } else {
            return { success: true }
        }
    });

})

Now. If I use the GMail settings. All works fine. It sends E-Mails. However, my pany has an own SMTP Server. The SMTP works for the Firebase Authentication E-Mails. It is successfully sending those emails.

The SMTP server also works when I paste the above configuration in my local environment. However, when I run this in the Firebase Cloud function it gives me the following error:

10:24:43.479 AM
sendConfirmationEmail
[2020-09-25 08:24:43] DEBUG [Cq6p67HnXLA] Closing connection to the server using "destroy"
10:24:43.479 AM
sendConfirmationEmail
[2020-09-25 08:24:43] ERROR Send Error: Connection timeout
10:24:44.673 AM
sendConfirmationEmail
{ Error: Connection timeout
10:24:44.673 AM
sendConfirmationEmail
    at SMTPConnection._formatError (/workspace/node_modules/nodemailer/lib/smtp-connection/index.js:784:19) 
10:24:44.674 AM
sendConfirmationEmail
    at SMTPConnection._onError (/workspace/node_modules/nodemailer/lib/smtp-connection/index.js:770:20) 
10:24:44.674 AM
sendConfirmationEmail
at Timeout._connectionTimeout.setTimeout (/workspace/node_modules/nodemailer/lib/smtp-connection/index.js:235:22)

I've tried to play around with the different nodemailer options, but so far not a lot of success. It also makes it hard that on local it works, but when I deploy it doesn't.

Any ideas?

I have an error. I am using nodemailer to send emails from my Firebase application.

This what my code looks like:

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

admin.initializeApp();

//THESE SETTINGS WORK ON LOCAL AND LIVE. BUT I DONT WANT TO USE THEM
// const transporter = nodemailer.createTransport({
//     service: 'gmail',
//     auth: {
//         user: 'GMAIL HERE',
//         pass:  'GMAIL PW HERE'
//     },
// })

//THESE SETTINGS WORK ON LOCAL, BUT NOT ON LIVE.
const transporter = nodemailer.createTransport({
    host: "smtp.mypanyname.",
    port: 25,
    secureConnection: false, // TLS requires secureConnection to be false
    logger: true,
    debug: true,
    secure: false,
    requireTLS: true,
    auth: {
        user: "USERNAME HERE",
        pass: "PASSWORD HERE"
    },
    tls: { rejectUnauthorized: false }
})

exports.sendConfirmationEmail = functions.https.onCall((data, context) => {

    var email_adress        = data.data.email;

    var email = {
        from: 'E-Mail Adress Goes Here',
        to: email_adress,
        subject: 'BlaBlaBla',
        text: 'BlaBlaBla',
        html: 'BlaBlaBla'
    };
    // Function to send e-mail to the user
    transporter.sendMail(email, function(err, info) {
        if (err) {
            console.log(err);
            return { success: false };
        } else {
            return { success: true }
        }
    });

})

Now. If I use the GMail settings. All works fine. It sends E-Mails. However, my pany has an own SMTP Server. The SMTP works for the Firebase Authentication E-Mails. It is successfully sending those emails.

The SMTP server also works when I paste the above configuration in my local environment. However, when I run this in the Firebase Cloud function it gives me the following error:

10:24:43.479 AM
sendConfirmationEmail
[2020-09-25 08:24:43] DEBUG [Cq6p67HnXLA] Closing connection to the server using "destroy"
10:24:43.479 AM
sendConfirmationEmail
[2020-09-25 08:24:43] ERROR Send Error: Connection timeout
10:24:44.673 AM
sendConfirmationEmail
{ Error: Connection timeout
10:24:44.673 AM
sendConfirmationEmail
    at SMTPConnection._formatError (/workspace/node_modules/nodemailer/lib/smtp-connection/index.js:784:19) 
10:24:44.674 AM
sendConfirmationEmail
    at SMTPConnection._onError (/workspace/node_modules/nodemailer/lib/smtp-connection/index.js:770:20) 
10:24:44.674 AM
sendConfirmationEmail
at Timeout._connectionTimeout.setTimeout (/workspace/node_modules/nodemailer/lib/smtp-connection/index.js:235:22)

I've tried to play around with the different nodemailer options, but so far not a lot of success. It also makes it hard that on local it works, but when I deploy it doesn't.

Any ideas?

Share Improve this question asked Sep 25, 2020 at 8:45 RutgerBrnsRutgerBrns 852 silver badges10 bronze badges
Add a ment  | 

3 Answers 3

Reset to default 4

I've faced exactly the same problem and it took some digging, but the solution is quite simple.

Solution


You cannot use use outbound connections on port 25 in your Cloud Function.


So I've changed port to 465 and used secure connection and it does work.

I have (randomly) found a mention of this at Tips & Tricks documentation page.

PS: The same limitation applies to Compute Engine (see docs).

You should use Promises in order to manage the lifecycle of your Cloud Function, which executes an asynchronous operation which returns a promise (i.e. the sendMail() method). See this doc for more details.

So, by using, as follows, the promise returned by the sendMail() method, instead of the callback, it should work.

exports.sendConfirmationEmail = functions.https.onCall((data, context) => {

    var email_adress = data.data.email;

    var email = {
        from: 'E-Mail Adress Goes Here',
        to: email_adress,
        subject: 'BlaBlaBla',
        text: 'BlaBlaBla',
        html: 'BlaBlaBla'
    };

    return transporter.sendMail(email).then(() => {  // Note the return here
        return { success: false };
    }).catch(error => {
        console.log(error);
        // !! Here, return an instance of functions.https.HttpsError.
        // See the doc: https://firebase.google./docs/functions/callable#handle_errors
    });

});

Note: If you are NOT using Node.js 10 or 12 but Node.js 8 (which I guess is NOT the case, since deployment of Node.js 8 functions is no longer allowed since February 15, 2020.), read below:

If you are using Node.js version 8 in your Cloud Function, be aware that you need to be on the "Blaze" pricing plan. As a matter of fact, the free "Spark" plan "allows outbound network requests only to Google-owned services". See https://firebase.google./pricing/ (hover your mouse on the question mark situated after the "Cloud Functions" title)

Since your SMTP server is not a Google-owned service, you need to switch to the "Blaze" plan.

  1. Blaze is required
  2. Port 25 won't work
  3. Node.js 12 does work
  4. tls: { rejectUnauthorized: false } is essential to make it working

Working (TypeScript) code below, the use case is a contact form.

import * as functions from 'firebase-functions';
import * as nodemailer from 'nodemailer';
import * as Mail from 'nodemailer/lib/mailer';
import { DocumentSnapshot } from 'firebase-functions/lib/providers/firestore';
import { EventContext } from 'firebase-functions';

async function onCreateSendEmail(
  snap: DocumentSnapshot,
  _context: EventContext
) {
  try {
    const contactFormData = snap.data();
    console.log('Submitted contact form: ', contactFormData);
    console.log('context: ', _context);

    // The non-null assertion signs (!) might be not required, if your IDE/TypeScript settings aren't strict.
    const mailTransport: Mail = nodemailer.createTransport({
      host: 'mail.yourdomain.', // Or however your SMTP provides defines it.
      port: 587, // Most probably that's your port number.
      auth: {
        user: '[email protected]', // It could happen that your SMTP provides in user authentication requires full name of mail account. For example 'yourmail' would be not correct, '[email protected]' would.
        pass: 'YOUR_MAIL_PASSWORD',
      },
      tls: {
        rejectUnauthorized: false, //! ESSENTIAL! Fixes ERROR "Hostname/IP doesn't match certificate's altnames".
      },
    });

    const mailOptions = {
      from: `${contactFormData!.formControlName} <${
        contactFormData!.formControlEmail
      }>`,
      to: '[email protected]',
      subject: `Contact Form`,
      html: `
        <p>Message from a contact form has been send.</p>
        <h3>Message content:</h3>
        <ul>
          <li>Name: ${contactFormData!.formControlName}</li>
          <li>E-mail: ${contactFormData!.formControlEmail}</li>
          ...
        </ul>
      `,
    };

    await mailTransport.sendMail(mailOptions);
  } catch (err) {
    console.error(err);
  }
}

exports.contactFormFunction = functions.firestore
  .document('mails/{formControlEmail}'
  )
  .onCreate(onCreateSendEmail);
发布评论

评论列表(0)

  1. 暂无评论