Node mailer not sending emails - node.js

I was trying to set up a email verification method using nodemailer but it seems to not work for some reason. Can anyone find a fix ?
i will first generate the token and then send it to the user for them to verify their email and continue login process. I seem to be stuck in this part. Node mailer not sending the email but user registration info does save in the database.
my register route -
const User = require('../models/User');
const router = require('express').Router();
const bcrypt = require('bcrypt');
const mongoose = require('mongoose');
const nodemailer = require('nodemailer');
const Token = require('../models/token');
router.post('/register', async (req,res) => {
try {
user = new User({
name: req.body.name,
email: req.body.email,
password: req.body.password
});
const salt = await bcrypt.genSalt(10);
user.password = await bcrypt.hash(user.password, salt);
// create and save user
await user.save(function(err) {
if (err) {
return res.status(500).send({msg: err.message});
}
});
// genereate token and save
let token = new Token({_userId: user._id, token: crypto.getRandomValues(16).toString('hex')});
token.save(() => {
if(err) {
return res.status(500).send({msg: err.message});
}
let transporter = nodemailer.createTransport({service: 'gmail', auth: {user: 'myEmail', pass: "Password"}});
let mailOptions = {from: "authmail14#gmail.com", to: user.email, subject: "Account Verification Link"}
transporter.sendMail(mailOptions, function(err) {
if(err) {
return res.status(500).send({msg: "Technical Issue"})
}
return res.status(200).send('Email Sent')
})
});
} catch {
console.log("NO result")
}
});
module.exports = router;
tokenSchema -
const mongoose = require("mongoose");
const tokenSchema = new mongoose.Schema({
_userId: {type: mongoose.Schema.Types.ObjectId, required: true, ref: "user"},
token: {type: String, required: true},
expiredAt: {type: Date, default: Date.now(), index: {expires: 86400000}}
});
const token = mongoose.model('token', tokenSchema);
module.exports = token;
userSchema -
const mongoose = require('mongoose');
const userSchema = new mongoose.Schema({
name: {
type: String, required: true, minlength: 5,maxlength:30
},
email: {
type: String, unique:true, maxlength:30
},
password: {
type: String, minlength: 8, required:true
},
isAdmin: {
type: Boolean, default: false
},
createdAt: {
type: Date, default: Date.now
},
verified: {
type: Boolean, default: false
}
});
app.js -
const express = require('express');
const mongoose = require('mongoose');
const dotenv = require('dotenv');
const path = require('path');
const authRoute = require('./routes/auth');
dotenv.config();
const app = express();
app.use(express.json());
app.use('/auth', authRoute);
mongoose.connect(process.env.MONGO).then(() => {
console.log('Database Connected')
});
const PORT = process.env.PORT || 5000;
if (process.env.NODE_ENV === "production") {
app.use(express.static("client/build"));
app.get("*", (req, res) => {
res.sendFile(path.resolve(__dirname, "client", "build", "index.html"));
});
}
app.listen(PORT, () => {
console.log(`Server Online at ${PORT}`);
});
module.exports = mongoose.model('Users', userSchema);

I think it doesn't work because transporter.sendMail() is asynchronous function you have to use async await to execute that function.
Try this
token.save(async () => {
if(err) {
return res.status(500).send({msg: err.message});
}
let transporter = nodemailer.createTransport({service: 'gmail', auth: {user: 'myEmail', pass: "Password"}});
let mailOptions = {from: "authmail14#gmail.com", to: user.email, subject: "Account Verification Link"}
await transporter.sendMail(mailOptions, function(err) {
if(err) {
return res.status(500).send({msg: "Technical Issue"})
}
return res.status(200).send('Email Sent')
})
});

Dharmik Patel's answer problematic
Either you use async/await to run synchronous code, or you use a callbak or promise for asynchronous code.
But not both at the same time.
Here is an example with callback :
mailer.transport.sendMail( mail_config, function( error ) {
if ( error ) {
console.info( 'Mail not sent : ', error );
return;
}
console.info( 'Mail sent ! ');
message.transport.close();
});
Just add console for printing error of mailler callback to see what is the problem.
In general, use promises or calbacks instead to keep the asynchronous advantage of node.js.
Synchronous operation must be reserved for processing that cannot be done asynchronously.

Related

I'm not sure how to delete a user using MongoDB

