Mongoose schema methods and "this" updating properties - NodeJS - javascript

In my usersSchema I want to set a hashed password to my hash field. The schema looks like this:
// models/user-model.js
const usersSchema = new Schema({
name: {
type: String,
required: true
},
email: {
type: String,
unique: true,
required: true
},
hash: String,
salt: String
}, { timestamps: true });
usersSchema.methods.setPassword = (password) => {
this.salt = crypto.randomBytes(16).toString('hex');
this.hash = crypto.pbkdf2Sync(password, this.salt, 1000, 64).toString('hex');
};
In my route, I am trying to set a new user with a name, email, and password. Here's the route:
// routes/users.js
router.get('/setup', (req, res) => {
const user = new User();
user.name = 'Jacob';
user.email = 'jacob#gmail.com';
user.setPassword('password');
user.save()
.then((user) => {
const token = user.generateJwt();
res.json({ yourToken: token });
})
.catch((err) => {
res.json(err);
});
});
When I console.log(user) from the route, it gives me the following:
{ name: 'Jacob', email: 'jacob#gmail.com' }
I know that the setPassword method works as far as creating proper hashes. It does not, however, save those hashes to the user object. How do I apply setPassword to the user object that calls it so that it can set the salt and hash properties?

By using the fat arrow notation, you're changing what this is referring to in setPassword, so it's not pointing to the user document anymore.
Try using a regular function declaration:
usersSchema.methods.setPassword = function(password) {
this.salt = crypto.randomBytes(16).toString('hex');
this.hash = crypto.pbkdf2Sync(password, this.salt, 1000, 64).toString('hex');
};

Related

How to insert data into MongoDB collection?

I'm using NodeJS with Mongoose. I've two tables into db.js:
const mongoose = require('mongoose')
const UserSchema = new mongoose.Schema(
{
username: { type: String, required: true, unique: true },
email: { type: String, required: true, unique: true },
password: { type: String, required: true }
},
{ collection: 'users' }
)
const model = mongoose.model('UserSchema', UserSchema)
const AccountSchema = new mongoose.Schema(
{
username: { type: mongoose.Schema.Types.ObjectId, required: true, ref: 'hahaha' },
balance: { type: Number, required: true }
},
{ collection: 'accounts' }
)
module.exports.UserSchema = model
module.exports.AccountSchema = model
As you can see the first collection contains users (username, email, password). The second collection represents a bank account, related to a specific user. So, it has two fields: user (foreign key) and balance ($100, i.e.). First question: is my code correct to accomplish this task?
Second question: how can I insert data into the AccountSchema?
I obviously want to insert data only if the user exists into Userschema. I think that this doesn't work:
const Schema = require('./model/db')
app.post('/api/addaccount', async (req, res) => {
const { username, balance } = req.body
try {
const response = await Schema.AccountSchema.create({
username,
balance
})
console.log('User account successfully: ', response)
res.json({status : "ok"})
} catch (error) {
throw error
}
})
How can I do this?
This won't work. You've to query the User model first to check if any user exists with this username. If yes, you'll continue to store data in the Account model, if not you'll return a response something like user doesn't exist

Mongoose populate replacing ObjectIds with empty array

