Can't update User informations MongoDB validation error - node.js

Why can't I Update User if my passwordConfirm is required: true? This is my User model:
const crypto = require('crypto');
const mongoose = require('mongoose');
const validator = require('validator');
const bcrypt = require('bcryptjs');
const userSchema = new mongoose.Schema({
name: {
type: String,
required: [true, 'Please tell us your name!'],
},
email: {
type: String,
required: [true, 'Please provide your email'],
unique: true,
lowercase: true,
validate: [validator.isEmail, 'Please provide a valid email'],
},
photo: {
type: String,
default: 'default.jpg',
},
role: {
type: String,
enum: ['user', 'guide', 'lead-guide', 'admin'],
default: 'user',
},
password: {
type: String,
required: [true, 'Please provide a password'],
minlength: 8,
select: false,
},
passwordConfirm: {
type: String,
required: [true, 'Please confirm your password'],
validate: {
validator: function (el) {
return el === this.password;
},
message: 'Passwords are not the same!',
},
},
passwordChangedAt: Date,
passwordResetToken: String,
passwordResetExpires: Date,
active: {
type: Boolean,
default: true,
select: false,
},
});
userSchema.pre('save', async function (next) {
// Only run this function if password was actually modified
if (!this.isModified('password')) return next();
// Hash the password with cost of 12
this.password = await bcrypt.hash(this.password, 12);
// Delete passwordConfirm field
this.passwordConfirm = undefined;
next();
});
userSchema.pre('save', function (next) {
if (!this.isModified('password') || this.isNew) return next();
this.passwordChangedAt = Date.now() - 1000;
next();
});
userSchema.pre(/^find/, function (next) {
// this points to the current query
this.find({ active: { $ne: false } });
next();
});
userSchema.methods.correctPassword = async function (
candidatePassword,
userPassword
) {
return await bcrypt.compare(candidatePassword, userPassword);
};
userSchema.methods.changedPasswordAfter = function (JWTTimestamp) {
if (this.passwordChangedAt) {
const changedTimestamp = parseInt(
this.passwordChangedAt.getTime() / 1000,
10
);
return JWTTimestamp < changedTimestamp;
}
// False means NOT changed
return false;
};
userSchema.methods.createPasswordResetToken = function () {
const resetToken = crypto.randomBytes(32).toString('hex');
this.passwordResetToken = crypto
.createHash('sha256')
.update(resetToken)
.digest('hex');
console.log({ resetToken }, this.passwordResetToken);
this.passwordResetExpires = Date.now() + 10 * 60 * 1000;
return resetToken;
};
const User = mongoose.model('User', userSchema);
module.exports = User;
And this is my controller:
exports.updateUserProfile = catchAsync(async (req, res) => {
const user = await User.findById(req.user._id);
if (user) {
user.name = req.body.name || user.name;
user.email = req.body.email || user.email;
if (req.body.password) {
user.password = req.body.password;
}
const updatedUser = await user.save();
res.json({
name: updatedUser.name,
});
} else {
res.status(404);
throw new Error('User not found');
}
});
res.json doesn't matter, I just want to get something back except error..
This is error from postman:
"error": {
"errors": {
"passwordConfirm": {
"name": "ValidatorError",
"message": "Please confirm your password",
"properties": {
"message": "Please confirm your password",
"type": "required",
"path": "passwordConfirm"
},
"kind": "required",
"path": "passwordConfirm"
}
},
"_message": "User validation failed",
"statusCode": 500,
"status": "error",
"name": "ValidationError",
"message": "User validation failed: passwordConfirm: Please confirm your password"
},
When I remove required: true code passwordConfirm then it does everything properly, I do not require anywhere in the controller to change the password or something like that