I'm trying to be able to delete a user if they choose to delete their account. I'm not sure how it's properly done. Here's the code for the delete function. I've looked at other StackOverflow solutions but I haven't been able to grasp the concept just yet. can anyone help?
const { validationResult } = require('express-validator')
const User = require('../modelSchema/User')
const mongo = require('mongodb')
const signUp = (req, res) => {
const errors = validationResult(req)
if(!errors.isEmpty()) return res.status(400).json({ errors: errors.array()})
//new instance of user
const user = new User({
name: req.body.name,
email: req.body.email,
password: req.body.password })
//save the user
user.save()
.then(data => {
res.json(data)
res.send('User added successfully!')
console.log(data)
})
.catch(err => {
res.status(400).json(err)
})
}
const deleteUser = (req, res) => {
let id = req.params.id;
User.get().createCollection('user', function ( col) {
col.deleteOne({_id: new mongo.ObjectId(id)});
});
res.json({ success: id })
res.send('User Deleted Successfully!')
.catch(err => {
res.status(400).json(err)
})
}
module.exports = { signUp, deleteUser }
here are the routes that I'm using
const express = require('express');
const { check } = require('express-validator')
const { signUp, deleteUser } = require('../controllers/users');
const router = express.Router();
router.post('/signup', [
check('name', 'Please Enter your Name').not().isEmpty(),
check('email', 'Please Enter your Email').isEmail(),
check('password', 'Please Enter your Password').isLength({ minLength: 6})
], signUp)
router.delete('/delete/:id', deleteUser)
module.exports = router;
and here's my schema
const mongoose = require('mongoose');
let userSchema = new mongoose.Schema({
name: { type: 'string', required: true},
email: { type: 'string', required: true},
password: { type: 'string', required: true},
date: { type: Date, default: Date.now}
})
module.exports = mongoose.model('user', userSchema)
Update your delete function as below
const deleteUser = async (req, res) => {
const { id } = req.params;
const user = await User.findByIdAndDelete(id);
if (!user) {
return res.status(400).json("User not found");
}
res.status(200).json("User deleted successfully");
};
findByIdAndDelete takes an ObjectId of a user to be deleted. If the user exist, it'll delete the user and return user document, else return null;

Mongoose validation error, "email is not defined"

I am new to mongoose and express. I try to create a simple login backend, however when send a post request with
{
"userEmail": "abc#xyz", "password": "pswrd"
}
I get "email is not defined" error whose type is "VALIDATION". My User Schema is as follows:
const mongoose = require("mongoose");
const bcrypt = require("bcrypt");
const UserSchema = new mongoose.Schema({
email: {
type: String,
required: [true, "Email is required"],
trim: true,
unique: true,
},
password: {
type: String,
trim: true,
required: [true, "Password is required"],
},
username: {
type: String,
required: [true, "Username is required"],
trim: true,
unique: true,
},
});
UserSchema.pre("save", async function (next) {
const user = await User.findOne({ email: this.email });
if (user) {
next(new Error(`${this.email} already taken`));
return;
}
const user1 = await User.findOne({ username: this.username });
if (user1) {
next(new Error(`${this.username} already taken`));
return;
}
const salt = await bcrypt.genSalt(8);
this.password = await bcrypt.hash(this.password, salt);
next();
});
// userSchema.statics is accessible by model
UserSchema.statics.findByCredentials = async (email, password) => {
const user = await User.findOne({ email });
if (!user) {
throw Error("User does not exist.");
}
const isMatch = await bcrypt.compare(password, user.password);
if (!isMatch) {
throw Error("Unable to login");
}
return user;
};
const User = mongoose.model("User", UserSchema);
module.exports = User;
I use findByCredentials to check if the User is in my mongoDB database or not. Finally, my login.js is as follows:
const express = require("express");
const mongoose = require("mongoose");
const User = require("../db/models/User");
const loginRouter = express.Router();
loginRouter.get("/api/login2", (req, res) => res.send("In Login"));
loginRouter.post("/api/login", async (req, res) => {
const { userEmail, password} = req.body;
if (!validateReqBody(userEmail, password)) {
return res
.status(401)
.send({ status: false, type: "INVALID", error: "invalid request body" });
}
try {
const newUser = new User({
email: userEmail,
password: password,
});
await newUser.findByCredentials(email, password);
} catch (error) {
const validationErr = getErrors(error);
console.log(validationErr);
return res
.status(401)
.send({ status: false, type: "VALIDATION", error: validationErr });
}
res.send({ status: true });
});
//user.find --> mongoose documentation
// Validates request body
const validateReqBody = (...req) => {
for (r of req) {
if (!r || r.trim().length == 0) {
return false;
}
}
return true;
};
// Checks errors returning from DB
const getErrors = (error) => {
if (error instanceof mongoose.Error.ValidationError) {
let validationErr = "";
for (field in error.errors) {
validationErr += `${field} `;
}
return validationErr.substring(0, validationErr.length - 1);
}
return error.message;
};
module.exports = { loginRouter };
Thank you.
You need to use body-parser middleware in backend
const bodyParser = require('body-parser');
const express = require('express');
const app = express();
//bodypraser middleware
app.use(bodyParser.json());
You can read more about bodyparser here
Happened to me once, it was really annoying. I don't know If it would help you, but try sending the post request with headers: { 'Content-Type': 'application/json' }, using fetch.
Definition of findByCredentials() is in User model. I was trying to reach that function by the object instance newUser that i created in login.js. However, i should have called the function as User.findByCredentials(email, password).

