Mongo updating a user - node.js

I have a sample piece of Node.js code which pulls a user from the database based on an email, does some checks, does a findOne by ID and updates, like this:
User.findOne({ email }, (err, user) => {
if (err) { return next(err); }
if (!user) {
return res.status(422).send({ error: { message: "User doesnt exists", resend: false } });
}
if (user.auth.used) {
return res.status(422).send({ error: { message: "link already used", resend: false } });
}
if (new Date() > user.auth.expires) {
return res.status(422).send({ error: { message: "link already expired", resend: true } });
}
if (token !== user.auth.token) {
return res.status(422).send({ error: { message: "something has gone wrong, please sign up again", resend: false } });
}
User.findByIdAndUpdate(user.id, { role: 1, auth: { used: true } }, (err) => {
if (err) { return next(err); }
const { email, firstname, lastname } = user;
res.json({ token: tokenForUser(user), email, firstname, lastname });
});
});
Could I not just update and save the user I already have, like this?:
Token.findOne({ token: req.body.token }, function (err, token)
{
if (!token || token !== user.auth.token) return res.status(422).send({ error: { message: "Link doesn't exist or has expired", resend: true } });
// If we found a token, find a matching user
User.findOne({ _id: token._userId }, function (err, user)
{
if (!user) return res.status(422).send({ error: { message: "We couldn't find a user for this token", resend: false } });
if (user.isVerified) return res.status(422).send({ error: { message: "link already used", resend: true } });
// Verify and save the user
user.isVerified = true;
user.save(function (err)
{
if (err) { return res.status(500).send({ msg: err.message }); }
res.json({ token: tokenForUser(user), req.user.email, req.user.firstName, req.user.lastName, req.user.company })
});
});
});

Related

sorting in mongoose is providing random result

I have a collection named payments with field named 'sorter' in it, I want to sort my results descending with the 'sorter' field
I have no Idea how to do it
return ApiProtectedRoute(
req,
res,
(protectedUserid, protectedUsername, protectedEmail, protectedBalance) => {
paymentModels
.find({
userId: protectedUserid
}, function(err, payments) {
if (payments.length == 0) {
res.status(400).json({
error: true,
message: "No payments found",
info: "getPayments",
});
} else if (payments.length > 0) {
let paymentsWithUsername = [];
payments.forEach((payment) => {
userModels.findOne({
_id: payment.userId
}, function(err, user) {
if (err) console.log(err);
paymentsWithUsername.push({
...payment._doc,
username: user.username,
email: user.email,
});
if (paymentsWithUsername.length === payments.length) {
res.status(200).json({
error: false,
payments: paymentsWithUsername,
info: "getPayments",
});
}
});
});
} else {
res.status(400).json({
error: true,
message: "Something went wrong",
info: "getPayments",
});
}
})
.sort({
sorter: -1
});
}
);
this code shows random result at everytime
Payments collection:

Cannot set headers after they are sent to the client error is showing

Error is showing if a user is already exiting. The error is not showing if the user does not exist in the database
Plz tell me if there is any better way to check the username thank you in advance
exports.updateUser = async(req, res) => {
if (req.body.userId === req.params.id) {
if (req.body.userName || req.body.email) {
await User.findOne({
$or: [{
email: req.body.email
}, {
username: req.body.userName
}]
}).exec((err, user) => {
if (err) {
return res.status(500).json(err);
} else if (user) {
if (user.username === req.body.userName) {
return res.status(400).json({ error: "User Name already exists" });
//err = "User Name already exists";
} else {
return res.status(400).json({ error: "Email already exists" });
}
}
})
}
if (req.body.password) {
const salt = await bcrypt.genSalt(10);
req.body.password = await bcrypt.hash(req.body.password, salt);
}
await User.findByIdAndUpdate(
req.params.id, { $set: req.body }, { new: true }
).exec((err, user) => {
if (err) {
return res.status(500).json(err);
}
if (user) {
return res.status(200).json("User Updated successfully!"); // here error is showing.
}
});
} else {
res.status(401).json("You can update only your account!");
}
};
Don't send response inside exec, because if after sending response it'll execute rest of the code and try to send a response but response is already sent.
exports.updateUser = async (req, res) => {
try {
if (req.body.userId === req.params.id) {
if (req.body.userName || req.body.email) {
await User.findOne({
$or: [
{
email: req.body.email,
},
{
username: req.body.userName,
},
],
}).exec((err, user) => {
if (err) {
throw {
code: 400,
error: err,
};
} else if (user) {
if (user.username === req.body.userName) {
throw {
code: 400,
error: { error: "User Name already exists" },
};
}
throw {
code: 400,
error: "Email already exists",
};
}
});
}
if (req.body.password) {
const salt = await bcrypt.genSalt(10);
req.body.password = await bcrypt.hash(req.body.password, salt);
}
await User.findByIdAndUpdate(
req.params.id,
{ $set: req.body },
{ new: true }
).exec((err, user) => {
if (err) {
throw {
code: 500,
error: err,
};
}
if (user) {
return res.status(200).json("User Updated successfully!"); // here error is showing.
}
});
} else {
throw {
code: 401,
error: "You can update only your account!",
};
}
} catch (e) {
res.status(e.code).json(e.error);
}
};