you can try this
exports.updateUserProfile = catchAsync(async (req, res) => {
const user = await User.findById(req.user._id);
const userToSave = {}
if (user) {
const userToSave = Object.assign({}, user)
userToSave.name = req.body.name || user.name;
userToSave.email = req.body.email || user.email;
if (req.body.password) {
userToSave.password = req.body.password;
}
const dbSave = new User(userToSave);
await dbSave.save();
res.json({
name: updatedUser.name,
});
} else {
res.status(404);
throw new Error('User not found');
}
});

Related

how to use populate method with react and node.js?

i have two collection in my db "users" and "forms"
each user has a table of forms
i used populate method to do this and it works
this is the model of user:
const Schema = mongoose.Schema
const UserSchema = new Schema({
firstName: {
type: 'string',
required: ' Firstname is Required'
},
lastName: {
type: 'string',
required: ' lastName is Required'
},
email: {
type: 'string',
required: ' email is Required'
},
phone: {
type: 'string',
},
entrprise: {
type: 'string'
},
password: {
type: 'string',
required: ' password is Required'
},
forms: [{
type: mongoose.Schema.Types.ObjectId,
ref: "Form"
}
]
})
const User = mongoose.model('User', UserSchema)
module.exports = User
this the model of form
const Schema = mongoose.Schema
const FormSchema = new Schema({
logo: {
type: String,
},
compagnyName: {
type: String,
},
title: {
type: String,
},
description: {
type: String,
},
userId: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User'
},
cardQuestion: [
{
questionTitle: String,
questionType: String,
questionCourte: String,
questionLongue: String,
choixMultiple: String,
caseaCocher: String,
telechargerFichier: String,
date: Date,
heure: String,
delete: false,
obligatoire: false,
}
]
})
const Form = mongoose.model('Form', FormSchema)
module.exports = Form
and this is how i use populate method
const getUser = async (req, res) => {
try {
const { userId } = req.params;
if (!userId) return res.status(400).json({ message: "ERROR ID!" });
const result = await User
.findById(userId).populate('forms')
.exec()
return res.status(200).json({ message: "Success", result });
} catch (err) {
res.status(500).json({ message: "INTERNAL ERROR SERVER!" });
console.log(err.message);
}
};
i added a user with forms using postman with the post method
when i try to add a form with react
const response = await axios.post(`${API_URL}/form/:userId`,
{
...form,
userId: localStorage.getItem('userId')
},
{
headers: {
authorization: localStorage.getItem('userId')
}
}
i get the form with the user id like this:
{
"_id": "6022916bf1d1060f84bc17d0",
"compagnyName": "axa",
"description": "recruitement",
"userId": "60214c5ec0491fcb2d8c29e8",
"__v": 0,
"cardQuestion": []
},
i find the new form in the forms collection but when i get the user ,the forms field doesn't update (still empty if i don't add the table of forms manually)
"result": {
"forms": [],
"_id": "60214c5ec0491fcb2d8c29e8",
"firstName": "moon",
"lastName": "lea",
"email": "moon15#gmail.com",
"password": "$2b$10$bnH0cEBQKktgaKHfBac3L.xUUNEYt9HaqKdLKxLOERrHPG4MVPPFS",
"phone": "087654266377",
"__v": 0
}
}
this is how i add a user
const register = async (req, res) => {
try {
const { firstName, lastName, email, phone, password, forms } = req.body
if (!firstName || !lastName || !email || !phone || !password)
return res.status(400).json({ message: 'ERROR!' })
//CHECKING EXISTING USER
const found = await User.findOne({ email })
console.log(found)
if (found) return res.status(400).json({ message: 'Email already exist' })
console.log(found)
//HASHING THE PASSWORD
const hashPassword = await bcrypt.hash(password, saltRounds)
console.log(hashPassword)
//CREATING THE NEW USER
const newUser = new User()
newUser.firstName = firstName
newUser.lastName = lastName
newUser.email = email
newUser.password = hashPassword
if (phone) newUser.phone = phone
if (forms) newUser.forms = forms
console.log('i')
//SAVING THE NEW USER
const result = await newUser.save()
console.log(result)
if (!result) return res.status(400).json({ message: 'failed to create user' })
can someone help?
maybe you have to change .populate('forms') in getUser to .populate('form')
i think it save it in singular shape

Routing in NodeJs

I am working on an eCommerce application, and I am trying to save users in my database but when I hit the API in the postmen then instead of :
res.json({
name: user.name,
email: user.email,
id: user._id
});
});
**instead of this code following code is running
user.save((err, user) => {
if (err) {
return res.status(400).json({
err: "NOT able to save user in DB"
});
}
//the complete code of my "auth.js" file is as following:
const User = require("../models/user");
exports.signup = (req, res) => {
const user = new User(req.body);
user.save((err, user) => {
if (err) {
return res.status(400).json({
err: "NOT able to save user in DB"
});
}
res.json({
name: user.name,
email: user.email,
id: user._id
});
});
};
exports.signout = (req, res) => {
res.json({
message: "User signout"
});
};
///and the complete code of my user model file is as following:
var mongoose = require("mongoose");
const crypto = require("crypto");
const uuidv1 = require("uuid/v1");
var userSchema = new mongoose.Schema(
{
name: {
type: String,
required: true,
maxlength: 32,
trim: true
},
lastname: {
type: String,
maxlength: 32,
trim: true
},
email: {
type: String,
trim: true,
required: true,
unique: true
},
userinfo: {
type: String,
trim: true
},
encry_password: {
type: String,
required: true
},
salt: String,
role: {
type: Number,
default: 0
},
purchases: {
type: Array,
default: []
}
},
{ timestamps: true }
);
userSchema
.virtual("password")
.set(function(password) {
this._password = password;
this.salt = uuidv1();
this.encry_password = this.securePassword(password);
})
.get(function() {
return this._password;
});
userSchema.methods = {
autheticate: function(plainpassword) {
return this.securePassword(plainpassword) === this.encry_password;
},
securePassword: function(plainpassword) {
if (!plainpassword) return "";
try {
return crypto
.createHmac("sha256", this.salt)
.update(plainpassword)
.digest("hex");
} catch (err) {
return "";
}
}
};
module.exports = mongoose.model("User", userSchema);
SO please anyone tell me how to solve this problem while hiting this code to api mongo shell is also opend and mean while i also keep ROBO3T connected.

How to update Password with new password on Reset Password

The GITHUB REPO i'm using code repository
I'm trying to reset the user password on redirecting the user to reset password page. On the 1st Singup I'm hashing the password and salt is generated and stored in database using CRYPTO. On reset password the new password is not getting updated it does not allow to signin using the updated password.
I tried using response.password which gives the updated password.Still couldn't figure out the solution.
Reset password :
exports.resetPassword = (req,res) => {
const {resetPasswordLink, newPassword } = req.body
if(resetPasswordLink){
jwt.verify(resetPasswordLink,process.env.JWT_RESET_PASSWORD, function(err,decoded){
if(err){
return res.status(401).json({
error : ' The Link has been expired ! , Try Again '
})
}
User.findOne({resetPasswordLink},(err,user)=>{
if(err || !user){
return res.status(401).json({
error: ' The Link has been expired ! , Try Again '
})
}
const updatedFields = {
password: newPassword,
resetPasswordLink: ''
}
user = _.extend(user,updatedFields)
user.save((err,result)=>{
if(err){
return res.status(400).json({
error: errorHandler(err)
})
}
return res.json({
message: ` Your Password Has Been Successfully Reset , Please Return to the SignIn Page to SignIn `
// result.password
})
})
})
})
}
}
UPDATE 4th August :
Here's the complete USER model
User Schema :
const mongoose = require('mongoose');
const crypto = require('crypto');
const userSchema = new mongoose.Schema(
{
username: {
type: String,
trim: true,
required: true,
max: 32,
unique: true,
index: true,
lowercase: true
},
name: {
type: String,
trim: true,
required: true,
max: 32
},
email: {
type: String,
trim: true,
required: true,
unique: true,
lowercase: true
},
profile: {
type: String,
required: true
},
hashed_password: {
type: String,
required: true
},
salt: String,
about: {
type: String
},
role: {
type: Number,
default: 0
},
photo: {
data: Buffer,
contentType: String
},
resetPasswordLink: {
data: String,
default: ''
}
},
{ timestamp: true }
);
userSchema
.virtual('password')
.set(function(password) {
// create a temporarity variable called _password
this._password = password;
// generate salt
this.salt = this.makeSalt();
// encryptPassword
this.hashed_password = this.encryptPassword(password);
})
.get(function() {
return this._password;
});
userSchema.methods = {
authenticate: function(plainText) {
return this.encryptPassword(plainText) === this.hashed_password;
},
encryptPassword: function(password) {
if (!password) return '';
try {
return crypto
.createHmac('sha1', this.salt)
.update(password)
.digest('hex');
} catch (err) {
return '';
}
},
makeSalt: function() {
return Math.round(new Date().valueOf() * Math.random()) + '';
}
};
module.exports = mongoose.model('User', userSchema);
problem is in your signin function where you have set expiry of 'jwt' and 'cookie' use { expiresIn: '1d' } instead of { expiresIn: '1' } because '1' means your jwt and cookie expires in 1 ms
const token = jwt.sign({ _id: user._id }, process.env.JWT_SECRET, { expiresIn: '1d' });
res.cookie('token', token, { expiresIn: '1d' });

Mongoose instance methods. Results in Type error. Method not found

The user model
```
const crypto = require('crypto');
const mongoose = require('mongoose');
const validator = require('validator');
const bcrypt = require('bcryptjs');
const UserSchema = new mongoose.Schema(
{
username: {
type: String,
unique: [true, 'Username already taken'],
required: [true, 'Please enter a usernam']
},
dob: String,
email: {
type: String,
required: [true, 'Please provide your email'],
unique: true,
lowercase: true,
validate: [validator.isEmail, 'Please provide with a mail']
},
profile_img: String, //url of the image |||Should think how we have store the image|||
password: {
type: String,
required: [true, 'Please provide with a password'],
minlength: 8,
select: false
},
passwordConfirm: {
type: String,
required: [true, 'Please confirm your password'],
validate: {
validator: function(el) {
return el === this.password;
},
message: "Password don't match"
}
},
passwordChangedAt: Date,
passwordResetToken: String,
passwordResetExpires: Date,
areaOfInterest: [], //array of String
friends: [
{
type: mongoose.Schema.ObjectId,
ref: 'User'
}
],
isBoarded: { type: Boolean, default: false },
createdAt: {
type: Date,
default: Date.now()
}
},
{
toJSON: { virtuals: true },
toObject: { virtuals: true }
}
);
//virtual populate
UserSchema.virtual('posts', {
ref: 'Post',
foreignField: 'userId',
localField: '_id'
});
UserSchema.pre('save', async function(next) {
if (!this.isModified('password')) return next();
this.password = await bcrypt.hash(this.password, 12);
this.passwordConfirm = undefined;
next();
});
UserSchema.pre('save', function(next) {
if (!this.isModified('password') || this.isNew) return next();
this.passwordChangedAt = Date.now() - 1000;
next();
});
UserSchema.methods.correctPassword = async function(passwordToMatch, password) {
return await bcrypt.compare(passwordToMatch, password);
};
UserSchema.methods.changedPasswordAfter = function(JWTTimeStamp) {
if (this.passwordChangedAt) {
const changedTimeStamp = parseInt(
this.passwordChangedAt.getTime() / 1000,
10
);
return JWTTimeStamp < changedTimeStamp;
}
return false;
};
This is where the method is added using the methods property before the exporting the model
UserSchema.methods.createPasswordResetToken = function() {
const resetToken = crypto.randomBytes(32).toString('hex');
this.passwordResetToken = crypto
.createHash('sha256')
.update(resetToken)
.digest('hex');
// console.log({ resetToken }, this.passwordResetToken);
this.passwordResetExpires = Date.now() + 10 * 60 * 1000;
return resetToken;
};
const User = mongoose.model('User', UserSchema);
module.exports = User;
And method is called in this fuction
const { promisify } = require('util');
const crypto = require('crypto');
const jwt = require('jsonwebtoken');
const User = require('../models/userModel');
const catchAsync = require('../utils/catchAsync');
const AppError = require('../utils/appError');
const sendEmail = require('../utils/email');
exports.forgotPassword =async (req, res, next) => {
console.log(User.schema.methods);
const user = await User.findOne({ email: req.body.email });
console.log(user);
if (!user) {
return next(new AppError('No user with that email address', 404));
}
Here is where the error occurs where I have called the method with the error "user.createPassswordResetToken is not a function","stack":"TypeError: user.createPassswordResetToken is not a function\n at controllers/authController.js:117:27\n at processTicksAndRejections (internal/process/task_queues.js:93:5)"
In the above mentioned error this is the line no which it points to
const resetToken = user.createPassswordResetToken();
await user.save({ validateBeforeSave: false });
const resetURL = `${req.protocol}://${req.get(
'host'
)}/api/v1/users/resetPassword/${resetToken}`;
const message = `Forgot your Password? Submit your new password and confirm your password at ${resetURL}.\n If you didn't forgot your password, please ignore this email.`;
The console log of User.schema.methods is
{
correctPassword: [AsyncFunction (anonymous)],
changedPasswordAfter: [Function (anonymous)],
createPasswordResetToken: [Function (anonymous)]
}
the method in user schema is createPasswordResetToken, while in the function you called another non existing function createPassswordResetToken
use createPasswordResetToken rather than createPassswordResetToken