I've been trying to use the mongoose populate function to connect two models. I can save an object but when trying to retrieve using populate the ObjectIds are just replaced with an empty array.
Many questions seem to have been asked but none have a solution that worked for me
user.js
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var Route = require('./route')
var passportLocalMongoose = require('passport-local-mongoose');
const postSchema = new Schema ({
text: {
type: String,
default: '',
required: true
}
}, {
timestamps: true
});
const UserSchema = new Schema({
firstname: {
type: String
},
posts: [postSchema],
route: [{
type: mongoose.Schema.Types.ObjectId,
ref: 'Route'
}]
}, {
timestamps: true
});
UserSchema.plugin(passportLocalMongoose);
const User = mongoose.model('User', UserSchema);
module.exports = User;
route.js
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
const locationSchema = new Schema ({
id: {
type: Number,
default: 0,
required: true
},
address: {
type: String,
default: '',
required: true
},
lat: {
type: Number,
default: 0,
required: true
},
lng: {
type: Number,
default: 0,
required: true
}
},{
timestamps: true })
const routeSchema = new Schema ({
locations: [locationSchema],
description: {
journey1: {
type: String,
default: '',
required: false
},
journey2: {
type: String,
default: '',
required: false
},
journey3: {
type: String,
default: '',
required: false
},
journey4: {
type: String,
default: '',
required: false
}
}
}, {
timestamps: true
});
module.exports = mongoose.model('Route', routeSchema);
within REST POST end point
User.findOne({_id: req.user._id}, function(err,user) {
if(user) {
var routed = new Route();
routed.locations = req.body.locations;
routed.description = req.body.description;
user.route.push(routed);
user.save()
.then((user) => {
res.statusCode = 200;
res.setHeader('Content-Type', 'application/json')
res.json(user)
}, (err) => next(err))
} else {
console.log("errored")
err = new Error('User ' + req.body.username + ' not found');
err.status = 404;
return next(err);
}
})
within REST GET end point
User.findOne({_id: req.user._id})
.populate('route')
.then((user) => {
if(user){
console.log("user")
console.log(user)
console.log("routes")
console.log(user.route)
res.statusCode = 200;
res.setHeader('Content-Type', 'application/json')
res.json({success: true, routes: user.route});
}
}, (err) => next(err))
.catch((err) => next(err));
If I remove populate I'll get something like
[
new ObjectId("61f053af7ba46267f4893f8f")
new ObjectId("61f053af7ba46267f4893f8f")
new ObjectId("61f053af7ba46267f4893f8f")
]
from the GET end point but adding it back in returns
[].
My understanding is that in 'new Route()' I'm creating a new Route Object with an Id that gets stored in the User model/document(?). Then when I call populate mongoose searches the Route document for those Ids and converts them to the objects I want. The only issue I could think of is that I'm not creating the Route objects correctly and so no object is being stored with that Id which is why an empty array is returned when I come to try swap Ids with Route objects.
Any ideas or are we all just stumbling in the dark ?
Not entirely sure this is the correct method but instead of instantiating a Route object as displayed I used the Route.create(...) method and then pushed that to the route array and now populate works as expected

Can't update values in mongodb database

Username doesn't get updated after running this. I also tried UpdateOne and it didn't work as well. The code was supposed to update an array but I tried updating the username to make it easier to track down the problem.
const mongoose = require('mongoose');
const schema = mongoose.Schema;
const userSchema = new schema({
Username:{
type: String,
required: true
},
Password:{
type: String,
required: true
},
Cart: Array
});
const User = mongoose.model('User', userSchema);
module.exports = User;
.
app.post('/putbox', (req, res) => {
var query = { Username: user };
var newvalues = { $push: { Cart: "Boxing" } };
try{
User.find(query).then((d)=>{
var values = d[0].Cart;
values.push("boxing")
User.findByIdAndUpdate(d._id, {Username: "test1"})
});
} catch(err) {
console.log(err);
}
res.redirect('/boxing');
});
I believe the syntax is not correct. The first element of updateOne searches for matches to update. You are just passing d._id which is not the same as the _id key in your db structure. Try this one
User.updateOne({ _id: d._id }, {Username: "test1"})

Mongoose - creating and saving a document, then saving new document with its id

