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.
Related
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;
I am trying to query a user by their name field, and I am testing it in insomnia. I have my user schema like so:
const userSchema = mongoose.Schema({
id: { type: String },
name: {
type: String,
required: true,
},
companion: {
type: String,
required: false
},
bio: {
type: String,
required: false
},
email: {
type: String,
required: true,
},
password: {
type: String,
required: true,
min: 5
},
userImage: {
type: String,
required: false,
},
});
And I have my route for /search which runs the getUserBySearch function:
router.get('/search', getUserBySearch)
getUserBySearch logic:
export const getUserBySearch = async (req, res) => {
// tried req.query and req.query.search
const { searchQuery } = req.params
try {
// make the search query not case sensitive
const user = new RegExp(searchQuery, `i`)
//find the user's name using the name field
const userFound = await User.find({name: user})
res.json({data: userFound})
} catch (error) {
res.status(404).json({message: error.message})
}
}
Tested in insomnia under the route: http://localhost:3001/user/search?searchQuery=test
I should only be receiving the users whose name field include test; however I get back 200 response with ALL of the users in the DB. how can I only retrieve the users related to my search query?
In your case http://localhost:3001/user/search?searchQuery=test,
you should be accessing the ?searchQuery=test as req.query.searchQuery:
const { searchQuery } = req.query;
const user = new RegExp(searchQuery, `i`);
Since, you were wrongly reading the query value, you had undefined as a value for user constant, hence getting all users in the DB.
exports.searchRecipe = async(req,res) => {
try {
let searchTerm = req.body.searchTerm;
let user = await user.find( { $text: { $search: searchTerm, $diacriticSensitive: true } });
res.render('search', {user} );
} catch (error) {
res.satus(500).send({message: error.message || "Error Occured" });
}
}
and in the search form try this,#
<form method="POST" action="/search">
<input type="search" name="searchTerm" class="form-control" placeholder="Search..." aria-label="Search">
</form>
try like this ,
In my my mern project when the user do login in the response my server sends him the response of with hash password and reset password token is there any way to fix it
Here is my user model
const mongoose = require("mongoose");
const validator = require("validator");
const bcrypt = require("bcryptjs");
const crypto = require("crypto")
const jwt = require("jsonwebtoken");
const userSchema = new mongoose.Schema({
name: {
type: String,
required: [true, "please Ente the your name"],
},
email: {
type: String,
required: [true, "Please Enter the email"],
unique: [true],
validate: [validator.isEmail, "Please Enter a valid email"],
},
password: {
type: String,
required: true,
minlength: [8, "Passwaord must be more that 8 characters"],
select: false,
},
avatar: {
public_id: {
type: String,
required: true,
},
url: {
type: String,
required: true,
},
},
role: {
type: String,
default: "user",
},
resetPasswordToken: String,
resetPasswordExpire: Date,
createdAt: {
type: Date,
default: Date.now,
},
});
userSchema.pre("save", async function (next) {
if (!this.isModified("password")) {
next();
}
this.password = await bcrypt.hash(this.password, 10);
});
userSchema.methods.comparePassword = async function (password) {
return await bcrypt.compare(password, this.password);
};
userSchema.methods.getJWTToken = function () {
return jwt.sign({ id: this._id }, process.env.JWT_SECRET, {
expiresIn: process.env.JWT_EXPIRE,
});
};
// compare password
// userSchema.methods.comparePassword = async function(enterPass){
// return await bcyrypt.compare(enterPass,this.password)
// }
// Reset Password
userSchema.methods.getResetPasswordToken = function () {
// Generating Token
const resetToken = crypto.randomBytes(20).toString("hex");
// Hashing and adding resetPasswordToken to userSchema
this.resetPasswordToken = crypto
.createHash("sha256")
.update(resetToken)
.digest("hex");
this.resetPasswordExpire = Date.now() + 15 * 60 * 1000;
return resetToken;
};
module.exports = mongoose.model("User", userSchema);
as suggest I have used already select false and also I select reset password token I am getting error when I try to return anything except user
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 = ....
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