why this error is showing? cant set header after they sent to the client

this is mylogin code : you can see the token from here and the next photo is the auth for token
can anyone explain it please............................................................................................................................
const login = (req, res, next) => {
var username = req.body.username;
var password = req.body.password;
User.findOne({$or: [{email: username}, {phoneNumber: username}]})
.then(user => {
if (user) {
bcrypt.compare(password, user.password, function(err, result){
if (err) {
res.json({
title: 'Server error',
error: err
})
console.log('err');
}if (result){
const maxAge = 24 * 60 * 60
const createToken = jwt.sign({ _id: user._id } , process.env.TOKEN_SECRET, { expiresIn: maxAge });
res.header('auth-token',token);
}else{
return res.status(401).json({
title: 'password wrong',
error: 'invalid credentials'
})
}
})
}
else {
return res.status(401).JSON({
title: 'password wrong',
error: 'invalid credentials'
})
}
})
If you look at the bcrypt.compare callback the res.json is executed twice:
function(err, result) {
if (err) {
res.json({}) // ONE
}
if (result) {
// skipped
} else {
return res.status(401).json({ // TWO
title: 'password wrong',
error: 'invalid credentials'
})
}
}
Add a return statement in the if(err)
Using a linter would help you to avoid these oversight

Getting error while logging in 402 (Payment Required)

I don't understand it. I am not able to login. User is already in my database, and when I log in, it simply says:
POST http://localhost:3000/api/v1/users/login 402 (Payment Required)
When I register for the first time, and then login, login is successful. If I logout, and then try to log in with that same email and password, it's throwing me the above error. I'm not even using someone's API. It's my own created one. It's sending me a response of "incorrect password"
Here's the controller:
loginUser: (req, res, next) => {
const { email, password } = req.body
if (!email || !password) {
return res.status(400).json({ message: "Email and password are must" })
}
User.findOne({ email }, (err, user) => {
if (err) {
return next(err)
} else if (!validator.isEmail(email)) {
return res.status(400).json({ message: "Invalid email" })
} else if (!user) {
return res.status(402).json({ error: "User not found" })
} else if (!user.confirmPassword(password)) {
return res.status(402).json({ error: "incorrect password" })
}
})
}
User model
const mongoose = require("mongoose")
const bcrypt = require("bcrypt")
const Schema = mongoose.Schema
const userSchema = new Schema({
username: { type: String, required: true },
email: { type: String, reuired: true },
password: { type: String, required: true },
posts:[{ type: Schema.Types.ObjectId, ref: "Post" }]
}, { timestamps: true })
userSchema.pre("save", function (next) {
if (this.password) {
const salt = bcrypt.genSaltSync(10)
this.password = bcrypt.hashSync(this.password, salt)
}
next()
})
userSchema.methods.confirmPassword = function (password) {
return bcrypt.compareSync(password, this.password)
}
const User = mongoose.model("User", userSchema)
module.exports = User
registration controller
registerUser: (req, res) => {
const { username, email, password } = req.body
User.create(req.body, (err, createdUser) => {
if (err) {
return res.status(500).json({ error: "Server error occurred" })
} else if (!username || !email || !password) {
return res.status(400).json({ message: "Username, email and password are must" })
} else if (!validator.isEmail(email)) {
return res.status(400).json({ message: "Invaid email" })
} else if (password.length < 6) {
return res.status(400).json({ message: "Password should be of at least 6 characters" })
}
else {
return res.status(200).json({ user: createdUser })
}
})
}
Edit
loginUser: async (req, res, next) => {
const { email, password } = req.body
if (!email || !password) {
return res.status(400).json({ message: "Email and password are must" })
}
await User.findOne({ email }, (err, user) => {
if (err) {
return next(err)
} else if (!validator.isEmail(email)) {
return res.status(400).json({ message: "Invalid email" })
} else if (!user) {
return res.status(402).json({ error: "User not found" })
} else if (!user.confirmPassword(password)) {
return res.status(402).json({ error: "incorrect password" })
}
})
}
new post controller
newPost: (req, res) => {
const data = {
title: req.body.title,
content: req.body.content,
user: req.user.userId
}
Post.create(data, (err, newPost) => {
if (err) {
return res.status(500).json({ error: err })
} else if (!newPost) {
return res.status(400).json({ message: "No Post found" })
} else if (newPost) {
User.findById(req.user.userId, (err, user) => {
user.posts.push(newPost._id) //pushing posts documnet objectid to the post array of the user document
user
.save()
.then(() => {
return res.json(200).json({ user })
})
.catch(err => {
return res.status(500).json({ error: err })
})
})
}
})
}
You might want to refactor your code so that you do the bcrypt operations in controller not in the model. You are checking this.password after the user is updated (creating new posts) and since this is the user, the below code is being met each time you update the user object.
if (this.password) {
const salt = bcrypt.genSaltSync(10)
this.password = bcrypt.hashSync(this.password, salt)
}
So your hashing it every time you update the user (create a post). Instead, remove the above code from the userSchema.pre(...) and try doing the bcrypt hashing only when the user first registers.
registerUser: (req, res) => {
var { username, email, password } = req.body
if (password) {
const salt = bcrypt.genSaltSync(10)
password = bcrypt.hashSync(password, salt)
}
User.create(req.body, (err, createdUser) => {
if (err) {
return res.status(500).json({ error: "Server error occurred" })
} else if (!username || !email || !password) {
return res.status(400).json({ message: "Username, email and password are must" })
} else if (!validator.isEmail(email)) {
return res.status(400).json({ message: "Invaid email" })
} else if (password.length < 6) {
return res.status(400).json({ message: "Password should be of at least 6 characters" })
}
else {
return res.status(200).json({ user: createdUser })
}
})
}
This way the hashing occurs only once at the creation of the user and should remain consistent throughout other operations.
As for the Can't set headers after they are sent error, you might be sending a response twice, since the error appears to come from the posts controller. You are likely sending the user response and the post response. Maybe don't send the posts response since you will be sending it along in the user response.
More info on the error here.

How to update token when the user logs in in express js?

I want to update token in user collection when the user logs in. So far, I have tried this.
router.post("/login", (req, res, next) => {
User.find({ email: req.body.email })
.exec()
.then(user => {
if (user.length < 1) {
return res.status(401).json({
message: "Auth failed"
});
}
bcrypt.compare(req.body.password, user[0].password, (err, result) => {
if (err) {
return res.status(401).json({
message: "Auth failed"
});
}
if (result) {
const token2 = jwt.sign(
{
email: user[0].email,iat: Math.floor(Date.now() / 1000) - 30
},
"123",
{
expiresIn: "1h"
}
);
User.update({token : token2 })
.exec()
return res.status(200).json({
message: "Auth successful",
token: token2
});
}
res.status(401).json({
message: "Auth failed"
});
});
})
.catch(err => {
console.log(err);
res.status(500).json({
error: err
});
});
});
here new token is getting generated but it is not being saved in the user collection. I want to update new token in the collection.
Can anyone know where I am missing?
Try Something like:
router.post("/login", async (req, res) => {
try{
const user = await User.find({ email: req.body.email });
if (user.length < 1) {
return res.status(401).json({
message: "Auth failed"
});
}
bcrypt.compare(req.body.password, user[0].password, (err, result) => {
if (err) {
return res.status(401).json({
message: "Auth failed"
});
}
if (result) {
const token2 = jwt.sign(
{
email: user[0].email,iat: Math.floor(Date.now() / 1000) - 30
},
"123",
{
expiresIn: "1h"
}
);
User.update({_id:user[0]._id},{$set:{token : token2 }},{new: true});
return res.status(200).json({
message: "Auth successful",
token: token2
});
}
res.status(401).json({
message: "Auth failed"
});
});
}
catch(err){
res.status(500).json({
error: err
});
}
});
Try below code:
jwt.sign(
{
email: user[0].email,iat: Math.floor(Date.now() / 1000) - 30
},
"123",
{
expiresIn: "1h"
}, function(err, token2) {
User.update({token : token2 }).exec() //Change the query to update
return res.status(200).json({
message: "Auth successful",
token: token2
});
}
);
Let me know if it helps.
Firstly understand why do you need to refresh the token. Once you create the token you can store that token as a session variable in a web, internal storage of your mobile etc...
Instead of refresh of token you can create another token.
In another way you can set timeout for the token. Token will be invalid after that time period.

Resources