Node.js: Updating a document with Mongoose - javascript

I'm trying to update an invitation when the invited user registers. The invitation has an auth property which is a nested object, which itself has a property with the key "used." I'm just trying to explicitly declare the value to be true, and save, using async/await. But it's not updating. Is there a better way to do this?
My function:
exports.invitedSignup = async (req, res, next) =>
{
const { firstName, lastName, company, password, email, companyCode, token } = req.body;
console.log(email);
try
{
const user = await User.findOne({ email });
const invitation = await Invitation.findOne({ email }).sort({ field: 'asc', _id: -1 }).limit(1);
if (user) { return res.status(422).send({ error: "User is already registered" }); };
if (!invitation) { return res.status(422).send({ error: "No invitation on record" }); };
if (token !== invitation.auth.token)
{
return res.status(422).send({ error: "Something has gone wrong, please sign up again" });
}
try
{
invitation.auth.used = true;
const updateInvitation = await invitation.save();
console.log("authorization: " + invitation.auth.used);
} catch (e)
{
return next(e);
}
try
{
const saveUser = new User({
firstName: firstName,
lastName: lastName,
email: req.body.email,
password: password,
company: company,
companyCode: companyCode,
role: 1,
auth: { used: true }
});
const newUser = await saveUser.save();
const { email, firstname, lastname } = newUser;
res.json({ token: tokenForUser(newUser), email, firstName, lastName });
}
catch (e)
{
return next(e);
}
}
catch (e)
{
return next(e);
}
};
The invitation schema:
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const bcrypt = require('bcrypt-nodejs');
//define model
const invitationSchema = new Schema({
email: { type: String, unique: true, lowercase: true, unique: true },
inviter: String,
company: String,
companyCode: String,
created: Date,
auth: {
token: String,
used: Boolean,
expires: Date,
}
});
invitationSchema.pre('save', function (next)
{
const invitation = this;
bcrypt.genSalt(10, (err, salt) =>
{
const tomorrow = new Date();
invitation.created = tomorrow;
tomorrow.setDate(tomorrow.getDate() + 1);
if (err) { return next(err); };
invitation.auth = { token: salt, used: 0, expires: tomorrow };
next();
});
});
//create model class
const ModelClass = mongoose.model('invitation', invitationSchema);
//export model
module.exports = ModelClass;

http://mongoosejs.com/docs/schematypes.html#mixed
person.anything = { x: [3, 4, { y: "changed" }] };
person.markModified('anything');
person.save(); // anything will now get saved

Related

Token is invalid or has expired