I have a user schema that looks like this:
const userSchema = new Schema({
email: {
type: String,
required: true,
unique: true,
trim: true
},
password: {
type: String,
required: true
},
role: {
type: String,
default: 'customer',
enum: ['customer', 'farmer', 'admin']
},
user_id: {
type: Schema.Types.ObjectId,
required: true,
unique: true
}
})
Additionally, I have another schema that would be called "customer" and looks something like this:
const customerSchema = new Schema({
company: String,
contact: String,
...etc
})
Now what I'd like to do is on creation of the User document, create and save a new Customer document, then save itd _id to my User document's user_id field.
The post request would look something like this:
// POST - USER REGISTRATION
router.post('/register', function (req, res) {
const { email, password, role } = req.body
const user = new User({ email, password, role,
user_id: new Customer({
company: req.body.company,
contact: req.body.contact,
...etc (rest of data)
})})
user.save(function(err) {
if (err) {
res.status(500)
.send("Failed to register. Please try again.")
}
else {
res.status(200).send("Registered.")
}
})
})
While this seems to create a Customer object (there's an ObjectID under user_id), the customer isn't saved to the database (naturally since .save isn't called).
My original approach here was to have a user_id variable set to null, then have customer create & save itself, then set the user_id variable to the customer._id value, but this didn't work as user.save(func(err)) would have a "null" user_id value.
just try like this
router.post("/register", async function (req, res) {
try {
const { email, password, role } = req.body;
let customer = new Customer({
company: req.body.company,
contact: req.body.contact,
});
let resultCustomer = await customer.save();
const user = new User({
email,
password,
role,
user_id: resultCustomer._id,
});
await user.save();
res.status(200).send("Registered.");
} catch (error) {
res.status(500).send("Failed to register. Please try again.");
}
});

Save doesn't save any data

I'm trying to save some data into a local mongodb database.
My schema looks as follows:
const mongoose = require('mongoose');
const userSchema = mongoose.Schema({
_id: mongoose.Schema.Types.ObjectId,
name: String,
email: String,
passwordHash: String,
registerTimeStamp: { type: Number, default: Date.now() },
usersFollowing: [],
accountStatus: {
isBanned: { type: Boolean, default: false },
reason: { type: String, default: '' }
}
});
module.exports = mongoose.model('User', userSchema);
and the insert Method looks like this:
createUser(name, email, password) {
const passwordHash = "asdf";
const user = new User({
_id: new mongoose.Types.ObjectId(),
name,
email,
passwordHash
});
user.save(console.log("saved"));
}
my problem is, that even though I called the method with the right arguments, and it prints "saved", no data is inserted into the database.
Thanks!
First of all, when you call:
user.save(console.log("saved"));
The console will print "saved" no matter if you get or not an error. So maybe you are getting an error without a proper handler. If you really want to know what is happening with your user instance:
user.save()
.then(() => console.log("saved"))
.catch((error) => console.log(error));
If you want to use callbacks instead of promises:
user.save(function(error) {
if(error) throw error;
console.log("saved");
});
Now, there is an error in your insert method.
Change:
_id: new mongoose.Types.ObjectId(),
With:
_id: new mongoose.Types.ObjectId,
The parenthesis is the problem.
const mongoose = require('mongoose');
const userSchema = mongoose.Schema({
_id: mongoose.Schema.Types.ObjectId,
name: String,
email: String,
passwordHash: String,
registerTimeStamp: { type: Number, default: Date.now() },
usersFollowing: [],
accountStatus: {
isBanned: { type: Boolean, default: false },
reason: { type: String, default: '' }
}
});
const User = mongoose.model('User', userSchema);
module.exports = User;
module.exports.createUser = function (name, email, password) {
const passwordHash = "asdf";
const user = new User({
_id: new mongoose.Types.ObjectId(),
name,
email,
passwordHash
});
User.save(user , callback);
}
The problem is that you are just logging the message, not saving the user in your database.
In order to do that, first you have to export the user model doing something like this:
const UserModel = mongoose.model('User', userSchema)
module.exports = UserModel
Next, in the same file where you have the createUser method, you import the UserModel. Then in your createUser method, you call the .save method but from your model just imported, sending the just created user:
const UserModel = require('../pathtothefile') //Here you specify the path of the user-model file
createUser(name, email, password) {
const passwordHash = "asdf"
const user = new User({
_id: new mongoose.Types.ObjectId(),
name,
email,
passwordHash
})
return UserModel.save(user)
}

Categories

Resources