Mongoose model.create() responds empty or do not responde

I created a small project with node+express+mongodb
This is where i call the create user:
const express = require("express");
const User = require("../models/user");
const router = express.Router();
router.post("/register", async (req, res) => {
try {
const user = await User.create(req.body);
return res.send({ user });
} catch (e) {
return res.status(400).send({ error: "Registration failed" });
}
});
module.exports = app => app.use("/auth", router);
and here is the Schema for the user:
const mongoose = require("mongoose");
const UserSchema = new mongoose.Schema({
name: {
type: String,
require: true
},
email: {
type: String,
require: true,
unique: true,
lowercase: true
},
password: {
type: String,
require: true,
select: false
},
createdAt: {
type: Date,
default: Date.now
}
});
const User = mongoose.model("User", UserSchema);
module.exports = User;
But when o make the resquest, it nevers get a response, it waits forever. And when o take out the await from the request, it gets an empty response { "user": {}}
I'm kind lost looking the mongoose documentation (the ideia is to make a simple rest api, i'm used with python, but looking to learn node)
You have to create a new user from User Model as follows:
const express = require("express");
const User = require("../models/user");
const router = express.Router();
router.post("/register", async (req, res) => {
try {
var user = new User(request.body);
var result = await user.create();
return res.send({ result });
} catch (e) {
return res.status(400).send({ error: "Registration failed" });
}
});
module.exports = app => app.use("/auth", router);

Can't get a response from my server. Why?

I can't get a response from my server. I am using postman and running the following post request:
localhost:4000/users/register?email=test#gmail.com&f_name=testf&s_name=tests&password=test
It hangs for a very long time and then says:
Could not get any response
This is my code:
[user.route.js]
const express = require('express');
const userRoutes = express.Router();
const cors = require('cors');
const bcrypt = require('bcryptjs');
const jwt = require('jsonwebtoken');
//require User model in the routes module
let User = require('../models/user.model.js');
//Make router use cors to avoid cross origin errors
userRoutes.use(cors);
//secret
process.env.SECRET_KEY = 'secret';
//define register route
userRoutes.post('/register', (req, res) => {
const today = new Date();
const userData = {
email: req.body.email,
f_name: req.body.f_name,
s_name: req.body.s_name,
password: req.body.password,
created: today
}
//Find one user based on email, hash their password and then create a document in the collection for that user
User.findOne({
email: req.body.email
})
.then(user => {
if (!user) {
bcrypt.hash(req.body.password, 10, (err, hash) => {
user.password = hash;
User.create(userData)
.then(user => {
res.json({
status: user.email + ' registered'
});
})
.catch(err => {
res.send('error: ' + err);
});
});
}
});
});
userRoutes.post('/login', (req, res) => {
User.findOne({
email: req.body.email
})
.then(user => {
if (user) {
if (bcrypt.compareSync(req.body.password, user.password)) {
const payload = {
_id: user._id,
f_name: user.f_name,
s_name: user.s_name,
email: user.email
}
let token = jwt.sign(payload, process.env.SECRET_KEY, {
expiresIn: 1440
});
res.send(token);
} else {
res.json({
'Error': 'Password Incorrect'
});
}
} else {
res.json({
'Error': 'User does not exist'
});
}
})
.catch(err => {
res.send('Error: ' + err);
});
});
module.exports = userRoutes;
[user.model.js]
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
let User = new Schema({
email: {
type: String
},
f_name: {
type: String
},
s_name: {
type: String
},
password: {
type: String
},
created: {
type: String
}
}, {
collection: 'users'
});
[server.js]
const express = require('express');
const app = express();
const bodyParser = require('body-parser');
const PORT = 4000;
const cors = require('cors');
const mongoose = require('mongoose');
const config = require('./db.js');
mongoose.Promise = global.Promise;
mongoose.connect(config.DB, {
useNewUrlParser: true
}).then(
() => {
console.log('Database is connected')
},
err => {
console.log('Can not connect to the database' + err)
}
);
app.use(cors());
app.use(bodyParser.urlencoded({
extended: true
}));
app.use(bodyParser.json());
var Users = require('./routes/user.route');
//make /users use routes
app.use('/users', Users);
app.listen(PORT, function() {
console.log('Server is running on Port:', PORT);
});
[db.js]
module.exports = {
DB: 'mongodb://localhost:27017/pharaohcrud'
}
I'm using Node, MongoDB, Mongoose, Vue, Express.
I'm new to Node in general so it's hard for me to give details on what i've done. Please feel free to ask any questions that you need answered to help me with issue and ill answer as thoroughly as i can :)
EDITS:
Here is the updated db.js file
module.exports = {
DB: 'mongodb://localhost:27017/pharaoh'
}
Here is the updated post request that im sending to the server through postman:
localhost:4000/users/register
[raw json request]
{
"email": "test#gmail.com",
"f_name": "test",
"s_name": "test",
"password": "test"
}
You have to send json data with your post request not query strings.
In postman, select "Body" tab and choose "raw" and from the dropdown menu select "json" format. Send your user data as Json, this will solve the issue.
Image description here
I went deleted all database-related code and retyped it, and now it works. I guess the lesson here is to always pay attention while typing code to make sure you're writing it correctly.

