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);
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;
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.
I have my Userschema
const userSchema = new mongoose.Schema(
username: {...},
},
name: {
},
email: {
type: String,
trim: true,
required: [true, 'Please add an email'],
unique: true,
lowercase: true,
},
profile: {
type: String,
required: true,
},
role: {
type: String,
enum: ['user', 'admin'],
default: 'user',
},
password: {
type: String,
required: [true, 'Please add a password'],
minlength: 8,
select: false,
},
resetPasswordToken: String,
resetPasswordExpire: Date,
createdAt: {
type: Date,
default: Date.now,
},
},
);
Signup and hash methods
userSchema.pre('save', async function (next) {
if (!this.isModified('password')) {
next();
}
const salt = await bcrypt.genSalt(13);
this.password = await bcrypt.hash(this.password, salt);
});
userSchema.methods.getSignedJwtToken = function () {
return jwt.sign({ id: this._id }, process.env.JWT_SECRET, {
expiresIn: process.env.JWT_EXPIRE,
});
};
userSchema.methods.matchPassword = async function (enteredPassword) {
await bcrypt.compare(enteredPassword, this.password);
};
I can signup without any problems,using POSTMAN
Image from MongoDb Cloud
Now I want to login(Added console.log)
boki#gmail.com boki32300
POST /api/v1/auth/login 401 1146.328 ms - 232
Error: Invalid credentials
at /home/milenko/blog_mongo/backend/controllers/auth.js:46:17
Auth.js
exports.login = asyncHandler(async (req, res, next) => {
const { email, password } = req.body;
if (!email || !password) {
return next(new ErrorResponse('Please provide an email and password', 400));
}
console.log(email, password);
const user = await User.findOne({ email }).select('+password');
if (!user) {
return next(new ErrorResponse('Invalid credentials', 401));
}
const isMatch = await user.matchPassword(password);
if (!isMatch) {
return next(new ErrorResponse('Invalid credentials', 401));
}
sendTokenResponse(user, 200, res);
});
isMatch is false.
Why?
I have a database for my login purposes contains loginid, username, password etc. How do I define that my usernames, loginids and passwords are unique in the MongoDB? That means no duplicates are allowed to be created. Here is some code I use:
app.post('/api/register', async function (req, res){
try {
const hashedPassword = await bcrypt.hash(req.body.password, 10);
console.log(hashedPassword);
console.log(await bcrypt.compare('testtest',hashedPassword));
var user = new User({ loginId: req.body.id, firstname: req.body.username, password: hashedPassword });
user.save(function (err, User) {
if (err) return console.error(err);
console.log("Saved successfully");
});
jwt2.sign({user}, 'secrethere', { expiresIn: '15min'}, (err, token) =>{
res.json({
token
});
});
} catch (err) {
res.status(500).send()
console.log(err);
}
});
My user.js:
const mongoose = require('mongoose');
const userSchema = mongoose.Schema({
loginId: String,
firstname: String,
lastname: String,
eMail: String,
password: String,
active: Boolean
});
module.exports = mongoose.model("User", userSchema);
You can use unique: true option in mongoose schema definition.This option creates an unique index on the field.
Making password field unique may not a good idea.
const mongoose = require("mongoose");
const userSchema = mongoose.Schema({
username: {
type: String,
required: true,
unique: true
},
email: {
type: String,
required: true,
unique: true
},
password: {
type: String,
required: true
},
firstname: String,
lastname: String,
active: Boolean
});
module.exports = mongoose.model("User", userSchema);
This will cause an error like this when a duplicate loginId is being tried to insert:
UnhandledPromiseRejectionWarning: MongoError: E11000 duplicate key
error collection: ....
This unique: true option is supposed to create an unique index.
But it does not create, you can manually create using the following script:
db.users.createIndex( { "email": 1 }, { unique: true } );
db.users.createIndex( { "username": 1 }, { unique: true } );
I also refactored your register route like this:
app.post("/api/register", async (req, res) => {
const { username, email, password, firstname, lastname } = req.body;
let user = new User({ username, email, password, firstname, lastname });
try {
user.password = await bcrypt.hash(user.password, 10);
user = await user.save();
const token = jwt.sign(
{
_id: user._id,
username: user.username,
email: user.email,
firstname: user.firstname,
lastname: user.lastname
},
"secrethere",
{ expiresIn: "15min" }
);
res.json({
token
});
} catch (err) {
console.log(err);
res.status(500).send("Something went wrong");
}
});
I've been working on a Nodejs+Express+Mongoose API that handles user registration/login, everything works perfectly when a user signs up, but when the user wants to login it says it can't find the user. Let me show you the code:
module.exports.registerUser = (req, res) => {
var user = new User();
user.email = req.body.email;
user.name = req.body.name;
user.setPassword(req.body.password);
user.save((err) => {
var token;
token = user.generateJwt();
res.status(200);
res.json({
'token': token,
'user': user,
});
});
};
When I register (I'm testing this with Postman) I get this message, showing everything it's OK:
{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE0ODg1MDcwNjUsImlhdCI6MTQ4ODQyMDY2NX0.GD0NehQ1EYEnOKx2OJWALpkHB8u5N_9Zjm1dcuEdl7I",
"user": {
"__v": 0,
"name": "Stack Overflow",
"email": "stackoverflow#stackoverflow.com",
"_id": "58b77f39cf5e8d5f3ffdb4ff"
}
}
The password gets hashed, that's why the JSON doesn't contain it.
And this is the code for user login:
module.exports.loginUser = (req, res) => {
var password = req.body.password;
var email = req.body.email;
User.findOne({'email': email}, (err, user) => {
if (err) {
res.status(500).json(err);
}
if (user) {
if (!user.validPassword(password)) {
res.status(401).send({
message: 'Wrong password.'
});
} else {
token = user.generateJwt();
res.status(200);
res.json({
'token': token,
'user': user
});
}
} else {
res.status(404).send({
message: 'No user was found.'
});
}
});
}
But every time I hit the login button on postman with the same password and email, I get this:
{
"message": "No user was found."
}
And this is the user schema:
'use strict'
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var crypto = require('crypto');
var jwt = require('jsonwebtoken');
var secret = require('../config/secret');
var userSchema = new Schema({
email: {
type: String,
required: true,
unique: true
},
name: {
type: String,
required: true
},
age: {
type: Number,
required: false
},
schoolID: {
type: String,
required: false,
unique: false
},
university: {
type: String,
required: false
},
area: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Area'
},
project: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Project'
},
hash: String,
salt: String
});
userSchema.methods.setPassword = (password) => {
this.salt = crypto.randomBytes(16).toString('hex');
this.hash = crypto.pbkdf2Sync(password, this.salt, 1000, 64, 'sha1').toString('hex');
};
userSchema.methods.validPassword = (password) => {
this.hash = crypto.pbkdf2Sync(password, this.salt, 1000, 64, 'sha1').toString('hex');
return this.hash === hash;
};
userSchema.methods.generateJwt = () => {
var expiry = new Date();
expiry.setDate(expiry.getDate() + 1);
return jwt.sign({
_id: this._id,
email: this.email,
name: this.name,
exp: parseInt(expiry.getTime() / 1000),
}, secret.secret);
};
var User = mongoose.model('User', userSchema);
module.exports = User;
I can't find the solution to this problem, because the way I see it, everything should be working just nice. Any ideas on why this isn't working?
Update:
This is for checking the email is being sent correctly:
{
"message": "No user was found. User: stackoverflow#stackoverflow.com password: stackoverflow"
}