I'm working with routes on node js. I created a user model shown below -
const mongoose = require("mongoose");
const bcrypt = require("bcrypt");
const jwt = require("jsonwebtoken");
const validator = require("validator");
require("dotenv").config();
const userSchema = mongoose.Schema(
{
email: {
type: String,
required: true,
unique: true,
trim: true,
lowercase: true,
validate(value) {
if (!validator.isEmail) {
throw new Error("Invalid Email");
}
},
},
password: {
type: String,
required: true,
trim: true,
},
role: {
type: String,
enum: ["user", "admin"],
default: "user",
},
name: {
type: String,
required: true,
maxlength: 21,
},
phone: {
required: true,
type: Number,
maxlength: 12,
},
},
{ timestamps: true },
);
userSchema.pre("save", async function (next) {
if (user.isModified("password")) {
// hash the password
const salt = await bcrypt.genSalt(10);
const hash = await bcrypt.hash(this.password, salt);
this.password = hash;
}
next();
});
const User = mongoose.model("User", userSchema);
module.exports = {
User,
};
And then I created a file containing user routes shown below -
const express = require("express");
const router = express.Router();
require("dotenv").config();
const { User } = require("../../models/userModel");
router.route("/signup").post(async (req, res) => {
// const { email, password, name, phone } = req.body;
console.log(req.body);
// try {
// // Check if user email exists
// // create user instance and hash password
// const user = new User({
// email: req.body.email,
// password: req.body.password,
// name: req.body.name,
// phone: req.body.phone,
// });
// // generate jwt token
// console.log("user is saving");
// const userDoc = await user.save();
// // send email
// // save....send token with cookie
// res
// .cookie("access-token", "jflsakjusdilfjadslfj32j43lrf")
// .status(200)
// .send(userDoc);
// } catch (error) {
// res
// .status(400)
// .json({ message: "Error while creating user", error: error });
// }
const user = new User({
name: req.body.name,
email: req.body.email,
password: req.body.password,
phone: req.body.phone,
});
user
.save()
.then((doc) => {
console.log("User saved");
res.send(doc);
})
.catch((err) => {
console.log(err);
});
});
module.exports = router;
But don't know why I'm getting this error -
ReferenceError: user is not defined
at model.<anonymous> (D:\CovidHelpers\CovidHelpers\server\models\userModel.js:46:3)
at callMiddlewareFunction (D:\CovidHelpers\CovidHelpers\node_modules\kareem\index.js:483:23)
at model.next (D:\CovidHelpers\CovidHelpers\node_modules\kareem\index.js:58:7)
at _next (D:\CovidHelpers\CovidHelpers\node_modules\kareem\index.js:107:10)
at D:\CovidHelpers\CovidHelpers\node_modules\kareem\index.js:508:38
at processTicksAndRejections (internal/process/task_queues.js:75:11)
I have just created a new project in mongodb, gave database and network access and it's connecting successfully but also getting this error
I have done this before also and it was working fine but don't know why am I getting this now :(
Any help is appreciated
save is document middleware and in document middleware functions, this refers to the document. So in your case, I believe it should be this.isModified("password") instead of user.isModified("password").
You can delete userSchema.pre() middleware and transfer the password hashing logic inside the router. Also you can simplify your router code like this:
router.route("/signup").post(async (req, res) => {
try {
const salt = await bcrypt.genSalt(10);
const hash = await bcrypt.hash(req.body.password, salt);
req.body.password = hash;
let user = await User.create(req.body)
res.status(200).json(user)
} catch (error) {
res.status(400).json({ error: error });
}
});
RECOMMENDATION:
I would recommend you to try the great Mongoose plugin called passport-local-mongoose that will do this for you out of the box, and it will also give you some nice authentication features if you are using passport for authentication.
Package: https://www.npmjs.com/package/passport-local-mongoose
You don't actually get access to the document, in the mongoose's pre('save') hook.
For your usecase, you can do the hasing before you save the user.
Related
I'm learning the MERN stack and trying to create an authentication, but now I have a problem, whenever I'm trying to register, I have an error 'TypeError: User.create is not a function'.
I think that I have a problem with user model or export. Please help
INDEX.JS
const express = require("express");
const mongoose = require("mongoose");
const cors = require("cors");
const dotenv = require("dotenv");
const app = express();
const User = require("./models/User");
dotenv.config({ path: "./.env" });
app.use(express.json());
app.use(cors());
mongoose.connect(process.env.MBD_CONNECT, { useNewUrlParser: true }, (err) => {
if (err) return console.error(err);
console.log("Connected to MongoDB");
});
app.post("/api/registr", async (req, res) => {
console.log(req.body);
try {
const user = await User.create({
firstName: req.body.firstName,
lastName: req.body.lastName,
email: req.body.email,
password: req.body.password,
});
res.json({ status: "ok" });
} catch (err) {
console.log(err);
res.json({ status: "error", error: "Duplicate email" });
}
});
app.post("/api/login", async (req, res) => {
const user = await User.findOne({
email: req.body.email,
password: req.body.password,
});
if (user) {
return res.json({ status: "ok", user: true });
} else {
return res.json({ status: "error", user: false });
}
});
app.listen(3001, () => {
console.log("SERVER RUNS PERFECTLY!");
});
USER.JS (MODEL)
const mongoose = require("mongoose");
const User = new mongoose.Schema({
firstName: { type: String, required: true },
lastName: { type: String, required: true },
email: { type: String, required: true, unique: true },
password: { type: String, required: true },
});
const model = mongoose.model("UserData", User);
module.exports = User;
You're exporting the schema, not the model. create is a method of mongoose Model class, see document here.
const model = mongoose.model("UserData", User);
module.exports = User; // <------ problem here
It should be:
const model = mongoose.model("UserData", User);
module.exports = model;
Your model file please update with the following code snip
const mongoose = require("mongoose");
const User = new mongoose.Schema({
firstName: { type: String, required: true },
lastName: { type: String, required: true },
email: { type: String, required: true, unique: true },
password: { type: String, required: true },
}, { collection : 'UserData'});
const model = mongoose.model("UserData", User);
module.exports = User;
Created MongoDb User Schema
MongoDb User
const mongoose = require("mongoose"); // Import mongoose
const UserSchema = new mongoose.Schema( // Create User Schema
{
username: { type: String, required: true, unique: true },
email: { type: String, required: true, unique: true },
password: { type: String, required: true },
isAdmin: {
type: Boolean,
default: false,
},
},
{ timestamps: true } // Add timestamps
);
module.exports = mongoose.model("User", UserSchema); // Export User Model
Used express.js Routes files in user.js
user.js
const User = require("../models/User"); // Import User Model Schema
const {
verifyTokenAndAuthorization
} = require("./verifyToken"); // Import verifyTokenAndAuthorization
const router = require("express").Router(); // Import express Router
//UPDATE
router.put("/:id", verifyTokenAndAuthorization, async (req, res) => {
if (req.body.password) {
req.body.password = CryptoJS.AES.encrypt( // Encrypt password
req.body.password, // Password
process.env.PASS_SEC // Encrypt password with secret key
).toString();
}
try {
const updatedUser = await User.findByIdAndUpdate( // Find user by id and update
req.params.id, // Find user by id
{
$set: req.body, // Set user data
},
{ new: true } // Return updated user
);
res.status(200).json(updatedUser); // Return user
} catch (err) { // Catch error
res.status(500).json(err); // Return error
}
});
module.exports = router;
Routes files in auth.js added access token
Access Token
const router = require('express').Router(); // import express
const User = require('../models/User'); // import user model
const CryptoJS = require('crypto-js'); // import crypto-js
const jwt = require('jsonwebtoken'); // import jsonwebtoken
//REGİSTER
router.post('/register', async (req, res) => {
const newUser = new User({
username: req.body.username, // username
email: req.body.email, // req.body.email is the same as req.body.email
password: CryptoJS.AES.encrypt(req.body.password,process.env.PAS_SEC).toString(), // encrypt password
}) // create new user
try{
const savedUser = await newUser.save(); // save user
res.status(201).json(savedUser); // send user
} catch(err){
res.status(500).json({message: err}); // send error
}
});
//LOGIN
router.post("/login", async (req, res) => {
try{
const user = await User.findOne({username: req.body.username}); // find user
if(!user){ // if user is not found
return res.status(400).json({message: "User not found"}); // send error
}
const hashedPassword = CryptoJS.AES.decrypt(user.password,process.env.PAS_SEC); // decrypt password
const Originalpassword = hashedPassword.toString(CryptoJS.enc.Utf8); // convert to string
const {password,...others} = user._doc; // others is the user data without password // Güvenlik için password olmadan kullanıcıyı aktardık ve ._doc ise mongodb datamızı document içinden gösteriyor
const accessToken = jwt.sign({ // create access token
userId: user._id, // user id
isAdmin: user.isAdmin // isAdmin
},
process.env.JWT_SEC, // secret key
{expiresIn: "24h"} // expire time
);
if(Originalpassword !== req.body.password){ // if password is not correct
return res.status(401).json({message: "Incorrect password"}); // send error
} else{
res.status(200).json({...others,accessToken}); // send success
}
}catch(err){ // if error
res.status(500).json({message: err}); // send error
}
});
module.exports = router; // export router
Routes files in verifyToken.js
VerifyToken.js
const jwt = require("jsonwebtoken"); // Import jsonwebtoken
const verifyToken = (req, res, next) => { // Verify token
const authHeader = req.headers.token; // Get token from header
if (authHeader) { // If token exists
const token = authHeader.split(" ")[1]; // Get token from header
jwt.verify(token, process.env.JWT_SEC, (err, user) => { // Verify token
if (err) res.status(403).json("Token is not valid!"); // Return error
req.user = user; // Set user
next(); // Call next middleware
});
} else {
return res.status(401).json("You are not authenticated!"); // Return error
}
};
const verifyTokenAndAuthorization = (req, res, next) => { // Verify token and authorization
verifyToken(req, res, () => {
if (req.user.id === req.params.id || req.user.isAdmin) { // If user id is equal to id from url or user is admin
next(); // Call next middleware
} else {
res.status(403).json("You are not alowed to do that!"); // Return error
}
});
};
module.exports = {
verifyToken, // Export verifyToken
verifyTokenAndAuthorization, // Export verifyTokenAndAuthorization
};
I used Postman. All the queries are working, but when I want to update the user, postman also gives an error. How can ı fix this error is : Cannot read properties of undefined (reading 'id') error. i cant see where i went wrong pls hep mee
Postman Error is
You have to send the JWT token that you get in the response of your login call to the request to update the user.
I want to use mongoose discriminator for my project to create a collection of users in which there is a document of owner which I want to implement using discriminators. But I am getting an error of
throw new Error('The 2nd parameter to mongoose.model() should be a ' +
^
Error: The 2nd parameter to mongoose.model() should be a schema or a POJO
at Mongoose.model (D:\Github\Food-Delivery-Website\node_modules\mongoose\lib\index.js:473:11)
at Object. (D:\Github\Food-Delivery-Website\models\Owner.js:21:27)
Code is given below:
// This file is models/User.js
const mongoose = require('mongoose');
const { Schema } = mongoose;
const options = { discriminatorKey: 'kind' };
const UserSchema = new Schema(
{
userName: {
type: String,
required: true,
unique: true,
},
restOwned: {
// type: [Schema.Types.ObjectId],
type: Number,
},
},
options,
);
module.exports = mongoose.model('User', UserSchema);
Below is the next file
// This file is models/Owner.js
const mongoose = require('mongoose');
const { Schema } = mongoose;
const User = require('./User');
const OwnerSchema = User.discriminator(
'Owner',
new Schema({
isOwner: {
type: Boolean,
required: true,
},
restName: {
type: String,
required: true,
},
}),
);
module.exports = mongoose.model('Owner', OwnerSchema);
Then I import these two files in userController.js
//This file is controllers/userController.js
const User = require('../models/User');
const Owner = require('../models/Owner');
exports.addUser = async (req, res) => {
try {
const newUser = new User({
userName: req.body.userName,
restOwned: req.body.restOwned,
});
const user = await newUser.save();
res.status(201).json({
status: 'Success',
user,
});
} catch (err) {
res.status(500).json({
status: 'failed',
message: 'Server Error: Failed Storing the Data.',
err,
});
}
};
exports.addOwner = async (req, res) => {
try {
const newOwner = new Owner({
isOwner: req.body.isOwner,
restName: req.body.restName,
});
const owner = await newOwner.save();
res.status(201).json({
status: 'Success',
owner,
});
} catch (err) {
res.status(500).json({
status: 'failed',
message: 'Server Error: Failed Storing the Data.',
err,
});
}
};
What am I doing wrong here?
enter image description here
The Model.discriminator() method returns a Model.
So you can directly export the discriminator and use it as the model
// This file is models/Owner.js
const mongoose = require('mongoose');
const { Schema } = mongoose;
const User = require('./User');
//Directly export the discriminator and use it as the model
module.exports = User.discriminator(
'Owner',
new Schema({
isOwner: {
type: Boolean,
required: true,
},
restName: {
type: String,
required: true,
},
}),
);
//module.exports = mongoose.model('Owner', OwnerSchema);
I am going through the full stack developer certification and creating an authentication app from devChallenges.io and I want the user to able to edit their profile and pass in the new profile image as well as their name, bio, phone number and password in the form data however, I get the error Missing required parameter - public_id. Here is my code below.
// #route POST /profile/edit/:id
// #desc edit profile
// #access Private
app.put('/profile/edit/:id', upload.single('image'), auth, async (req, res) => {
const { name, bio, phone, password } = req.body;
try {
let user = await AuthUser.findById(req.params.id);
// Delete image from cloudinary
await cloudinary.uploader.destroy(user.cloudinary_id);
// Upload image to cloudinary
let result;
if (req.file) {
result = await cloudinary.uploader.upload(req.file.path);
}
const data = {
name: name || user.name,
avatar: result.secure_url || user.avatar,
bio: bio || user.bio,
phone: phone || user.phone,
password: password || user.password,
cloudinary_id: result.public_id || user.cloudinary_id,
};
// Encrypt password
const salt = await bcrypt.genSalt(10);
data.password = await bcrypt.hash(password, salt);
// Update
user = await User.findByIdAndUpdate(req.params.id, data, { new: true });
return res.json(user);
} catch (err) {
console.error(err.message);
res.status(500).send('Server error');
}
});
models/user.js
const mongoose = require('mongoose');
const UserSchema = new mongoose.Schema({
name: {
type: String,
default: '',
},
email: {
type: String,
unique: true,
required: true,
},
password: {
type: String,
required: true,
},
avatar: {
type: String,
},
bio: {
type: String,
default: '',
},
phone: {
type: String,
default: '',
},
cloudinary_id: {
type: String,
},
});
module.exports = AuthUser = mongoose.model('AuthUser', UserSchema);
I am building a Node, Express-based API for user authentication. I am using mongo to store the data. Using postman, I submitted a /post request by passing in the following object
{
"username": "abc",
"password": "abc123",
"email": "abc#ghi.com"
}
under req.body.
This is how my /post function looks in the code:
//create one user
router.post('/createUser', async (req, res) => {
if (!req.body.email || !req.body.password || !req.body.username) {
return res.status(400).send({
message: "No username/password/email specified"
});
}
const newUser = new User({
email: req.body.email,
username: req.body.username,
password: req.body.password
});
await User.create(newUser, (err, data) => {
//res.send(data);
if(err) {
console.log(err);
res.status(500).send('Error creating user');
}
});
});
User.create() method calls .save() method under the covers. I have a pre-condition on saving to encrypt passwords. On running the post, I get an error that says UnhandledPromiseRejectionWarning: Error: data and salt arguments required
I did some console logging and noticed that this is happening because user.password is coming in as undefined. So it looks like my request is not going through properly from the postman.
Edit:
Here is the schema:
const userSchema = new mongoose.Schema({
id: {
type: Number
},
email: {
type: String,
unique: true,
required: true,
},
username: {
type: String,
unique: true,
required: true
},
password: {
type: String,
required: true
},
});
userSchema.pre('save', (next) => {
const user = this;
console.log(user.password);
bcrypt.hash(user.password, 10, (err, hash) => {
if (err) {
next(err);
} else {
user.password = hash;
next();
}
});
});
Can someone please help me understand what's wrong?
You cannot use arrow function in .pre hooks because arrow function does not bind "this". "this" is supposed to refer to each individual user that about to be saved. however if you use "this" inside the arrow function, it will point to the global object. run this code console.log(this) you will see. use arrow functions for standalone functions. in your case, you are creating a method that part of the object so you should avoid using arrow function
I do not use .pre, because some mongoose queries bypass mongoose middleware, so u need to do extra work. so instead I hash the password inside the router, so everything related to user will be in the same place. single source of truth
const bcrypt = require("bcrypt");
router.post('/createUser', async (req, res) => {
if (!req.body.email || !req.body.password || !req.body.username) {
return res.status(400).send({
message: "No username/password/email specified"
});
}
const newUser = new User({
email: req.body.email,
username: req.body.username,
password: req.body.password
});
//we created newUser and now we have to hash the password
const salt = await bcrypt.genSalt(10);
newUser.password = await bcrypt.hash(newUser.password, salt);
await newUser.save();
res.status(201).send(newUser)
//201 code success for something is created
});
here is the list of http status codes:
https://httpstatuses.com/
The password from postman is getting received on your NodeJS code.
In your code:
const newUser = new User({
email: req.body.email,
username: req.body.username,
password: req.body.password
});
when you do this, your expected output from newUser changes.
so when your code reaches here...
const user = this;
console.log(user.password);
Instead of logging user.password
try logging user itself like...
console.log(user)
and see if
"(const user=this)" is giving what you expected.
signUp: (req, res, next) => {
bcrypt.genSalt(10, (err, salt) => {
bcrypt.hash(req.body.password, salt, (err, hashedPass) => {
let insertquery = {
'_id': new mongoose.Types.ObjectId(),
'username': req.body.username,
'email': req.body.password,
'salt': salt,
'password': hashedPass
};
user.create(insertquery, function (err, item) {
});
});
});
}