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.
Related
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');
}
});
I try to make one method for signup to two types of user ' buyer and seller ' .
When I save seller , I should get all information about their ' storeinfo ' , see my updated code below .
I used populate() but I get an empty array .
Is my idea wrong ?
code for signup
exports.signupUser = async (req, res, next) => {
role = req.body.role
const user = new User(req.body)
const seller = new Seller(req.body)
try{
if(role=='seller'){
await seller.save()
await user.save()
const token = await user.generateToken()
res.status(200).send({
error: null,
apiStatus:true,
data: {user,seller ,token}
})
}
await user.save()
const token = await user.generateToken()
res.status(200).send({
error: null,
apiStatus:true,
data: {user, token}
})
}
catch(error){
res.status(400).send({
error: error.message,
apiStatus:false,
data: 'unauthorized user'
})
}
}
code for login
exports.login = async (req, res) => {
try{
const user = await User.findUserByCredentials(req.body.email, req.body.password)
const token = await user.generateToken()
console.log(user.role)
if(user.role=='seller'){
console.log(user._id)
await User.findOne({_id: user._id}).populate('storeinfo').exec(function(err, user) {
if (err) {console.log(err)}
res.status(200).send({
error: null,
apiStatus:true,
user,token
})})
}
res.status(200).send({
error: null,
apiStatus:true,
data: {user, token}
})
}
catch(error){
res.status(400).send({
error: error.message,
apiStatus:false,
data: 'Something went wrong'
})
}
}
schema user
const userSchema = new Schema({
first_name: { type: String,required:true},
last_name: { type: String,required:true},
email: { type: String, unique: true, required: true, trim: true,lowercase: true ,validate(value) {
if (!validator.isEmail(value)) throw new Error("Invalid Email"); },
},
password: { type: String,minlength: 6,required: true, trim: true, match: /^(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[^a-zA-Z0-9]).{8,}$/},
role: { type: String, enum: ['admin','seller', 'user'], default: 'user' ,required: true},
mobileNumber: { type: String,default:'',trim: true,
validate(value) {
if (!validator.isMobilePhone(value, ["ar-EG"]))
throw new Error("invalid phone number");
},
},
address: {
type: String,
required: true,
trim: true
},
storeinfo:{
type: mongoose.Schema.Types.ObjectId,
ref:'Seller',
},
tokens: [{
token: {
type: String,
required: true
}
}]
}, {
timestamps: true
})
userSchema.methods.generateToken = async function(){
const user = this
const token = jwt.sign({_id: user._id.toString() , role:user.role}, process.env.JWTKEY)
user.tokens = user.tokens.concat({token})
await user.save()
return token
}
// login
userSchema.statics.findUserByCredentials = async(email, password)=>{
const user = await User.findOne({email})
if(!user) throw new Error('invalid email')
const matched = await bcrypt.compare(password, user.password)
if(!matched) throw new Error('invalid password')
return user
}
const User = mongoose.model('User', userSchema)
module.exports = User
seller schema
const SellerSchema = new Schema(
{
storeName: {
required: true,
type: String,
},
category: {
type: String,
required: true
},
image: {
type: String,
// required: true,
},
addresses:[
{
street: String,
locality: String,
aptName: String,
lat: Number,
lng: Number,
}
],
numberOfBranches: Number,
_userId:{ type: Schema.Types.ObjectId, ref: 'User'},
items: [{ type: Schema.Types.ObjectId, ref: "Item" }]
},
{ timestamps: true }
);
const Seller = mongoose.model('Seller', SellerSchema)
module.exports = Seller
I'm creating a web application that has chats, and users can join the chat. Once the user joins the chat, I want to add the user's ID as well as their name to the users field in the Chat schema. So far, I'm able to add their ID, but I am finding it difficult to add their name. Below, I have attached my Chat mongoose model, as well as my route to add a user to a chat. Also, I have attached my User mongoose model. Any help is greatly appreciated. Thank you!
Chat model:
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const ChatSchema = new Schema({
title: {
type: String,
required: true
},
password: {
type: String,
required: true
},
creator: {
type: Schema.Types.ObjectId,
ref: 'user'
},
users: [
{
user: {
type: Schema.Types.ObjectId,
ref: 'user'
},
name: {
type: String,
required: true
}
}
],
code: {
type: String,
required: true
},
posts: [
{
text: {
type: String,
required: true
},
title: {
type: String,
required: true
},
date: {
type: Date,
default: Date.now
}
}
],
date: {
type: Date,
default: Date.now
}
});
module.exports = Chat = mongoose.model('chat', ChatSchema);
route to add user to chat:
// #route Put api/chats
// #desc Add a user to a chat
// #access Private
router.put('/', [auth,
[
check(
'code',
'Please include the code for the chat')
.not()
.isEmpty(),
check(
'password',
'Please include the password for the chat'
).not()
.isEmpty()
]
],
async (req, res) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
try {
const chat = await Chat.findOne({ code: req.body.code });
//const user = await User.findOne({ user: req.user.id });
if (!chat) {
return res.status(400).json({ msg: 'Invalid Credentials' });
}
// Check if the chat has already been joined by the user
if (chat.users.filter(member => member.user.toString() === req.user.id).length > 0) {
return res.status(400).json({ msg: 'Chat already joined' });
}
//console.log(chat.password);
const isMatch = await bcrypt.compare(req.body.password, chat.password);
if (!isMatch) {
return res.status(400).json({ errors: [{ msg: 'Invalid Credentials' }] });
}
const newUser = {
user: req.user.id,
text: req.user.name
}
chat.users.unshift(newUser);
await chat.save();
res.json(chat.users);
} catch (err) {
console.error(err.message);
res.status(500).send('Server Error');
}
});
User model:
const mongoose = require('mongoose');
const UserSchema = new mongoose.Schema({
name: {
type: String,
required: true
},
email: {
type: String,
required: true,
unique: true
},
password: {
type: String,
required: true
},
date: {
type: Date,
default: Date.now
}
});
module.exports = User = mongoose.model('user', UserSchema);
in this part, it seems you are assigning the user's name to a text property, which I think it should be name not text.
const newUser = {
user: req.user.id,
text: req.user.name
}
The code should be:
const newUser = {
user: req.user.id,
name: req.user.name //Property should be name
}
I hope this works!
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);
}
I have a problem with mongoose, when i use the method findByIdi receive error :
CastError: Cast to ObjectId failed for value "protected" at path "_id"
My _id is valid tested by mongoose.Types.ObjectId.isValid(_id);
I also tested to convert my string _id to ObjectId: mongoose.Types.ObjectId(_id) same error...
My Model is :
var UserSchema = new Schema({
_id: {type:ObjectIdSchema, default: function () { return new ObjectId()} },
email: { type: String, unique: true, required: true },
pseudonyme: { type: String, unique: true, required: true },
password: { type: String, required: true }})
I use node v6.7.0 and mongoose v4.6.5
Thks in advance for your help,
Full Code :
const jwtLogin = new JwtStrategy(jwtOptions, function(payload, done) {
//payload { _id: "58109f58e1bc7e3f28751cdb",email: "antoine.drian#laposte.net",exp: 1477494763,firstName: "antoine",iat: 1477484683,lastName: "drian"}
var isValid = mongoose.Types.ObjectId.isValid(payload._id);
if(!isValid) done(null, false);
var ObjectId = mongoose.Types.ObjectId;
var _id = ObjectId(payload._id);
User.findById( _id , function(err, user) {
if (err) { return done(err, false); }
if (user) {
done(null, user);
} else {
done(null, false);
}
});
});
models/User.js
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var ObjectIdSchema = Schema.ObjectId;
var ObjectId = mongoose.Types.ObjectId;
var bcrypt = require('bcrypt');
// set up a mongoose model
var UserSchema = new Schema({
// _id: {type: Schema.Types.ObjectId, auto: true},
email: { type: String, unique: true, required: true },
pseudonyme: { type: String, unique: true, required: true },
password: { type: String, required: true },
profile: {
firstName: { type: String, required: true },
lastName: { type: String, required: true },
birthdate: { type: Date },
gender: { type: String, enum: ['Male', 'Female'] },
}
}, {
timestamps: true
});
UserSchema.pre('save', function(next) {
var user = this;
if (this.isModified('password') || this.isNew) {
bcrypt.genSalt(10, function(err, salt) {
if (err) {
return next(err);
}
bcrypt.hash(user.password, salt, function(err, hash) {
if (err) {
return next(err);
}
user.password = hash;
next();
});
});
} else {
return next();
}
});
UserSchema.methods.comparePassword = function(passw, cb) {
bcrypt.compare(passw, this.password, function(err, isMatch) {
if (err) {
return cb(err);
}
cb(null, isMatch);
});
};
module.exports = mongoose.model('User', UserSchema);
I found the solution it was just a route conflict between two routes with ExpressJS.
There is no relation between the both.
Thks all people for your help
Try removing the _id from your schema.
var UserSchema = new Schema({
email: { type: String, unique: true, required: true },
pseudonyme: { type: String, unique: true, required: true },
password: { type: String, required: true }
});
Try using the playload_id directly without casting it like below
User.findById( payload._id , function(err, user) {
if (err) { return done(err, false); }
if (user) {
done(null, user);
} else {
done(null, false);
}
});
first don't define _id in your Schema, and change 'isValid', use this instead
var UserSchema = new Schema({
email: { type: String, unique: true, required: true },
pseudonyme: { type: String, unique: true, required: true },
password: { type: String, required: true }
})
and if is there an error keep it as first parameter EX : done(err) otherwise use null, EX: done(null, result)
const jwtLogin = new JwtStrategy(jwtOptions, function(payload, done) {
var _id = mongoose.mongo.ObjectId(payload._id);
User.find( {_id : _id} , function(err, user) {
if (err) { return done(err); }
if (user) {
done(null, user);
} else {
done(new Error('User not found!!!'));
}
});
});