Unable to reference from token schema token mongodb express passport

i need help with this problem been dealing it for days.
i am trying to make a verification email route by using passport to hash passwords while issuing a verification token to the user.
here is my code for index.js in controllers folder
const User = require("../models/user");
const Token = require("../models/token")
const crypto = require("crypto");
const nodemailer = require("nodemailer");
var smtpTransport = nodemailer.createTransport({
service: 'Gmail',
auth: {
user: process.env.GMAILUSER,
pass: process.env.GMAILPW
}
});
module.exports = {
async postRegister(req, res, next) {
var user = new User({
name: req.body.name,
email: req.body.email,
isVerified: false,
username: req.body.username
});
await User.register(user, req.body.password);
res.redirect('/');
var token = new Token({ _userId: user._id, token: crypto.randomBytes(16).toString('hex') });
token.save(function (err) {
if (err) { return res.status(500).send({ msg: err.message
});
}
var mailOptions = {
to: user.email,
from: 'xxxt#xxx.com',
subject: 'xxxxx verify email',
text:'You are receiving this because you need to verify your email for your account.\n\n' +
'Please click on the following link, or paste this into your browser to complete the process:\n\n' +
'http://' + req.headers.host + '/confirmation/' + token.token + '\n\n' +
'If you did not request this, please ignore this email.\n'
};
smtpTransport.sendMail(mailOptions, function(err) {
if (err) { return res.status(500).send({ msg: err.message }); }
res.status(200).send('A verification email has been sent to ' + user.email + '.');
});
})
},
confirmationPost(req,res, next) {
Token.findOne({ token: req.params.token }, function (err, token) {
if (!token)
{console.log("sss")
} else {
User.findOne({ _id: token._userId, email: req.body.email }, function (err, user) {
if (!user) return console.log(user)
if (user.isVerified) return res.status(400).send({ type: 'already-verified', msg: 'This user has already been verified.' });
user.isVerified = true;
user.save(function (err) {
if (err) { return res.status(500).send({ msg: err.message }); }
res.status(200).send("The account has been verified. Please log in.");
})
});
};
})
}
}
This is my Token Schema
const mongoose = require('mongoose');
const passportLocalMongoose = require('passport-local-mongoose');
const Schema = mongoose.Schema;
const tokenSchema = new mongoose.Schema({
_userId: {
type: Schema.Types.ObjectId,
ref: 'User' },
token: {
type: String,
required: true },
createdAt: {
type: Date, required: true,
default: Date.now, expires: 43200 }
});
tokenSchema.plugin(passportLocalMongoose);
module.exports = mongoose.model('Token', tokenSchema);
lastly my user schema
const mongoose = require('mongoose');
const passportLocalMongoose = require('passport-local-mongoose');
const Schema = mongoose.Schema;
const UserSchema = new Schema({
username: String,
name: String,
email: { type: String, unique: true },
image: String,
isVerified: { type: Boolean, default: false },
password: String,
passwordResetToken: String,
passwordResetExpires: Date,
posts: [
{
type: Schema.Types.ObjectId,
ref: 'Post'
}
]
});
UserSchema.plugin(passportLocalMongoose);
module.exports = mongoose.model('User', UserSchema);
everything works fine until the part where the email verification was sent to my email and when i clicked on the link. It gives an error, i tried to console.log
and found that this line from controllers folder index.js
confirmationPost(req,res, next) {
Token.findOne({ token: req.params.token }, function (err, token) {
if (!token)
{console.log("err")
} else {
User.findOne({ _id: token._userId, email: req.body.email }, function (err, user) {
gives me back null.
how do i link that current line to get the token from the registered user?
i've used postman to send a get request to the confirmation route while giving it back the same token and it works.

Resources