My problem is to find the user with the resetPassword in the database and the hashToken does not match or passwordResetExpires the time has expired. How can I fix this?
How this should work find the user and set the password, reset the passwordReset and passwordResetExpires.
authController.js
exports.forgotPassword = catchAsync(async (req, res, next) => {
//1) Get user based on POSTed email
const user = await User.findOne({ email: req.body.email });
if (!user) {
return next(
new AppError('There is no user with email ' + req.body.email, 404)
);
}
//2) Generate the random reset token
const resetToken = user.createPasswordResetToken();
await user.save({ validateBeforeSave: false });
//3) Send it to user's email
const resetURL = `${req.protocol}://${req.get(
'host'
)}/api/v1/users/resetPassword:${resetToken}`;
const message = `Forgot your password? Submit a PATCH request with your new password and passwordConfirm to: ${resetURL}.\nIf you didn't forget your password, please ignore this email!`;
try {
await sendEmail({
email: user.email,
subject: 'You password on Account reset token (valid for 10 min)',
message,
});
res.status(200).json({
status: 'success',
message: 'Token sent to email',
token: resetToken
});
} catch (err) {
user.passwordResetToken = undefined;
user.passwordResetExpires = undefined;
await user.save({ validateBeforeSave: false });
return next(
new AppError(
'There was an error sending the email. Try again later!!!',
500
)
);
}
});
/**
*
* #param {*} req
* #param {*} res
* #param {*} next
*/
exports.resetPassword = catchAsync(async (req, res, next) => {
//!) Get user based on the token
const hashedToken = crypto
.createHash('sha256')
.update(req.params.token)
.digest('hex');
console.log(hashedToken);
const user = await User.findOne({
passwordResetToken: hashedToken,
passwordResetExpires: { $gt: Date.now() },
});
console.log(user);
//2) IF token has not expired, and there is user, set the new password
if (!user) {
return next(new AppError('Token is invalid or has expired', 400));
}
user.password = req.body.password;
user.passwordConfirm = req.body.passwordConfirm;
user.passwordResetToken = undefined;
user.passwordResetExpires = undefined;
await user.save();
//3) Update changedPasswordAt property for the user
//4) Log the user in , send JWT
const token = signToken(user._id);
res.status(200).json({
statusbar: 'success',
token,
});
userModel
/* eslint-disable no-console */
const crypto = require('crypto');
const mongoose = require('mongoose');
const validator = require('validator');
const bcrypt = require('bcrypt');
// const validator = require('validator');
const userSchema = new mongoose.Schema({
name: {
type: String,
required: [true, 'Please tell us your full name'],
trim: true,
},
email: {
type: String,
required: [true, 'Please tell us your email address'],
unique: true,
lowercase: true,
validate: [validator.isEmail, 'Please enter a valid email address'],
},
role: {
type: String,
required: [true, 'A tour must have a role'],
enum: {
values: ['admin', 'user', 'lead-guide', 'guide'],
message: 'Role is either: admin, user, lead-guide or guide',
},
default: 'user',
},
photo: String,
password: {
type: String,
required: [true, 'A use must have a password'],
minlength: 8,
select: false,
},
passwordConfirm: {
type: String,
required: [true, 'A use must have a password'],
validate: {
// This only work on CREATE and SAVE!!!
validator: function (el) {
return el === this.password;
},
message: 'Password is not the same',
},
},
active: {
type: Boolean,
default: true,
},
passwordChangeAt: Date,
passwordResetToken: String,
passwordResetExpires: Date,
});
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 14
this.password = await bcrypt.hash(this.password, 14);
//Delete the passwordConfirm because it only need as a input not as output to the database
this.passwordConfirm = undefined;
next();
});
userSchema.methods.correctPassword = function (
candidatePassword,
userPassword
) {
return bcrypt.compare(candidatePassword, userPassword);
};
userSchema.methods.changePasswordAfter = function (JWTTimestamp) {
if (this.passwordChangeAt) {
const changeTimestamp = parseInt(
this.passwordChangeAt.getTime() / 1000,
10
);
return JWTTimestamp < changeTimestamp;
}
// False means that it is 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;

"message": "Invalid Credentials", "stack": "Error: Invalid Credentials\n \ecommerce\\controller\\userCtrl.js:36:19

Each time i register a user and then tries to login, postman says invalid credentials, i checked the isPasswordMatched several times but i cant solve the issue.
if i got a successfully login, postman is suppose to logout the loginUserctrl function first object before switching to "Invalid credentials"
/* controller/userCtrl.js file */
const User = require("../models/userModel");
const asyncHandler = require("express-async-handler");
const { find } = require("../models/userModel");
const { generateToken } = require("../config/jwtToken");
const createUser = asyncHandler(async(req, res) => {
const email = req.body.email;
const findUser = await User.findOne({email: email });
if (!findUser) {
//Create a new User
const newUser = await User.create(req.body);
res.json(newUser);
}else {
//user Allready Exist
throw new Error("User already exists");
}
});
const loginUserctrl = asyncHandler(async (req, res) => {
const {email, password } = req.body;
// check if user exits or not
const findUser = await User.findOne({ email });
if(findUser && (await findUser.isPasswordMatched(password))) {
res.json({
_id: findUser?._id,
firstname: findUser?.firstname,
lastname: findUser?.lastname,
email: findUser?.email,
mobile: findUser?.mobile,
token: generateToken(findUser?._id)
});
}else{
throw new Error("Invalid Credentials");
}
});
//Get all users
const getAllUser = asyncHandler(async (req, res) =>{
try{
const getUsers = await User.find();
res.json(getUsers);
}catch (error) {
throw new Error(error);
}
});
//Get a single user
const getaUser = asyncHandler(async (req, res) => {
const { id } = req.params;
try{
const getaUser = await User.findById(id);
res.json({getaUser});
} catch (error){
throw new Error(error)
}
});
//update a user
const updatedUser = asyncHandler(async (req, res) => {
const { _id } = req.user;
try {
const updatedUser = await User.findByIdAndUpdate(
_id,
{
firstname: req?.body?.firstname,
lastname: req?.body.lastname,
email: req?.body?.email,
mobile: req?.body?.mobile,
},{
new: true,
})
res.json(updatedUser);
} catch (error){
throw new Error(error);
}
})
//delete a single user
const deleteaUser = asyncHandler(async (req, res) => {
const { id } = req.params;
try{
const deleteaUser = await User.findByIdAndDelete(id);
res.json({
deleteaUser,
})
} catch (error){
throw new Error(error)
}
})
const blockUser = asyncHandler(async(req,res)=>{
const {id} = req.params;
try{
const block = User.findByIdAndUpdate(
id,{
isBlocked: true,
},
{
new: true,
}
);
res.json({
messages: "user Blocked",
})
}catch(error){
throw new Error(error);
}
})
const unblockUser = asyncHandler(async(req,res)=>{
const { id } = req.params;
try{
const unblock = User.findByIdAndUpdate(
id,{
isBlocked: false,
},
{
new: true,
},
);
res.json({
messages: "user unblocked",
})
}catch (error){
throw new Error(error)
}
});
module.exports = {
createUser,loginUserctrl,
getAllUser,getaUser,deleteaUser,
updatedUser,blockUser,unblockUser};
/*Models/userModels.js file */
const mongoose = require('mongoose'); // Erase if already required
const bcrypt = require('bcrypt');
// Declare the Schema of the Mongo model
var userSchema = new mongoose.Schema({
firstname:{
type:String,
required:true,
},
lastname:{
type:String,
required:true,
},
email:{
type:String,
required:true,
unique:true,
},
mobile:{
type:String,
required:true,
unique:true,
},
password:{
type:String,
required:true,
},
role: {
type: String,
default: "user",
},
isBlocked: {
type: Boolean,
default: false,
},
cart: {
type: Array,
default: [],
},
address: [{ type: mongoose.Schema.Types.ObjectId, ref: "Address" }],
wishlist: [{ type: mongoose.Schema.Types.ObjectId, ref: "Product" }],
},
{
timestamps: true,
}
);
userSchema.pre("save", async function (next){
const salt = await bcrypt.genSaltSync(10);
this.password = await bcrypt.hash(this.password,salt);
});
userSchema.methods.isPasswordMatched = async function (enteredPassword){
return await bcrypt.compare(enteredPassword, this.password);
}
//Export the model
module.exports = mongoose.model('User', userSchema);

Mongoose model initial value?

I have been trying to initialize new users with a balance of 0 when they create a new banking account. However, I keep getting an empty array on Postman instead. On submit, "fill in all the fields balance" from the transaction controller is displayed.
I assume the error includes the User model affecting the Transaction model, but I am unsure how to fix this. Do I just need to move this block
balance: {
type: Number,
default: 0,
},
into the User model for the creation of a user? If so, should it be nested in the transactions reference somewhere? How should I keep track of the balance if I need to remove "balance" from the controller.
USER MODEL
const mongoose = require("mongoose");
const bcrypt = require("bcrypt");
const validator = require("validator");
const Schema = mongoose.Schema;
const Transaction = require("./transactionModel");
const userSchema = new Schema({
fname: {
type: String,
required: true,
},
email: {
type: String,
required: true,
unique: true,
},
password: {
type: String,
required: true,
},
transactions: {
// type: Array,
transactions: {
// type: Array,
type: mongoose.ObjectId,
ref: Transaction,
},
},
});
// static signup method
userSchema.statics.signup = async function (fname, email, password) {
// validation
if (!fname || !email || !password) {
throw Error("All fields must be filled");
}
if (!validator.isEmail(email)) {
throw Error("Email not valid");
}
if (!validator.isStrongPassword(password)) {
throw Error(
"Password not strong enough. Must be at least 8 characters and contain: uppercase, lowercase, number, and a special character."
);
}
const exists = await this.findOne({ email });
if (exists) {
throw Error("Email already in use");
}
const salt = await bcrypt.genSalt(10);
const hash = await bcrypt.hash(password, salt);
const user = await this.create({ fname, email, password: hash });
return user;
};
// static login method
userSchema.statics.login = async function (email, password) {
if (!email || !password) {
throw Error("All fields must be filled");
}
const user = await this.findOne({ email });
if (!user) {
throw Error("Incorrect email");
}
const match = await bcrypt.compare(password, user.password);
if (!match) {
throw Error("Incorrect password");
}
return user;
};
module.exports = mongoose.model("User", userSchema);
TRANSACTION MODEL
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const transactionSchema = new Schema(
{
transactionType: {
type: String,
required: true,
},
balance: {
type: Number,
default: 0,
},
amount: {
type: Number,
required: true,
},
user_id: {
type: String,
required: true,
},
},
{ timestamps: true }
);
module.exports = mongoose.model("Transaction", transactionSchema);
new transaction controller
// create new transaction
const createTransaction = async (req, res) => {
const { transactionType, amount, balance } = req.body;
let emptyFields = [];
if (!transactionType) {
emptyFields.push("transactionType");
}
if (!amount) {
emptyFields.push("amount");
}
if (!balance) {
emptyFields.push("balance");
}
if (emptyFields.length > 0) {
return res.status(400).json({
error: "Please fill in all the fields " + emptyFields + " ",
emptyFields,
});
}
I tried removing balance from the transaction controller/ nesting the balance in the User model/ having the default balance be 0 in the transactions model. I can't figure out what or what combination of things needs to change.

Nodejs - TypeError stating that the hashPassword function is not a function, how can I solve it?

how is everything ? I really need your help!
I'm building an API to register authenticated users, with storage in the mongo atlas database (cloud). I'm currently experiencing the following error: TypeError subscription error: User.hashPassword is not a function. I've done several researches, in several questions here on stackoverflow and on other sites, after testing all the solutions the error persists.
my user.model.js file looks like this:
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const bcrypt = require('bcrypt');
const userSchema = new Schema(
{
userName: { type: String, unique: true, required: true },
email: { type: String, required: true, unique: true },
emailToken: { type: String, default: null },
emailTokenExpires: { type: Date, default: null },
active: { type: Boolean, default: false},
password: { type: String, required: true},
resetPasswordToken: { type: String, default: null },
resetPasswordExpires: { type: Date, default: null },
emailToken: {type: String, default: null},
emailTokenExpires: {type: Date, default: null},
},
{
timestamps: {
createdAt: "createdAt",
updatedAt: "updatedAt",
},
}
);
const User = mongoose.model("user", userSchema);
module.exports.hashPassword = async (password) => {
try {
const salt = await bcrypt.genSalt(10); // 10 rounds
return await bcrypt.hash(password, salt);
} catch (error) {
throw new Error("Hashing failed", error);
}
};
module.exports = User;
and my user.controller.js file looks like this:
const Joi = require("joi");
require("dotenv").config();
const { v4: uuid } = require("uuid");
const { sendEmail } = require("./helpers/mailer");
const User = require("./user.model");
//Validate user schema
const userSchema = Joi.object().keys({
email: Joi.string().email({ minDomainSegments: 2 }),
password: Joi.string().required().min(4),
confirmPassword: Joi.string().valid(Joi.ref("password")).required(),
});
exports.Signup = async (req, res) => {
try {
const result = userSchema.validate(req.body);
if (result.error) {
console.log(result.error.message);
return res.json({
error: true,
status: 400,
message: result.error.message,
});
}
//Check if the email has been already registered.
var user = await User.findOne({
email: result.value.email,
});
if (user) {
return res.json({
error: true,
message: "Email is already in use",
});
}
const hashPassword = await User.hashPassword(result.value.password);
const id = uuid(); //Generate unique id for the user.
result.value.userId = id;
//remove the confirmPassword field from the result as we dont need to save this in the db.
delete result.value.confirmPassword;
result.value.password = hashPassword;
let code = Math.floor(100000 + Math.random() * 900000); //Generate random 6 digit code.
let expiry = Date.now() + 60 * 1000 * 15; //Set expiry 15 mins ahead from now
const sendCode = await sendEmail(result.value.email, code);
if (sendCode.error) {
return res.status(500).json({
error: true,
message: "Couldn't send verification email.",
});
}
result.value.emailToken = code;
result.value.emailTokenExpires = new Date(expiry);
const newUser = new User(result.value);
await newUser.save();
return res.status(200).json({
success: true,
message: "Registration Success",
});
} catch (error) {
console.error("signup-error", error);
return res.status(500).json({
error: true,
message: "Cannot Register",
});
}
};
Error displayed in terminal:
Danilo#DANILO-PC D:\Meus Documentos\Área de Trabalho\api-auth-pokestore
$ node app.js
Server started listening on PORT : 5000
Database connection Sucess.
signup-error TypeError: User.hashPassword is not a function
at exports.Signup (D:\Meus Documentos\Área de Trabalho\api-auth-pokestore\src\users\user.controller.js:39:37)
at processTicksAndRejections (internal/process/task_queues.js:94:5)
image terminal
Look at this part of your code:
module.exports.hashPassword = async (password) => { ... };
module.exports = User;
You're setting hashPassword in your exports, then completely replacing your exports with User. You probably wanted to do something like this instead:
User.hashPassword = async (password) => { ... };
module.exports = User;
or move your module.exports.hashPassword = ... so it's after the module.exports = ....

findOneandReplace keeps giving error: "Error: The replacement document must not contain atomic operators."?

I am currently developing a Pokemon Team Builder app with a React frontend and an Express backend with MongoDB for the database.
As far as I can tell my TeamSchema has no such atomic operators? Here is my TeamSchema:
const mongoose = require('mongoose');
const TeamSchema = new mongoose.Schema({
name: {
type: 'String',
required: true,
unique: true,
},
team: [
{
name: { type: String },
types: [{ type: String }],
sprite: { type: String },
},
],
username: {
type: String,
required: true,
},
userId: {
type: String,
required: true,
},
});
const TeamModel = mongoose.model('Team', TeamSchema);
module.exports = TeamModel;
And the error gets thrown in this method when I attempt to call the findOneAndReplace method by finding a team that has a name and userId match.
const replaceTeam = async (req, res) => {
const { teamName: name, filteredTeam: team } = req.body;
const { username, _id: userId } = req.user;
const newTeam = new Team({ name, team, username, userId });
try {
const replacedTeam = await Team.findOneAndReplace({ name, userId }, newTeam);
console.log(replacedTeam);
res.status(200).json({ message: 'Team was successfully overwritten!' });
} catch (err) {
console.log(err);
res.status(500).json({ message: 'An error occurred while updating the team.' });
}
};
This has been a real headscratcher here and I am not sure what is going wrong here. I have only started using mongoose a couple of weeks ago, so I wonder if it's something fundamental I am misunderstanding here.
The Mongoose function findOneAndReplace expects a document object passed in. See the below code.
details.findOneAndReplace(
{ location: "New York" },
{ name: "Sunny", age: 292, location: "Detroit" },
function(err, result) {
if (err) {
res.send(err);
} else {
res.send(result);
}
}
);
Change
const newTeam = new Team({ name, team, username, userId })
to
const newTeam = {name, team, username, userId}
Also as in the other poster's code, add the new: true option to the call as follows by changing
const replacedTeam = await Team.findOneAndReplace({ name, userId }, newTeam);
to
const replacedTeam = await Team.findOneAndReplace({ name, userId }, newTeam, { new: true });
otherwise the original document will be returned into replacedTeam
You can just use findOneAndUpdate and update all the fields with new data. You can do it like this:
const replaceTeam = async (req, res) => {
const { teamName: name, filteredTeam: team } = req.body;
const { username, _id: userId } = req.user;
try {
const replacedTeam = await Team.findOneAndUpdate({ name, userId }, { name, team, username, userId }, {new: true});
console.log(replacedTeam);
res.status(200).json({ message: 'Team was successfully overwritten!' });
} catch (err) {
console.log(err);
res.status(500).json({ message: 'An error occurred while updating the team.' });
}
};

Categories