Create a list in an mongo model of a express/mongo API

i have a api with 2 models, Users and Books and i want to be able to do a favourite book list in the users model, how can it be done?
I think you could make a list of books within the users model but I do not really know how it should be done in mongo models or what the method would be like
There are my models:
User model
const schema = new Schema({
username: { type: String, unique: true, required: true },
hash: { type: String, required: true },
firstName: { type: String, required: true },
lastName: { type: String, required: true },
email: { type: String, required: false },
image: { type: String, required: false },
createdDate: { type: Date, default: Date.now }
});
Book model
const schema = new Schema({
BookTitulo: String,
BookSinopsis: String,
BookISBN: String,
BookPortada: String,
BookGenero: String,
BookAutor: String,
BookPaginas: Number,
BookPuntuacion: Number,
BookPrecio: Number,
updatedAt: { type: Date, default: Date.now },
});
And the user methods:
async function authenticate({ username, password }) {
const user = await User.findOne({ username });
if (user && bcrypt.compareSync(password, user.hash)) {
const { hash, ...userWithoutHash } = user.toObject();
const token = jwt.sign({ sub: user.id }, config.secret);
return {
...userWithoutHash,
token
};
}
}
async function getAll() {
return await User.find().select('-hash');
}
async function getById(id) {
return await User.findById(id).select('-hash');
}
async function create(userParam) {
// validate
if (await User.findOne({ username: userParam.username })) {
throw 'Username "' + userParam.username + '" is already taken';
}
const user = new User(userParam);
// hash password
if (userParam.password) {
user.hash = bcrypt.hashSync(userParam.password, 10);
}
// save user
await user.save();
}
async function update(id, userParam) {
const user = await User.findById(id);
// validate
if (!user) throw 'User not found';
if (user.username !== userParam.username && await User.findOne({ username: userParam.username })) {
throw 'Username "' + userParam.username + '" is already taken';
}
// hash password if it was entered
if (userParam.password) {
userParam.hash = bcrypt.hashSync(userParam.password, 10);
}
// copy userParam properties to user
Object.assign(user, userParam);
await user.save();
}
async function _delete(id) {
await User.findByIdAndRemove(id);
}

Resources