Content for confirmation email not showing when sent through nodemailer? - node.js

In my Node.js/MERN app, I get error 250 2.0.0 OK 1590267554 o18sm275551eje.40 - gsmtp & something went wrong when I register my user for authentication and receive an email with EMPTY body. I am using the code from https://blog.bitsrc.io/email-confirmation-with-react-257e5d9de725
I can see user is added to mongodb database with confirmed set to false. Why am I not getting the complete email with confirmation?
Please find my attached code for my MERN application. I would really appreciate a reply! Thank you!
Register
Register route in users which takes you to login and on React side OnSubmit starts chain of sending and confirming email.
router.post("/register", type, function (req, res, next) {
// var tmp_path = req.file.path;
if(!req.file){
console.log("File missing");
}
/** The original name of the uploaded file
stored in the variable "originalname". **/
// var target_path = 'uploads/' + req.file.originalname;
// /** A better way to copy the uploaded file. **/
// var src = fs.createReadStream(tmp_path);
// var dest = fs.createWriteStream(target_path);
// src.pipe(dest);
// fs.unlink(tmp_path);
// src.on('end', function() { res.render('complete'); });
// src.on('error', function(err) { res.render('error'); });
// Form validation
const { errors, isValid } = validateRegisterInput(req.body);
const url = req.protocol + '://' + req.get('host')
// Check validation
if (!isValid) {
return res.status(400).json(errors);
}
//Checks email against registered emails in database
registeredemails.findOne({ email: req.body.email}).select("email").lean().then(result => {
if (!result) {
return res.status(400).json({email: "Email not provided"});
}
});
User.findOne({ email: req.body.email }).then(user =>
{
if (user) {return res.status(400).json({ email: "Email already exists" })
}
else if(!user){
const newUser = new User({
firstName: req.body.firstName,
lastName: req.body.lastName,
email: req.body.email,
password: req.body.password,
fileimg: url + '/public/' + req.file.filename
});
// // Hash password before saving in database
bcrypt.genSalt(10, (err, salt) => {
bcrypt.hash(newUser.password, salt, (err, hash) => {
if (err) throw err;
newUser.password = hash;
newUser
.save()
.then(newUser =>
sendEmail(newUser.email),
templates.confirm(newUser._id)
)
.then(() => res.json({ msg: msgs.confirm }))
.catch(err => console.log(err))
}
)}
)}
else if (user && !user.confirmed) {
sendEmail(user.email, templates.confirm(user._id))
.then(() => res.json({ msg: msgs.resend })).catch(err => console.log(err))
}
// The user has already confirmed this email address
else {
res.json({ msg: msgs.alreadyConfirmed })
}
}).catch(err => console.log(err))
sendemail as used in the MEDIUM articles
const nodemailer = require('nodemailer');
const { CLIENT_ORIGIN } = require('../../config')
// const mg = require('nodemailer-mailgun-transport');
// The credentials for the email account you want to send mail from.
const credentials = {
secure: true,
service: 'Gmail',
auth: {
user: process.env.MAIL_USER,
pass: process.env.MAIL_PASS
// These environment variables will be pulled from the .env file
// apiKey: 'b61286bf9e28b149fac32220f0c7349f-e5e67e3e-00a38515',
// domain: 'sandbox0b8a7f0ebcc74c0d8161304f24909bd2.mailgun.org'
}
}
// Getting Nodemailer all setup with the credentials for when the 'sendEmail()'
// function is called.
const transporter = nodemailer.createTransport(credentials)
// exporting an 'async' function here allows 'await' to be used
// as the return value of this function.
module.exports = async (to, content) => {
// The from and to addresses for the email that is about to be sent.
const contacts = {
from: process.env.MAIL_USER,
to // An array if you have multiple recipients.
// subject: 'React Confirm Email',
// html: `
// <a href='${CLIENT_ORIGIN}/confirm/${id}'>
// click to confirm email
// </a>
// `,
// text: `Copy and paste this link: ${CLIENT_ORIGIN}/confirm/${id}`
}
// Combining the content and contacts into a single object that can
// be passed to Nodemailer.
const email = Object.assign({}, content, contacts)
// This file is imported into the controller as 'sendEmail'. Because
// 'transporter.sendMail()' below returns a promise we can write code like this
// in the contoller when we are using the sendEmail() function.
//
// sendEmail()
// .then(() => doSomethingElse())
//
// If you are running into errors getting Nodemailer working, wrap the following
// line in a try/catch. Most likely is not loading the credentials properly in
// the .env file or failing to allow unsafe apps in your gmail settings.
await transporter.sendMail(email, function(error, info){
if(error)
{
return console.log(error);
}
else
{
return console.log(info.response);
}
})
}
templates.confirm as used in Medium article
onst { CLIENT_ORIGIN } = require('../../config')
// This file is exporting an Object with a single key/value pair.
// However, because this is not a part of the logic of the application
// it makes sense to abstract it to another file. Plus, it is now easily
// extensible if the application needs to send different email templates
// (eg. unsubscribe) in the future.
module.exports = {
confirm: id => ({
subject: 'React Confirm Email',
html: `
<a href='${CLIENT_ORIGIN}/confirm/${id}'>
click to confirm email
</a>
`,
text: `Copy and paste this link: ${CLIENT_ORIGIN}/confirm/${id}`
})
}

Related

Send email through nodemailer nodejs

First I am checking the email present in the database, if present then it will generate the hashcode and after that it will update the database and once the hashcode is generated then email will be send to the user. So I am stuck how to send email please any help.
const md5 = require('md5');
let transporter = require("../config/transporter");
export const newemail = async(req,res ) => {
try{
var re = /^(([^<>()[\]\\.,;:\s#\"]+(\.[^<>()[\]\\.,;:\s#\"]+)*)|(\".+\"))#((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
const user = await db.findOne('USER', { email: req.body.email });
res.status(200).json(user);
if (!re.test(req.body.email)) {
res.json('Email address is not valid!');
} else {
const email = req.body.email;
const name = email.substring(0, email.lastIndexOf("#"));
const domain = email.substring(email.lastIndexOf("#") +1);
const hashCode = md5(name + domain);
function sendingMail(referredBy){
const user = await db.findOneandUpdate('USER', { email: req.body.email, referralCode: hashCode, referredBy: referredBy });
const email = user.email;
console.log(email + "email");
//send verification mail
let mailOptions = {
from: 'xxyyzz#gmail.com', // sender address
to: email, // list of receivers
subject: settingsConfig.subjectLine, // Subject line
text: settingsConfig.message, // plaintext body
html: settingConfig.message // html body
};
console.log("MAILING!");
console.log(mailOptions)
// send mail with defined transport object
transporter.sendMail(mailOptions, function (error, info) {
if (error) {
return console.log(error);
}
console.log('Message sent: ' + info.response);
});
};
}
}
catch (err) {
console.log(err)
res.status(500).json({ message: "Something went wrong" });
}
}
Please anyone how to move further.
const md5 = require('md5');
let transporter = require("../config/transporter");
export const sendEmail = async (to, from, subject, body) => {
// use any mailer library to send the email
}
export const isValidEmail = async (email) => {
const regex = /^(([^<>()[\]\\.,;:\s#\"]+(\.[^<>()[\]\\.,;:\s#\"]+)*)|(\".+\"))#((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
return regex.test(email);
}
export const findUserByEmail = async (email) => {
// db instance
return await db.findOne('USER', { email });
}
export const generateHash = async (email) => {
const name = email.substring(0, email.lastIndexOf("#"));
const domain = email.substring(email.lastIndexOf("#") +1);
return md5(name + domain);
}
export const verifyAndEmail = async(req,res ) => {
try{
// var re = /^(([^<>()[\]\\.,;:\s#\"]+(\.[^<>()[\]\\.,;:\s#\"]+)*)|(\".+\"))#((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
// const user = await db.findOne('USER', { email: req.body.email });
// res.status(200).json(user);
// step-1 destructure email from request body
const {
email
} = req.body;
// step-2, validate the email id
const isEmailValid = await isValidEmail(email);
if(!isEmailValid) {
throw new Error('Invalid email');
// return appropiate response
}
// step-3 find a user by email
const user = await findUserByEmail(email);
// step-4 validate, if user does not exists
if (!user) {
throw new Error('User does not exist');
// return appropiate response
}
//step-5 generate the hash using user's email
const hash = await generateHash(email);
// step-6, call send email method with the parameters
}
catch (err) {
console.log(err)
res.status(500).json({ message: "Something went wrong" });
}
}
I would suggest, you should refactor your code to following.
Run it and let me know if you face any issues.
This approach will help you to debug the error.

How can i access signup function elements from verify function?

I'm new to website building. I am using node js, express, and express-handlebars. I have 3 hbs page file called signup, verify, and login. I am trying to check if signup page has any errors using exports.signup and then if it's alright then rendering verify page and authenticating it using otp. Now my problem is I need to enter signup page values from verify page in the database after user is verified. How can I get signup page values from exports.signup and use it in exports.verify function?
This works to check in signup page:
exports.signup = (req, res) => { console.log(req.body);
const { name, email, password, passwordConfirm } = req.body;
db.query("select email from test where email=?",[email],async (error, results) => {
if (error) {
console.log(error);
}
if (results.length > 0) {
return res.render("signup", {
message: "The email is already in use",
});
} else if (password !== passwordConfirm) {
return res.render("signup", {
message: "Passwords do not match",
});
}
let hashedPassword = await bcrypt.hash(password, 8);
console.log(hashedPassword);
var digits = "0123456789";
let OTP = "";
for (let i = 0; i < 6; i++) {
OTP += digits[Math.floor(Math.random() * 10)];
}
let transporter = nodemailer.createTransport({
service: "gmail",
auth: {
user: process.env.GMAIL,
pass: process.env.GMAIL_PASSWORD,
},
});
let mailOptions = {
from: "checkmate.sdp#gmail.com",
to: email,
subject: "Verification code for Checkmate profile.",
text: "Your OTP is : " + OTP,
};
transporter.sendMail(mailOptions, function (error, info) {
if (error) {
console.log(error);
} else {
console.log("Email sent: " + info.response);
res.render("verify");
}
});
}
);
};
This verifies the user and enters values in database: (I haven't added anything here yet)
exports.verify = (req, res) => {
console.log(req.body);
};
Just an overview for you
signup.js
'use-strict';
exports.signup = (params) => { console.log("Hit", params) }
controller.js
'use-strict';
var signup = require('./signup');
var http = require('http');
// you can now access the signup function,
signup.signup({username: 'test', password: 'test'})
Looks like you want this to be an HTTP endpoint reciever,
depending on what library youre using, example with Koa route
Backend--
signup.js
var route = require('koa-route');
exports.init = (app) => {
app.use(route.post('/signup', signup));
}
async function signup(ctx) {
var body = ctx.request.body;
//operate
}
Frontend --
$.ajax({
url: "/signup",
type: "post",
data: JSON.stringify({username: 'get from html', password: 'get from html'})
});

TypeError: newUser.find is not a function

I am very new to the MERN stack and I would like some help figuring out this error. I'm trying to check if an email is already in the database upon creating a new user. Can anyone tell me why I am getting this error?
The model and scheme
//schema
const Schema = mongoose.Schema;
const VerificationSchema = new Schema({
FullName: String,
email: String,
password: String,
date: Date,
isVerified: Boolean,
});
// Model
const User = mongoose.model("Users", VerificationSchema);
module.exports = User;
The Api
const express = require("express");
const router = express.Router();
const User = require("../Models/User");
router.get("/VerifyEmail", (req, res) => {
console.log("Body:", req.body);
const data = req.body;
const newUser = new User();
newUser.find({ email: data.email }, function (err, newUser) {
if (err) console.log(err);
if (newUser) {
console.log("ErrorMessage: This email already exists");
} else {
console.log("This email is valid");
}
});
res.json({
msg: "We received your data!!!",
});
});
module.exports = router;
The api caller using axios
const isEmailValid = (value) => {
const info = {
email: value,
};
axios({
url: "http://localhost:3001/api/VerifyEmail",
method: "get",
data: info,
})
.then(() => {
console.log("Data has been sent");
console.log(info);
})
.catch(() => {
console.log("Internal server error");
});
};
if you have body in your request, change the type of request to POST...
after that for use find don't need to create a instance of model, use find with Model
router.get("/VerifyEmail", (req, res) => {
console.log("Body:", req.body);
const data = req.body;
User.find({ email: data.email }, function (err, newUser) {
if (err) console.log(err);
if (newUser) {
console.log("ErrorMessage: This email already exists");
} else {
console.log("This email is valid");
}
});
res.json({
msg: "We received your data!!!",
});
});
I prefer to use async/await and don't use Uppercase world for routing check the article: like this
router.post("/verify-email", async (req, res) => {
try {
let { email } = req.body;
let newUser = await User.findOne({ email });
if (newUser) {
console.log("ErrorMessage: This email already exists");
} else {
console.log("This email is valid");
}
} catch (error) {
res.json({
msg: "somthing went wrong",
});
}
res.json({
msg: "We received your data!!!",
});
});
The proper way to query a Model is like so:
const User = mongoose.model('Users');
User.find({<query>}, function (err, newUser) {...
So you need to get the model into a variable (in this case User) and then run the find function directly against it, as opposed to running it against an object you instantiate from it. So this is incorrect:
const newUser = new User();
newUser.find(...
So assuming all your files and modules are linked up correctly, this should work:
const User = require("../Models/User");
User.find({<query>}, function (err, newUser) {...
The problem wasn't actually the mongoose function but I needed to parse the object being sent.
let { email } = JSON.parse(req.body);
Before parsing the object looked like {"email" : "something#gmail.com"}
and after parsing the object looked like {email: 'something#gmail.com'}
I also changed the request from 'get' to 'post' and instead of creating a new instance of the model I simply used User.find() instead of newUser.find()

How to show an array of errors from express to the user with Vue.js after making a post request

I'm trying to create a basic register/sign up web app with Express, Vue.js and MongoDB.
I have written some backend validation such as checking the all the fields are filled in and the passwords match etc... and then push a string of the error message into an array if the user fails the validation.
If a user tries to sign up on the front end and fails some part of the validation the user is not inserted into the database but to message is displayed as to why and that what I'm having trouble with.
router.post("/register", (req, res) => {
const name = req.body.name;
const email = req.body.email;
const password = req.body.password;
const password2 = req.body.password2;
let errors = [];
// check required fields
if (!name || !email || !password || !password2) {
errors.push({ msg: "please fill in all fields" });
}
// check for errors
if (password !== password2) {
errors.push({ msg: "Passwords do not match" });
}
// password validation
if (password.length < 6) {
errors.push({ msg: "Password to short" });
}
if (errors.length > 0) {
// if there are errors redirect
res.redirect("/");
console.log(errors);
} else {
// res.send("pass");
// check if the user already exists:
User.findOne({ email: email }).then(user => {
if (user) {
// User exists
// if you have found one user
// then user exists and send to home page
errors.push({ msg: "Email is already registered" });
res.redirect("/");
console.log(errors);
} else {
// create new user by using the keyword 'new'
// name is equal to const name = req.body.name; etc...
const newUser = new User({
name: name,
email: email,
password: password
});
console.log(newUser + " hi new user");
// Hash Password before insert into db
bcrypt.genSalt(10, (err, salt) =>
bcrypt.hash(newUser.password, salt, (err, hash) => {
if (err) throw err;
// set PS to hashed PS
newUser.password = hash;
// save user
// insert into db then redirect to login
newUser
.save()
.then(user => {
res.redirect("/login");
})
.catch(err => {
console.log(err);
});
})
);
}
});
}
});
Vue:
name: "RegisterForm",
// data is a function that
// returns an object
data: () => ({
errorMessage: "",
user: {
name: "",
email: "",
password: "",
password2: ""
}
}),
// watch allows you to run a function
// any time data changes
watch: {
user: {
handler() {
this.errorMessage;
},
// deep means anytime data changes this watch will run
deep: true
}
},
methods: {
register() {
// clear error message on register
this.errorMessage = "";
// use keyword 'this' to refer to the current object 'methods'
if (this.validUser()) {
// send data to server
axios({
method: "post",
url: "http://localhost:4000/register",
data: {
name: this.user.name,
email: this.user.email,
password: this.user.password,
password2: this.user.password2
}
})
.then(response => {
console.error(response)
})
.catch(error => {
console.error(error)
});
}
},
validUser() {
// if (this.user.password !== this.user.password2) {
// this.errorMessage = "Passwords must match";
// return false;
// } else {
return true;
// }
// }
}
}
};
There is some frontend validation that works (by doing this.errorMessage = "Passwords must match or some error";) and shows a message but I need to show the errors from "let errors = []"
At the moment I'm currently getting a 'ReferenceError: error is not defined' error in the console when typing in invalid data such as two none matching passwords.
Use express-flash-message for save those on redirect. This plugins also expose a local variable for render those message. See documentation.

TypeError: Cannot read property 'send' of undefined in nodejs

I am developing a register form using Angular6, mongodb and nodejs. There I have written a post method to save users in mongodb if the user does not exist in the database. When the users are added to the database, an email should be sent to user and user should redirect to another view. That view is also in the earlier html and it shows only when the result is success.If the email name is already in the db it should show an error message. I have used the default error message in password. strategy-options.ts for the error message for existing users.But when I try to add a new user it does not navigate to the next view and the terminal shows the following error message.
TypeError: Cannot read property 'send' of undefined
"....node_modules\mongodb\lib\utils.js:132"
Here is my save method.
router.post('/signup', function(req, next) {
console.log("Came into register function.");
var newUser = new userInfo({
firstName : req.body.firstName,
lastName : req.body.lastName,
rank : req.body.lastName,
mobile : req.body.lastName,
email : req.body.email,
userName : req.body.userName,
password : req.body.password,
status : req.body.status
});
newUser.save(function (err, user,res) {
console.log("Came to the save method");
if (err){
console.log(user.email);
res.send(err);
return res;
}
else{
var transporter = nodemailer.createTransport({
service: 'Gmail',
auth: {
user: 't36#gmail.com',
pass: '12345'
}
});
var mailOptions = {
from: 'reg#demo.com',
to: newUser.email,
subject: 'Send mails',
text: 'That was easy!'
};
console.log("This is the user email"+" "+newUser.email);
transporter.sendMail(mailOptions, function(error, info){
if (error) {
console.log("Error while sending email"+" "+error);
} else {
console.log('Email sent: ' + info.response);
}
});
console.log("success");
return res.send("{success}");
}
});
});
Here is my register method in register.component.ts file.
register(): void {
this.errors = this.messages = [];
this.submitted = true;
this.service.register(this.strategy, this.user).subscribe((result: NbAuthResult) => {
this.submitted = false;
if (result.isSuccess()) {
this.messages = result.getMessages();
this.isShowConfirm = true;
this.isShowForm = false;
}
else {
this.errors = result.getErrors();
}
const redirect = result.getRedirect();
if (redirect) {
setTimeout(() => {
return this.router.navigateByUrl(redirect);
}, this.redirectDelay);
}
this.cd.detectChanges();
});
}
I have tried so many methods in internet to solve this. But still did not.
First of all node js router consist of 3 params req, res, next you miss out the res param, in your case the next behave as the res params.
Secondly Model.save only returns error and saved data there no res parameter in it. So the finally code will look like this
router.post('/signup', function(req, res, next) {
console.log("Came into register function.");
var newUser = new userInfo({
firstName : req.body.firstName,
lastName : req.body.lastName,
rank : req.body.lastName,
mobile : req.body.lastName,
email : req.body.email,
userName : req.body.userName,
password : req.body.password,
status : req.body.status
});
newUser.save(function (err, user) {
console.log("Came to the save method");
if (err){
console.log(user.email);
res.send(err);
return res;
}
else{
var transporter = nodemailer.createTransport({
service: 'Gmail',
auth: {
user: 't36#gmail.com',
pass: '12345'
}
});
var mailOptions = {
from: 'reg#demo.com',
to: newUser.email,
subject: 'Send mails',
text: 'That was easy!'
};
console.log("This is the user email"+" "+newUser.email);
transporter.sendMail(mailOptions, function(error, info){
if (error) {
console.log("Error while sending email"+" "+error);
} else {
console.log('Email sent: ' + info.response);
}
});
console.log("success");
return res.send("{success}");
}
});
});
To solve this same error message TypeError: Cannot read property 'send' of undefined in my rest api application, I found that I missed the valid syntax res.status(200).send(data) or res.send(data). Although I found the data in my console.
module.exports.getUsersController = async (req, res) => {
try {
// Password is not allowed to pass to client section
const users = await User.find({}, "-password");
const resData = {
users,
success: {
title: 'All Users',
message: 'All the users info are loaded successfully.'
}
}
console.log(resData)
// This is not correct
// return res.status(200).res.send(resData);
// It should be
return res.status(200).send(resData);
} catch (err) {
console.log(err)
return res.status(500).send(err);
}
};
When you use res.status(), you have to use .send() following this, not use res again.
I think it will be helpful for developers who did the same mistake as well. Happy developers!

Resources