I am sending bulk emails from Firebase HTTP Request using Nodemailer. Some of the emails are always missed out and it returns with this error:
Client network socket disconnected before secure TLS connection was established
Here is my code for Client-Side:
export const sendEmails = async () => {
// Fetch All Users
let users = []
let data = await db.collection('users')
data.forEach(e => {
let data = e.data()
let email = data['email']
users.push({ email })
})
// Divide users into multiple batches of 25
let batches = [[]], num = 0, batchSize = 25
Promise.all(users.map(batch => {
if (batches[num].length < batchSize) {
batches[num].push(batch)
} else {
batches.push([batch])
num++
}
}))
// Send Email Request for each batch with some cooldown time.
Promise.all(batches.map((batch, index) => {
setTimeout(async () => {
await sendBroadcast(batch)
}, 2000 * index)
}))
}
export const sendBroadcast = async (users) => {
const url = base + "sendBroadcast?"
const body = JSON.stringify({ users })
return await fetch(url, { method: "POST", body })
}
Server-Side:
let transporter = nodemailer.createTransport({
service: 'gmail',
pool: true,
maxConnections: 20,
maxMessages: 500,
auth: {
user: 'SOME EMAIL',
pass: 'SOME PASSWORD',
},
})
exports.sendBroadcast = functions.runWith({ timeoutSeconds: 540, memory: '2GB' }).https.onRequest((req, res) => {
cors(req, res, () => {
const body = JSON.parse(req.body)
const users = body.users
console.log('info', 'Send email to ' + users.length + ' users')
users.forEach(function (to, index) {
var msg = {
from: 'SOME EMAIL',
to: to.email,
subject: "SUBJECT",
html: "<h2> SOME HTML </h2>",
}
setTimeout(() => {
return transporter.sendMail(msg, (erro, info) => {
if (erro) {
console.log('error', erro.message, 'failed to send to ' + email)
return res.status(500).send(erro.toString())
}
return res.send('sent')
})
}, 10000 * index)
})
})
})
I was getting the Error "Temporary System Problem. Try again later (10)" before, so I added the cooldown time and sent multiple requests in batches. But now I'm getting the TLS error. I have tried a lot of solutions online, but there is always some error due to which all emails don't get sent.
I have deployed an HTTPS Function on Firebase, and I always get this error when I'm trying to send emails in bulk. I am using nodemailer pool to send emails.
Error: Data command failed: 421 4.3.0 Temporary System Problem. Try again later (10) - gsmtp
Client-side code:
export const sendEmails = async () => {
// Fetch All Users
let users = []
let data = await db.collection('users')
data.forEach(e => {
let data = e.data()
let email = data['email']
users.push({ email })
})
// Divide users into multiple batches of 25
let batches = [[]], num = 0, batchSize = 25
Promise.all(users.map(batch => {
if (batches[num].length < batchSize) {
batches[num].push(batch)
} else {
batches.push([batch])
num++
}
}))
// Send Email Request for each batch with some cooldown time.
Promise.all(batches.map((batch, index) => {
setTimeout(async () => {
await sendBroadcast(batch)
}, 2000 * index)
}))
}
export const sendBroadcast = async (users) => {
const url = base + "sendBroadcast?"
const body = JSON.stringify({ users })
return await fetch(url, { method: "POST", body })
}
Server-side code:
let transporter = nodemailer.createTransport({
service: 'gmail',
pool: true,
maxConnections: 20,
maxMessages: 500,
auth: {
user: 'SOME EMAIL',
pass: 'SOME PASSWORD',
},
})
exports.sendBroadcast = functions.runWith({ timeoutSeconds: 540, memory: '2GB' }).https.onRequest((req, res) => {
cors(req, res, () => {
const body = JSON.parse(req.body)
const users = body.users
console.log('info', 'Send email to ' + users.length + ' users')
users.forEach(function (to, index) {
var msg = {
from: 'SOME EMAIL',
to: to.email,
subject: "SUBJECT",
html: "<h2> SOME HTML </h2>",
}
return transporter.sendMail(msg, (erro, info) => {
if (erro) {
console.log('error', erro.message, 'failed to send to ' + email)
return res.status(500).send(erro.toString())
}
return res.send('sent')
})
})
})
})
Is this because gmail limits number of emails per day? I read somewhere that it would work if I added cooldowns after each email, I tried that as well but I was getting "Client network socket disconnected before secure TLS connection was established" error. Here's the Stack Overflow thread for that issue: Firebase Function Error "Client network socket disconnected before secure TLS connection was established" when sending bulk emails via Nodemailer
I have a challenge with express-fileupload when a user doesn't upload a file that is meant to be optional.
Someone should please help me out.
This is my code:
const file = req.files.document;
const file2 = req.files.document2;
const uploader = req.body.fullname;
const filename = `CV_${uploader}_${file.name}`;
const filename2 = `Cover_${uploader}_${file2.name}`;
let savedFile = filename.replace(/\s+/g, "");
let savedFile2 = filename2.replace(/\s+/g, "");
const path = "uploads/" + savedFile;
const path2 = "uploads/" + savedFile2;
file.mv(path, (err) => {
if (err) {
console.log(err);
}
});
file2.mv(path2, (err) => {
if (err) {
console.log(err);
}
});
The second file is optional for the user to upload. When the user doesn't upload it, it shows an error.
Please, how can I make it optional from here.
It shows an error like this:
Type Error: Cannot read property 'name' of undefined
Thank you so much.
So, I was able to find my way around the whole thing.
I did it like this...
app.post("/form", (req, res) => {
const file = req.files.document;
const file2 = req.files.document2;
const uploader = req.body.fullname;
const filename = `CV_${uploader}_${file.name}`;
let savedFile = filename.replace(/\s+/g, "");
const path = "uploads/" + savedFile;
file.mv(path, (err) => {
if (err) {
console.log(err);
}
});
// function to save file2 to server if it exists and send the filename to be used outside the function
const filename2 = file2 ? `Cover_${uploader}_${file2.name}` : null;
let savedFile2 = filename2 ? filename2.replace(/\s+/g, "") : null;
const path2 = filename2 ? "uploads/" + savedFile2 : null;
if (file2 && file2.name) {
const filename2 = `Cover_${uploader}_${file2.name}`;
let savedFile2 = filename2.replace(/\s+/g, "");
const path2 = "uploads/" + savedFile2;
file2.mv(path2, (err) => {
if (err) {
console.log(err);
}
});
}
// Saving to the database...
const date = new Date();
const dateNow = moment(date).format("llll");
const job = new Jobs({
position: req.body.positions,
language: req.body.lang,
fullName: req.body.fullname,
gender: req.body.gender,
education: req.body.education,
email: req.body.email,
address: req.body.address,
phone: req.body.phone,
fileCV: savedFile,
fileCover: savedFile2,
date: dateNow,
});
job.save((err) => {
if (!err) {
res.render("success");
}
});
// Sending to mail server
const output = `
<p> You have a new applicant! </p>
<h2> Contact Details </h2>
<ul>
<li>position: ${req.body.positions}</li>
<li>language: ${req.body.lang} </li>
<li>fullName: ${req.body.fullname}</li>
<li>gender: ${req.body.gender}</li>
<li>email: ${req.body.email}</li>
<li>address: ${req.body.address}</li>
<li>phone: ${req.body.phone}</li>
<li>education: ${req.body.education}</li>
</ul>
`;
const transporter = nodemailer.createTransport({
service: "localhost",
port: 1025,
secure: false, // true for 465, false for other ports
auth: {
user: "project.1", // generated ethereal user
pass: "secret.1", // generated ethereal password
},
tls: {
rejectUnauthorized: false,
},
});
let senderName = req.body.fullname;
let senderEmail = req.body.email;
//send mail with unicode symbols
let mailOptions = {
from: `"${senderName}" <${senderEmail}>`, // sender address
to: "mikejuwon737#gmail.com, sjobopisa#gmail.com", // list of receivers
subject: "Job Application ✔", // Subject line
text: "Check out my details here...", // plain text body
html: output, // html body
attachments: [
{ filename: `${savedFile}`, path: `${path}` },
{ filename: `${savedFile2}`, path: `${path2}` },
], // list of attachments
};
// sending mail with defined transport object
transporter.sendMail(mailOptions, (err, info) => {
if (err) {
console.log(err);
} else {
console.log("Message sent: %s", info.messageId);
// console.log("Preview URL: %s", nodemailer.getTestMessageUrl(info));
}
});
});
Node: v8.6.0
Nodemailer: v4.6.4
This is my code:
const transport = nodemailer.createTransport({
host: process.env.MAIL_HOST,
port: process.env.MAIL_PORT,
auth: {
user: process.env.MAIL_USER,
pass: process.env.MAIL_PASS
}
});
const generateHTML = (filename, options = {}) => {
const html = pug.renderFile(`${__dirname}/../views/email/${filename}.pug`,
options);
const inlined = juice(html);
return inlined;
}
exports.send = async (options) => {
const html = generateHTML(options.filename, options);
const text = htmlToText.fromString(html);
const mailOptions = {
from: `Site <noreply#domain.com>`,
to: options.user.email,
subject: options.subject,
html,
text
};
const sendMail = P.promisify(transport.sendMail, transport);
return sendMail(mailOptions);
}
When i execute sendMail i get this fail:
TypeError: Cannot read property 'getSocket' of undefined↵ at sendMail (/Users/...../node_modules/nodemailer/lib/mailer/index.js:143:24
I check the mention line and is this one:
if (typeof this.getSocket === 'function') {
this.transporter.getSocket = this.getSocket;
this.getSocket = false;
}
In my case, I received this error when I was trying to promisify the transport. Omit the callback parameter and it will natively return a promise. No need to promisify.
Try this.
const transport = nodemailer.createTransport({
host: process.env.MAIL_HOST,
port: process.env.MAIL_PORT,
auth: {
user: process.env.MAIL_USER,
pass: process.env.MAIL_PASS
}
});
const generateHTML = (filename, options = {}) => {
const html = pug.renderFile(`${__dirname}/../views/email/${filename}.pug`,
options);
const inlined = juice(html);
return inlined;
}
exports.send = async (options) => {
const html = generateHTML(options.filename, options);
const text = htmlToText.fromString(html);
const mailOptions = {
from: `Site <noreply#domain.com>`,
to: options.user.email,
subject: options.subject,
html,
text
};
return transport.sendMail(mailOptions)
.then((stuff) => { console.log(stuff); })
.catch((err) => { console.log(err); }) ;
}
I have code that send email with nodemailer in nodejs but I want to attach file to an email but I can't find way to do that I search on net but I could't find something useful.Is there any way that I can attach files to with that or any resource that can help me to attach file with nodemailer?
var nodemailer = require('nodemailer');
var events = require('events');
var check =1;
var events = new events.EventEmitter();
var smtpTransport = nodemailer.createTransport("SMTP",{
service: "gmail",
auth: {
user: "example#gmail.com",
pass: "pass"
}
});
function inputmail(){
///////Email
const from = 'example<example#gmail.com>';
const to = 'example#yahoo.com';
const subject = 'example';
const text = 'example email';
const html = '<b>example email</b>';
var mailOption = {
from: from,
to: to,
subject: subject,
text: text,
html: html
}
return mailOption;
}
function send(){
smtpTransport.sendMail(inputmail(),function(err,success){
if(err){
events.emit('error', err);
}
if(success){
events.emit('success', success);
}
});
}
///////////////////////////////////
send();
events.on("error", function(err){
console.log("Mail not send");
if(check<10)
send();
check++;
});
events.on("success", function(success){
console.log("Mail send");
});
Include in the var mailOption the key attachments, as follow:
var mailOptions = {
...
attachments: [
{ // utf-8 string as an attachment
filename: 'text1.txt',
content: 'hello world!'
},
{ // binary buffer as an attachment
filename: 'text2.txt',
content: new Buffer('hello world!','utf-8')
},
{ // file on disk as an attachment
filename: 'text3.txt',
path: '/path/to/file.txt' // stream this file
},
{ // filename and content type is derived from path
path: '/path/to/file.txt'
},
{ // stream as an attachment
filename: 'text4.txt',
content: fs.createReadStream('file.txt')
},
{ // define custom content type for the attachment
filename: 'text.bin',
content: 'hello world!',
contentType: 'text/plain'
},
{ // use URL as an attachment
filename: 'license.txt',
path: 'https://raw.github.com/andris9/Nodemailer/master/LICENSE'
},
{ // encoded string as an attachment
filename: 'text1.txt',
content: 'aGVsbG8gd29ybGQh',
encoding: 'base64'
},
{ // data uri as an attachment
path: 'data:text/plain;base64,aGVsbG8gd29ybGQ='
}
]}
Choose the option that adjust to your needs.
Link:Nodemailer Repository GitHub
Good Luck!!
Your code is almost right, just need to add, "attachments" property for attaching the files in your mail,
YOUR mailOption:
var mailOption = {
from: from,
to: to,
subject: subject,
text: text,
html: html
}
Just add attachments like
var mailOption = {
from: from,
to: to,
subject: subject,
text: text,
html: html,
attachments: [{
filename: change with filename,
path: change with file path
}]
}
attachments also provide some other way to attach file for more information check nodemailer community's documentation HERE
If you are passing options object in mail composer constructor and attachment is on http server then it should look like:
const options = {
attachments = [
{ // use URL as an attachment
filename: 'xxx.jpg',
path: 'http:something.com/xxx.jpg'
}
]
}
var express = require('express');
var router = express(),
multer = require('multer'),
upload = multer(),
fs = require('fs'),
path = require('path');
nodemailer = require('nodemailer'),
directory = path.dirname("");
var parent = path.resolve(directory, '..');
// your path to store the files
var uploaddir = parent + (path.sep) + 'emailprj' + (path.sep) + 'public' + (path.sep) + 'images' + (path.sep);
/* GET home page. */
router.get('/', function(req, res) {
res.render('index.ejs', {
title: 'Express'
});
});
router.post('/sendemail', upload.any(), function(req, res) {
var file = req.files;
console.log(file[0].originalname)
fs.writeFile(uploaddir + file[0].originalname, file[0].buffer, function(err) {
//console.log("filewrited")
//console.log(err)
})
var filepath = path.join(uploaddir, file[0].originalname);
console.log(filepath)
//return false;
nodemailer.mail({
from: "yourgmail.com",
to: req.body.emailId, // list of receivers
subject: req.body.subject + " ✔", // Subject line
html: "<b>" + req.body.description + "</b>", // html body
attachments: [{
filename: file[0].originalname,
streamSource: fs.createReadStream(filepath)
}]
});
res.send("Email has been sent successfully");
})
module.exports = router;
attachments: [
{
filename: "inovices_1.pdf", // the file name
path: "https://*************************/invoice/10_9_RMKUns.pdf",// link your file
contentType: "application/pdf", //type of file
},
{
filename: "inovices_2.pdf",
path: "https://**************************/invoice/10_9_RMKUns.pdf",
contentType: "application/pdf",
},
];
var nodemailer = require("nodemailer");
var all_transporter = nodemailer.createTransport({
host: process.env.MAIL_SERVICE,
port: 587,
auth: {
user: process.env.MAIL_USER,
pass: process.env.MAIL_PASS,
},
maxConnections: 3,
pool: true,
});
exports.send_email = function (email, subject, html, extra_cc = [], attachments = []) {
return new Promise(async (resolve, reject) => {
var mailOptions = {
from: process.env.MAIL_FROM_ADDRESS,
to: email,
subject: subject,
html: html,
cc: [],
};
mailOptions["cc"] = mailOptions["cc"].concat(extra_cc);
if (attachments.length > 0) mailOptions["attachments"] = attachments;
all_transporter.sendMail(mailOptions, function (error, info) {
// console.log(error);
// console.log(info);
if (error) {
resolve({ failed: true, err: error });
} else {
resolve({ failed: false, data: info.response });
}
});
});
};
The alternative solution is to host your images online using a CDN and link to the online image source in your HTML, eg. <img src="list_image_url_here">.
(I had problems with nodemailer's image embedding using nodemailer version 2.6.0, which is why I figured out this workaround.)
An added benefit of this solution is that you're sending no attachments to nodemailer, so the sending process is more streamlined.
var mailer = require('nodemailer');
mailer.SMTP = {
host: 'host.com',
port:587,
use_authentication: true,
user: 'you#example.com',
pass: 'xxxxxx'
};
Then read a file and send an email :
fs.readFile("./attachment.txt", function (err, data) {
mailer.send_mail({
sender: 'sender#sender.com',
to: 'dest#dest.com',
subject: 'Attachment!',
body: 'mail content...',
attachments: [{'filename': 'attachment.txt', 'content': data}]
}), function(err, success) {
if (err) {
// Handle error
}
}
});
Just look at here. Nodemailer > Message configuration > Attachments
The code snippet is below (pdfkit gets the stream):
// in async func
pdf.end();
const stream = pdf;
const attachments = [{ filename: 'fromFile.pdf', path: './output.pdf',
contentType: 'application/pdf' }, { filename: 'fromStream.pdf', content: stream, contentType: 'application/pdf' }];
await sendMail('"Sender" <sender#test.com>', 'reciver#test.com', 'Test Send Files', '<h1>Hello</h1>', attachments);
Stream uses content not streamSource This bothered me before, share with everyone :)
Reference = https://nodemailer.com/message/attachments/
var mailOption = {
from: from,
to: to,
subject: subject,
text: text,
html: html,
attachments: [
{
filename: filename,
path: filePath
},
]
}