handle mutiple duplicate error mongoose nodejs - node.js

I have User Schema like this
const UserSchema = new Schema({
username: {
type: String,
unique: true,
required: true,
lowercase: true,
trim: true
},
email: {
type: String,
unique: true,
required: true
},
phoneNumber: {
type: Number,
unique: true,
required: true
},
});
When I post a user with duplicate name, phonenumber or email. I receive the error like this:
This is error message E11000 duplicate key error collection: project.users index: username_1 dup key: { : "john" }
So my question is that how I can know it is a duplicate name or duplicate phonenumber. I want to receive the error like this.
This phone number has been already used
This name has been already used

You can catch the error in your error handler. You can get the path name and value using regex. Simple example:
app.use((err, req, res, next) => {
if (err.code === 11000 || err.code === 11001) {
const pathRegex = err.message.match(/\.\$([a-z]+)/)
const path = pathRegex ? pathRegex[1] : '';
const keyRegex = err.message.match(/key:\s+{\s+:\s\"(.*)(?=\")/)
const key = keyRegex ? keyRegex[1] : '';
return res.status(409)
.json({ status: false, message: `${path} '${key}' already exists`})
}
}
You can also check mongoose-unique-validator that produces a mongoose 'ValidationError' that you can parse more easily.

Alternately you can use a path validation mechanism.
UserSchema.path('username').validate(function(value, done) {
this.model('User').count({ username: value }, function(err, count) {
if (err) {
return done(err);
}
done(!count);
});
}, 'User Name already exists');
UserSchema.path('email').validate(function(value, done) {
this.model('User').count({ email: value }, function(err, count) {
if (err) {
return done(err);
}
done(!count);
});
}, 'Email already exists');
UserSchema.path('phoneNumber').validate(function(value, done) {
this.model('User').count({ phoneNumber: value }, function(err, count) {
if (err) {
return done(err);
}
done(!count);
});
}, 'Phone already exists');

Related

Unique array in Mongoose is not throwing error when same key is stored again

I try to store friends in my friends collection. This collection contains a field userId which is an array of user ids. When I store the same id again I want mongoose to throw an error.
My friend schema looks like this:
const friendSchema = mongoose.Schema({
_id: mongoose.Schema.Types.ObjectId,
userId: [{
type: mongoose.Schema.Types.ObjectID,
unique: true,
required: true,
ref: 'User',
}],
});
I am calling it like this:
Friends.findByIdAndUpdate({_id: req.userData.userId}, {$addToSet: { userId: req.body.id } }, {safe:false, upsert: true}, function (error, friend) {
if(error){
return res.status(500).json({
message: 'You already added this user as friend! ' +error,
});
}else if (!friend) {
return res.status(401).json({
message: 'Authentication failed',
});
} else {
Friends.
find({_id: req.userData.userId})
.populate('userId')
.exec(function(error, posts) {
if(!error) {
let returnValue = [];
posts.map((x)=>{
returnValue = x.userId;
})
return res.status(200).json(returnValue);
}else {
return res.status(400).json({message: error.message});
}
})
}

Bcrypt password compare fails when login

when I am trying to login with the defined url , at a point of bcrypting compare password it fails.
login route:-
router.post('/:compId/administration/login' , (req, res, next) => {
Admin.find({'admins.email': req.body.email},{ companyID: req.params.compId })
.exec()
.then(admin => {
if(admin.admins.length < 1) {
return res.status(401).json({
message: "Auth failed. admin not found."
})
}
bcryptt.compare(req.body.password, admin.admins.password, (err, result) =>{
if (err) {
return res.json({
message: "Auth failed. Check email and password"
});
}
if (result && admin.admins.verified === "true"){
const adminEmaill = "xyz#info.com";
const role2 = admin.admins.email===adminEmaill? "superadmin" : "admin";
const token = jwt.sign(
{
email: admin.admins.email,
phoneNo: admin.admins.phoneNumber,
role2,
comID: admin.admins.companyID
},
process.env.JWT_KEY,
{
expiresIn : "1h"
});
return res.status(200).json({
message: "Auth Successful",
token : token
});
}
else{
console.log("admin is not verified");
return res.json({
message: "Admin is not verified"
});
}
});
})
.catch(err =>{
if (err.code == 500)
res.status(500).send(["Something went wrong in login"]);
else
return next(err);
});
});
On response it is not validating my user and throwing message: "Auth failed. Check email and password" in response.
I think I have some error in defining my password path.
My model:-
var adminSchema = new mongoose.Schema({
companyName : {
type: String,
required: "Company name can't be empty.",
required: false
},
companyID: {
type: String,
},
admins: {
email : {
type: String,
required: "Email can't be empty.",
unique: true
},
password: {
type: String,
required: "Password name can't be empty."
},
firstName : {
type: String,
required: "First name can't be empty."
},
lastName : {
type: String,
required: "Last name can't be empty."
},
phoneNumber : {
type: String,
required: "Reqired for further contact. Can't be empty."
},
verified: String,
role: String,
emailResetTokenn: String,
saltSecret: String,
users:[ userSchema ]
}
});
adminSchema.pre('save', function (next){
bcryptt.genSalt(10, (err, salt) => {
bcryptt.hash(this.admins.password, salt, (err, hash) => {
this.admins.password = hash ;
this.admins.saltSecret = salt;
next();
});
});
});
I am not getting why I am getting this ? Is defining of my password is correct ? How can I do this when I have password in nested object ?
You need to call findOne method on mongoose model.
Admin.findOne({'admins.email': req.body.email, companyID: req.params.compId}) ...
The result of find method is an array

Nested array value comparison with another array value in mongoose using nodejs

How can I get addedproducts array elements where productids' of addedproducts array match with every id in productids' array of invitationfrom array(for a particular user email) in the below structure?
var UserSchema = new Schema(
{ email:
{ type: String,
unique: true,
required: true
},
addedproducts:[ {
name: String,
productid:String,
producttype:String
} ],
invitationfrom : [ {
fromemail: String,
productid:[String]
}]
}, {collection: 'user-data-db'});
Try this one
User.find({'addedproducts.productid': "123456"}, {'invitationfrom.productid': "123456"})
.exec(function (err, user) {
if (err) {
return res.status(500).json({message: err});
}
if (!user) {
return res.status(404).json({message: 'No Match Found'});
} else {
return res.status(200).json(user);
}
});

Why Password and Salt automatically in MEAN STACK?

Here I am trying to verify mobile number in user module. I have created token and I sent to user but whenever user is trying to verify using that particular token 'Password' and 'salt' automatically got changed. How to avoid this? Some one help me out .. here I want to update only
user.Mobileverification = 'verfied';
user.Mobileverificationcode = undefined;
user.mobileVerificationExpires = undefined;
Above three variables got changed but I don't know why password and salt has changed?
I have given my routes below:
app.route('/auth/mobilereset/:token').get(users.mobileresetResetToken);
app.route('/auth/mobilereset/:token').post(users.mobilereset);
controller:
exports.mobileresetResetToken = function(req, res) {
User.findOne({
Mobileverificationcode :req.params.token,
mobileVerificationExpires: {
$gt: Date.now()
}
// resetPasswordToken: req.params.token,
// resetPasswordExpires: {
// $gt: Date.now()
// }
}, function(err, user) {
if (!user) {
res.send({
message: 'Invalid token'
});
} else {
console.log('working fine');
}
});
};
exports.mobilereset = function(req, res, next) {
async.waterfall([
function(done) {
User.findOne({
Mobileverificationcode: req.params.token,
mobileVerificationExpires: {
$gt: Date.now()
}
}, function(err, user) {
if (!err && user) {
user.Mobileverification = 'verfied';
user.Mobileverificationcode = undefined;
user.mobileVerificationExpires = undefined;
user.save(function(err) {
if (err) {
return res.status(400).send({
message: errorHandler.getErrorMessage(err)
});
} else {
req.login(user, function(err) {
if (err) {
res.status(400).send(err);
} else {
// Return authenticated user
res.json(user);
done(err, user);
}
});
}
});
} else {
return res.status(400).send({
message: 'reset token is invalid or has expired.'
});
}
});
},
], function(err) {
if (err) return next(err);
});
};
model:
var UserSchema = new Schema({
username: {
type: String,
unique: 'testing error message',
required: 'Please fill in a username',
trim: true
},
password: {
type: String,
default: '',
// validate: [validateLocalStrategyPassword, 'Password should be longer']
},
email: {
type: String,
trim: true,
default: '',
// validate: [validateLocalStrategyProperty, 'Please fill in your email'],
// match: [/.+\#.+\..+/, 'Please fill a valid email address']
},
Mobilenumber: {
type: String,
default: ''
},
roles: {
type: [{
type: String,
enum: ['user', 'admin']
}],
default: ['user']
},
salt: {
type: String
},
provider: {
type: String,
required: 'Provider is required'
},
providerData: {},
additionalProvidersData: {},
updated: {
type: Date
},
created: {
type: Date,
default: Date.now
},
/* For reset password */
Mobileverificationcode: {
type: String,
},
mobileVerificationExpires: {
type: Date
},
Mobileverification: {
type: String,
trim: true,
default: 'Not Verified',
},
resetPasswordToken: {
type: String
},
resetPasswordExpires: {
type: Date
}
});
I don't know if you removed this or not but in MEAN.js user model, you have to be careful with the following code block:
/**
* Hook a pre save method to hash the password
*/
UserSchema.pre('save', function (next) {
if (this.password && this.isModified('password')) {
this.salt = crypto.randomBytes(16).toString('base64');
this.password = this.hashPassword(this.password);
}
next();
});
Which will be called right before you save the user data. That's probably why password and salt keep changing... You are calling user.save in mobile.reset() and that code block above is still present somewhere.
Update:
A possible way of doing it is:
/**
* Hook a pre save method to hash the password
*/
UserSchema.pre('save', function (next) {
if(!this.isModified('Mobileverification') && !this.isModified('Mobileverificationcode') && !this.isModified('mobileVerificationExpires')) {
if (this.password && this.isModified('password')) {
this.salt = crypto.randomBytes(16).toString('base64');
this.password = this.hashPassword(this.password);
}
}
next();
});
However it might need a few adjustments, such as improving this pre-save hook according to your needs and testing password changing and mobile verification to see if nothing is broken.

Model with unique attribute is being created twice

I am using this passport-generate-auth module, and I am trying to get my grasp around understanding this whole thing.
So, in my User model, I've got
var User = {
schema: true,
attributes: {
username: {
type: 'string',
unique: true,
required: true
},
email: {
type: 'email',
unique: true,
required: true
},
password: {
type: 'string',
required: true,
minLength: 8
},
}
};
module.exports = User;
And when I call
exports.register = function (req, res, next) {
var email = req.param('email')
, username = req.param('username')
, password = req.param('password');
User.create({
username: username
, email: email
, password: password
}, function (err, user) {
if (err) {
if (err.code === 'E_VALIDATION') {
if (err.invalidAttributes.email) {
req.flash('error', 'Error.Passport.Email.Exists');
} else {
req.flash('error', 'Error.Passport.User.Exists');
}
}
return next(err);
}
});
};
};
when providing username and email that already exist in the database, the new entry is stored in the DB, instead of giving me an error msg.
Isn't User.create() supposed to take care of checking in the schema attributes rules whether they are unique and then check the records in the DB for a record with a value that already exists?

Resources