im really stuck here. i already try a couple hours to solve this. on development, nodemailer works for sending email, like verification or reset password. but when i deploy or on production mode using vercel, nodemailer not work. can somebody help me? please? i really stuck here. thank you. here's the code
const nodemailer = require("nodemailer");
const fs = require("fs");
const mustache = require("mustache");
const path = require("path");
const gmail = require("../config/gmail");
module.exports = {
sendMail: (data) => {
// eslint-disable-next-line no-new
new Promise((resolve, reject) => {
const transporter = nodemailer.createTransport({
service: "gmail",
host: "smtp.gmail.com",
port: 465,
auth: {
type: "OAuth2",
user: process.env.MAIL_USERNAME,
pass: process.env.APP_PASSWORD,
clientId: gmail.clientId,
clientSecret: gmail.clientSecret,
refreshToken: gmail.refreshToken,
accessToken: gmail.accessToken,
},
secure: "true",
});
const filePath = path.join(
__dirname,
`../../src/templates/email/${data.template}`
);
const fileTemplate = fs.readFileSync(filePath, "utf8");
const mailOptions = {
from: '"Event Organizing" <arkawebdev1#gmail.com>',
to: data.to,
subject: data.subject,
html: mustache.render(fileTemplate, { ...data }),
};
transporter.sendMail(mailOptions, (error, result) => {
if (error) {
reject(error);
} else {
resolve(result);
}
});
});
},
};
Related
I have a middleware that validates my user object as users are created and also when they make edits. It worked not too long ago but I went to make a change this morning and realized it no longer works and I can't figure out why.
I've done some troubleshooting... the middleware function is being called (tested with a
console.log) but the req.body is coming through as undefined from the middleware function.
Joi Schema
const Joi = require('joi');
module.exports.userSchema = Joi.object({
user: Joi.object({
username: Joi.string().min(2).max(40).required(),
password: Joi.string().min(8).max(15).required(),
email: Joi.string().email({ minDomainSegments: 2, tlds: { allow: ['com', 'net'] } }).required(),
location: Joi.string().alphanum(),
image: Joi.string().alphanum(),
website: Joi.string().uri({
scheme: [
/https?/
]
}),
stripeId: Joi.string().alphanum(),
})
});
Middleware function
const { userSchema } = require('./schemas.js');
const ExpressError = require('./utils/ExpressError');
module.exports.validateUser = (req, res, next) => {
const { error } = userSchema.validate(req.body);
console.log("validation schema running"); <---- logs `validation schema running`
console.log(req.body) <--- `undefined`
if (error) {
const msg = error.details.map(el => el.message).join(',')
throw new ExpressError(msg, 400)
} else {
next();
}
}
Router
const { validateUser } = require('../middleware');
router.route('/:id')
.get( isLoggedIn, isUser, catchAsync(users.showUser))
.put( validateUser, isLoggedIn, upload.array('image'), catchAsync(users.updateUser))
And finally, the Controller function
module.exports.updateUser = async (req, res) => {
const { id } = req.params;
console.log("req body ", req.body);
const user = await User.findByIdAndUpdate(id, { ...req.body.user });
const imgs = req.files.map(f => ({ url: f.path, filename: f.filename }));
user.image.push(...imgs);
await user.save();
res.redirect(`/users/${user._id}`);
};
req.body from controller function comes through as below while req.body from the middleware comes through as undefined.
req body [Object: null prototype] {
user: [Object: null prototype] {
username: "Dan's Sands",
location: '1 Main St, Austin, TX 78703',
email: 'test#test.com',
website: 'https://test.com'
}
}
Why isn't the request body making it to my middleware function?
EDIT - I'm not sure if this would have any impact but adding the body parsing logic I have on my server:
App.js
app.use(bodyParser.raw({type: "application/json"}));
app.use(express.json({ limit: '1mb' }));
app.use(express.urlencoded({ extended: true }));
So i have the main nodejs server file (myserver.js)
const express = require("express");
const app = express();
const nodemailer = require("nodemailer");
const port = 80;
const vectorExpress = require("./node_modules/#smidyo/vectorexpress-nodejs/index");
const fs = require("fs");
var cors = require("cors");
app.use(cors());
app.use(express.json())
var randomnum = require('./randomnum.js');
var number = randomnum.number;
app.post('/mail', (req, res)=>{
console.log(req.body)
let transporter = nodemailer.createTransport({
service: 'Gmail',
auth: {
user: '',
pass: ''
}
});
const mailOptions = {
from: req.body.email,
to: 'naizeylines.info#gmail.com',
subject: `Order from ${req.body.name}`,
text:
`${req.body.name}
${req.body.street}
${req.body.postcode} ${req.body.town}
${req.body.country}
Quantity: ${req.body.quantity}
Additional information:
${req.body.message}
Shipping address:
${req.body.name2}
${req.body.street2}
${req.body.postcode2} ${req.body.town2}
${req.body.country2}
${req.body.phone2}
Email: ${req.body.email}
Phone number: ${req.body.phone}
File number: ${number}
`,
attachments: [{ // utf-8 string as an attachment
path: `${number}.svg`,
},
{
path: `${number}.dxf`,
},
]
}
transporter.sendMail(mailOptions, (error, info)=>{
if(error){
console.log(error);
res.send('error');
}else{
console.log('Email sent:' + info.response);
res.send('success');
}
})
})
var bodyParser = require("body-parser");
and a seperate script file (randomnum.js)
function randomnumber() {
return Math.floor(100000 + Math.random() * 900000);
}
var number = randomnumber();
exports.number = number;
console.log(number);
i would like to have it so that everytime nodemailer sends an email the main script would run the randomnum.js so that i would get a new random number generated. been trying for a few days now but i think im in over my head with my limited knowledge.
Based on code you provided, I see one obvious issue. You are defining randomnum out of the POST request. Also If i were you I'd generate random number inside of the myserver.js file.
Try this bit of code:
app.post('/mail', (req, res)=>{
var number = Math.floor(100000 + Math.random() * 900000)
let transporter = nodemailer.createTransport({
service: 'Gmail',
auth: {
user: '',
pass: ''
}
}); REST OF YOUR POST REQUEST LOGIC.....
Put Nodemailer code in separate file and export in main.js file and use with pass your data from main.js file
Hope this code will help to you
const nodemailer = require("nodemailer");
import * as dotenv from "dotenv";
dotenv.config({});
export class SendEmail {
public static send(data) {
const transport = nodemailer.createTransport({
name: process.env.SMTP_HOST,
host: process.env.SMTP_HOST,
port: process.env.SMTP_PORT,
auth: {
user: process.env.SMTP_USER_NAME,
pass: process.env.SMTP_PASSWORD,
},
pool: true, // use pooled connection
rateLimit: true, // enable to make sure we are limiting
maxConnections: 1, // set limit to 1 connection only
maxMessages: 3, // send 3 emails per second
});
var mailOptions = {
from: process.env.FROM,
html: data.html,
replyTo: process.env.REPLY_TO,
to: data.to,
subject: data.subject,
text: data.text,
};
transport.sendMail(mailOptions, (error, info) => {
if (error) {
return console.log(error);
}
console.log("Message sent: %s", info.messageId);
return;
});
}
}
I'm making a plugin that sends mail in trapi cms using nodemailer and google's oauth2, however my tokens automatically expire after 1 hour. I am trying several ways but my code still not working. Here is my code:
const nodemailer = require("nodemailer");
const { google } = require("googleapis");
const OAuth2 = google.auth.OAuth2;
const oauth2Client = new OAuth2(
`"${process.env.CLIENT_ID }"`,
`"${process.env.CLIENT_SECRET}"`,
"https://developers.google.com/oauthplayground",
);
// oauth2Client.setCredentials({
// refresh_token: `"${process.env.REFRESH_TOKEN }"`,
// });
// const accessToken = oauth2Client.getAccessToken()
module.exports = ({ env }) => ({
// ...
email: {
provider: env(`${process.env.REACT_APP_EMAIL_PROVIDER}`),
providerOptions: {
host: env("EMAIL_SMTP_HOST", "smtp.gmail.com"),
port: env("EMAIL_SMTP_PORT", 587),
auth: {
type: "OAuth2",
user: "hotrodhv58#gmail.com",
clientId: `"${process.env.CLIENT_ID }"`,
clientSecret: `"${process.env.CLIENT_SECRET}"`,
refreshToken: `"${process.env.REFRESH_TOKEN}"`,
accessToken: `"${process.env.ACCESS_TOKEN}"`,
},
},
settings: {
defaultFrom: env(`${process.env.REACT_APP_EMAIL_ADDRESS_FROM}`),
defaultReplyTo: env(`${process.env.REACT_APP_EMAIL_ADDRESS_REPLY}`),
},
},
// ...
});
The error I received:
Error sending email to xxxxssss#gmail.com {"code":"EAUTH","command":"AUTH XOAUTH2"}
If I leave Async Await in the module, I will receive an error:
{"Errno":-4078, "code": "Esocket", "Syncall": "Connect", "Address":"127.0.0.1", "Port": 587, "Command": "Conn"}}
What do I need to do to make them work. Hope to get help from everyone!
const nodeMailer = require("nodemailer");
const { google } = require('googleapis');
const oAuth2Client = new google.auth.OAuth2(
process.env.CLIENT_ID,
process.env.CLEINT_SECRET,
process.env.REDIRECT_URI
);
oAuth2Client.setCredentials({ refresh_token: process.env.REFRESH_TOKEN });
const sendEmail = async (options) => {
const accessToken = await oAuth2Client.getAccessToken();
const transporter = nodeMailer.createTransport({
host: process.env.SMTP_HOST,
port: process.env.SMTP_PORT,
secure: true,
service: process.env.SMTP_SERVICE,
auth: {
type: 'OAuth2',
user: process.env.SMTP_MAIL,
clientId: process.env.CLIENT_ID,
clientSecret: process.env.CLEINT_SECRET,
refreshToken: process.env.REFRESH_TOKEN,
accessToken: accessToken,
},
});
const mailOptions = {
from: process.env.SMPT_MAIL,
to: options.email,
subject: options.subject,
text: options.message,
};
await transporter.sendMail(mailOptions,function(err,info){
if(err){
console.log(err)
} else {
console.log('success')
}
});
};
module.exports = sendEmail;
when it says no refresh token , but each time when new req is made oAuth2Client creates new token and that is being passed into setting the credentials for refresh token, but ultimately the transporter is unable to process the mailing part.
In https://developers.google.com/oauthplayground : i've set API as "https://mail.google.com" and "Use your own OAuth credentials"
also the temp gmail account which i created for mailing purpose i activated Less secure app access to "On" mode.
can anyone help me to pass this error?
reg
I'm having an issue and I can't seem to narrow down what exactly I'm doing wrong. When I run my code, this is the error I get:
[Error: ENOENT: no such file or directory, open 'C:\Users\Alex\Desktop\emailtest\main.handlebars'] { errno: -4058, code: 'ENOENT', syscall: 'open', path: 'C:\\Users\\Alex\\Desktop\\emailtest\\main.handlebars' }
Here is my code:
const nodemailer = require("nodemailer");
const hbs = require("nodemailer-express-handlebars");
const express = require("express");
const app = express();
const port = 3000;
const transporter = nodemailer.createTransport({
service: "gmail",
auth: {
user: "useremail",
pass: "userpassword",
},
// tls: {
// rejectUnauthorized: false,
// },
});
transporter.use(
"compile",
hbs({
viewEngine: "express-handlebars",
viewPath: "views",
})
);
const mailOptions = {
from: "myemailhere",
to: "receiverpassword",
subject: "Automated Email",
template: "index",
};
transporter.sendMail(mailOptions, function (error, info) {
if (error) {
console.log(error);
} else {
console.log("Email sent: " + info.response);
}
});
app.listen(port, () => {
console.log(
`Listening at http://localhost:${port}`
);
});
Folder structure:
server.js
views
-index.handlebars
Without using Handlebars, I can send emails just fine, but being that I need a way to fill in HTML dynamically, I think Handlebars would be the best option if I can just get it working. Any insight would be great, thanks in advance!
const path = require('path');
const handlebarOptions = {
viewEngine: {
extName: ".handlebars",
partialsDir: path.resolve(__dirname, "views"),
defaultLayout: false,
},
viewPath: path.resolve(__dirname, "views"),
extName: ".handlebars",
};
trasnporter.use('compile', hbs(handlebarOptions));