Error in saving mongoose data - javascript

inside my users route.
var User = require('../models/user');
router.route('/verify')
.get( (req, res) => {
res.render('verify');
})
.post( (req, res, next) => {
const {secretToken} = req.body;
const user = User.findOne({'secretToken' : secretToken});
if(!user)
{
req.flash('error_msg', 'No user found');
res.redirect('/users/verify');
return;
}
user.active = true;
user.secretToken = '';
user.save();
req.flash('success_msg','Thank you.You can now login');
res.redirect('/users/login');
});
Thats my model
var userSchema = new mongoose.Schema({
username : {
type : String,
index : true
},
password : {
type : String
},
email : {
type : String,
unique : true
},
password : {
type : String
},
secretToken :
{
type : String
},
active : {
type : Boolean
}
});
var User = module.exports = mongoose.model('User', userSchema);
I set a verify route where a user will post his/her secretToken and if the secretToken match with database then that secretToken will uodated to null and my boolean element active will become true from false.So firstly i am taking the secret token from database,then i am checking it with the one which user giving,if match then i am changing my secretToken and active but problem is i cnt save it.It shows me this error.
TypeError: user.save is not a function
My problem is not in creating user,its about updating the info of already created users.

The problem is here:
const user = User.findOne({'secretToken' : secretToken});
findOne returns a Mongoose promise which you are not awaiting. You probably expect to have the document when you call user.save(), but that's not the case. You just need to await the findOne() and then you should be able to call .save() on that document.

What, I'd suggest you to do is:
var User = require('../models/user');
router.route('/verify')
.get((req, res) => {
res.render('verify');
})
.post((req, res, next) => {
const {
secretToken
} = req.body;
User.findOne({
'secretToken': secretToken
}, (user) => {
if (!user) {
req.flash('error_msg', 'No user found');
res.redirect('/users/verify');
return;
}
user.active = true;
user.secretToken = '';
user.save();
req.flash('success_msg', 'Thank you.You can now login');
res.redirect('/users/login');
});
});
I used a callback here, you could also use promises and then try async await functions using babel, but this works for me.

Related

how to hide my client's password in API response

I am just a beginner at Javascript & MERN. I am trying to create a small social media app, and in my sign up api, I gave a response of the user's info. I couldn't segregate and hide the password.
here is the code
userRouter.post("/signUp", async (req, res) => {
const {name, userName, email, password} = req.body
const existingUser = await userSchema.findOne({email: email})
const SameUserName = await userSchema.findOne({userName: userName})
if (existingUser) {
return res.status(406).send({
message: `sorry, an account with email: ${email} has already been created.`
})
} else if (SameUserName) {
return res.status(406).send({
message: `sorry, user name taken. Try another one...`
})
}
const newUser = new userSchema({
name,
userName,
email,
password
})
console.log(newUser)
try {
await newUser.save()
res.status(201).send({
message: `Account successfully created!`,
user: newUser
})
} catch (err) {
res.send({
message:`Something went wrong`,
})
}
})
So, how can I send the user info without the password?
Following up on the comment I left below, here is what you can do.
Refactoring of your code is must thou.
try {
const userSaved = await newUser.save();
delete userSaved.password // assuming this is the property name
return res.status(201).send({ message: 'Account created successfully', user: userSaved })
}
you could also just:
try {
const userSaved = await newUser.save();
delete userSaved.password // assuming this is the property name
return userSaved;
}
In this case you handle the message and everything on the front-end.
You'll want to implement the toJSON and transform methods on your schema. This will allow you to 'intercept' schema objects as they are created, and as they are serialized and sent to the client.
Here's an example:
Schema:
import { Schema, model } from 'mongoose';
const schema = new Schema(
{
name: {
required: true,
type: String
},
userName: {
required: true,
type: String
},
email: {
required: true,
type: String
},
password: {
required: true,
type: String
}
},
{
// here, we implement the `toJSON` method to serialize the user object sans password, __v;
// we'll also convert the mongo-specific `_id` property to a db-agnostic format
toJSON: {
transform(_, ret) {
ret.id = ret._id;
delete ret.password;
delete ret._id;
delete ret.__v;
}
}
}
);
// this is our user schema, used to initialize new user objects before we persist them in the db
const User = model('User', schema);
userRouter.post('/signUp', async (req, res) => {
// grab the inputs - we do *not* at this time know whether any of these are valid - they must be validated
const { name, userName, email, password } = req.body;
// validate the email format, performing checks for any requirements you wish to enforce
if (!email) {
// error response
}
// now, we check if the email is already in-use
const existingUser = await User.findOne({ email });
if (existingUser) {
return res.status(400).send({
message: `sorry, an account with email: ${email} has already been created.`
});
}
// validate userName format here
if (!userName) {
// error response
}
// notice we don't bother making this query until `existingUser` check has passed
// this way we don't incur needless computation
const sameUserName = await User.findOne({ userName });
if (sameUserName) {
return res.status(400).send({
message: `sorry, user name taken. Try another one...`
});
}
// validate name and password and handle accordingly here
if (!name || ...) {
// error response
}
// assuming all is well, we create a new user with the schema
// think of the schema as a template
const newUser = new User({ name, userName, email, password });
// save the new user
await newUser.save().catch((ex) => {
// error response
});
res.status(201).send({
message: `Account successfully created!`,
user: newUser
});
});
You might also look into express-validator, a middleware that handles much of the request body validation for you.

How Do I Updated a Sequelize Model with An Association?

I'm trying to update a sequelize model (called conversation) whenever I insert a value into its association (called message). The conversation model doesn't automatically update, and I haven't found any useful information in the docs.
const router = require("express").Router();
const { Conversation, Message } = require("../../db/models");
const onlineUsers = require("../../onlineUsers");
// expects {recipientId, text, conversationId } in body (conversationId will be null if no conversation exists yet)
router.post("/", async (req, res, next) => {
try {
if (!req.user) {
return res.sendStatus(401);
}
const senderId = req.user.id;
const { recipientId, text, conversationId, sender } = req.body;
// if we already know conversation id, we can save time and just add it to message and return
if (conversationId) {
const message = await Message.create({ senderId, text, conversationId });
Conversation.update({ })
return res.json({ message, sender });
}
// if we don't have conversation id, find a conversation to make sure it doesn't already exist
let conversation = await Conversation.findConversation(
senderId,
recipientId
);
if (!conversation) {
// create conversation
conversation = await Conversation.create({
user1Id: senderId,
user2Id: recipientId,
});
if (onlineUsers.includes(sender.id)) {
sender.online = true;
}
}
const message = await Message.create({
senderId,
text,
conversationId: conversation.id,
});
res.json({ message, sender });
}
catch (error) {
next(error);
}
});
module.exports = router;
for update you must change or append data into your record
and second argument is your condition for filter users (if you didnt pass argument for condition it means you didnt want to filter conversations then updade all of the records
await conversation.update(
{ title: 'sample title' },
{ where: { : 2 } }
)

nodejs mongoose - how to check items in the database before login

In my project, I've different roles (seller/user/admin)and i want to check the role and redirect to specific page if they are seller for example.
I struggle on how i can check the role in Mongo DB before the login. My login page is basic email-password and submit button.
for my signup all is good, it's use the correct model and post it in the DB.
here are some pieces of my code:
(client model)
userSchema.statics.login = async function (email, password, role) {
const user = await this.findOne({ email });
if (user) {
const auth = await bcrypt.compare(password, user.password);
if (auth) {
return user;
}
throw Error("incorrect password");
}
throw Error("incorrect email");
};
const ClientModel = mongoose.model("client", userSchema, "users");
login controller:
module.exports.clientSignIn = async (req, res) => {
const { email, password } = req.body;
try {
const user = await LoginModel.login(email, password);
const token = createToken(user._id);
res.cookie("jwt", token, { httpOnly: true, maxAge });
res.redirect('/success');
} catch (err) {
console.log(err.message);
}
};
thanks in advance for your help, if you need more info please feel free to ask
Following #EAzevedo 's advice.
i just change my Controller
module.exports.clientSignIn = async (req, res) => {
const { email, password } = req.body;
try {
const user = await LoginModel.login(email, password);
const token = createToken(user._id);
res.cookie("jwt", token, { httpOnly: true, maxAge });
if (user.role == "client") {
res.redirect("/success");
} else if (user.role == "technicien") {
res.redirect("/success-technicien");
} else if (user.role == "superuser") {
res.redirect("/success-admin");
};
} catch (err) {
const errors = signInErrors(err);
res.status(200).json({ errors });
}
};
when you get the user , you should have field for the role ,
then check which role logged in and redirect him to where he needs to be

Forgot password functionality using NodeJs/Knex/Nodemailer and it is not working properly

Note: this is my first time posting, if you have feedback please let me know
Goal: I am building some endpoints that let a user reset their password if they forgot it. Flow would look like this:
User doesn't know password so they click on forgot password.
User types in email and clicks send
User receives email with link to reset password. Clicks on link and is redirected to type in their new password.
They click 'save' and they are redirected to login to sign in with their new password
I am using Insomnia to hit the endpoints for testing.
Things that are working:
When providing an email to reset password, Nodemailer does send out an email.
When updating the password it does show 'password updated' and gives a 200 status.
Bugs:
After trying to log in with that new password, it is not saving to the database. Only the old password will allow you to log back in.
Things I have tried:
I tried changing my user.model to use my findByEmail function and ran into some weird bugs, which then led me down a rabbit hold of issues.
I tried console logging quite a few things to see if I could trace the path.
I tried changing the user.update function but was not able to get it to work.
Here is my code:
Any guidance would be appreciated. If you need to look at any other files please let me know.
Forgot.password.js
const router = require('express').Router();
const crypto = require('crypto')
const User = require('../models/users.model')
const nodemailer = require('nodemailer')
router.post('/forgotpassword', (req, res) => {
let {
email
} = req.body
console.log(req.body)
// if (req.body.email === '') {
// res.status(400).json({ message: 'Email is required'})
// } console.error(req.body.email)
User.findBy({
email
})
.first()
.then(user => {
if (user === null) {
res.status(403).json({
message: 'Email not in db'
})
} else {
const token = crypto.randomBytes(20).toString('hex')
User.update({
resetPasswordToken: token,
resetPasswordExpires: Date.now() + 3600000,
})
const transporter = nodemailer.createTransport({
service: 'gmail',
auth: {
user: `${process.env.EMAIL_USER}`,
pass: `${process.env.EMAIL_PASS}`
}
})
const mailOptions = {
from: `${process.env.EMAIL_USER}`,
to: `${user.email}`,
subject: '[Promoquo] Reset Password Link',
text: 'You are receiving this because you (or someone else) have requested the reset of the password for your account.\n\n' +
'Please click on the following link, or paste this into your browser to complete the process within one hour of receiving it:\n\n' +
`http://localhost:5000/reset/${token}\n\n` +
'If you did not request this, please ignore this email and your password will remain unchanged.\n',
}
transporter.sendMail(mailOptions, (err, res) => {
if (err) {
console.log('ERROR coming from forgot.password js and it sucks', err)
} else {
console.log('here is the res', res)
res.status(200).json({
message: 'recovery email sent hell yes'
})
}
})
}
res.status(200).json({
message: 'Reset password email has been sent WOOHOO 🎉'
})
})
.catch(error => {
res.status(500).json({
message: 'ERROR on last catch forgotpassword.js, likely no user exists',
error
})
console.log(error)
})
})
module.exports = router
Update.password.js
const router = require('express').Router();
const passport = require('passport')
const bcrypt = require('bcrypt')
const User = require('../models/users.model')
const BCRYPT_SALT_ROUNDS = 12
router.put('/updatePasswordViaEmail', (req, res) => {
User.find({
where: {
username: req.body.username,
resetPasswordToken: req.body.resetPasswordToken,
resetPasswordExpires: Date.now() + 3600000,
}
})
.then(user => {
if (user == null) {
console.error('password reset link has expired')
res.status(403).json({ message: 'Password reset link is invalid or has expired' })
} else if (user != null) {
console.log('user exists in db')
bcrypt.hash(req.body.password, BCRYPT_SALT_ROUNDS)
.then(hashedPassword => {
User.update({
password: hashedPassword,
resetPasswordToken: null,
resetPasswordExpires: null,
})
})
.then(() => {
console.log('log for THEN updating password')
res.status(200).json({ message: 'password updated' })
})
} else {
console.error('no user exists in db to update')
res.status(401).json({ message: 'no user exists in db to update'})
}
})
})
module.exports = router
Users.model.js
const db = require('../dbConfig')
module.exports = {
add,
find,
findBy,
findById,
findByEmail,
findByType,
update
};
function find() {
return db('users').select('id', 'username', 'email', 'password');
}
function findBy(filter) {
return db('users').where(filter);
}
async function add(user) {
const [id] = await db('users').insert(user);
return findById(id);
}
function findById(id) {
return db('users').where({ id }).first();
}
function findByEmail(email) {
return db('users').where({ email }).first();
}
function findByType(type) {
return db('users').where({ type }).first();
}
function update(changes, id) {
return db('users').where({ id }).update(changes)
}
20200913211559_users.js (this is the table)
exports.up = function(knex) {
return knex.schema.createTable('users', tbl => {
tbl.increments();
tbl.string('firstname', 30).notNullable();
tbl.string('lastname', 30).notNullable();
tbl.string('username', 30).notNullable()
tbl.string('email', 50).notNullable()
tbl.string('password', 128).notNullable();
tbl.string('type').notNullable();
tbl.boolean('confirmed').defaultTo('false');
tbl.string('resetPasswordToken');
tbl.date('resetPasswordExpires');
})
};
exports.down = function(knex) {
return knex.schema.dropTableIfExists('users')
};
Your User.update() lines aren't running (you either need to return their promises into the chains of promises, or hook into their callbacks). async/await is your friend here to avoid "callback hell."
const user = await User.find({
where: {
username: req.body.username,
resetPasswordToken: req.body.resetPasswordToken,
resetPasswordExpires: Date.now() + 3600000,
}
})
if (!user) { /* ... */ }
const token = crypto.randomBytes(20).toString('hex')
await User.update({ // await here!
resetPasswordToken: token,
resetPasswordExpires: Date.now() + 3600000,
})

cannot compare password with bcrypt compare

Im trying to build a node api for change password,
User must type the currentPassword and the new password
when bcrypt.compare the new currentPassword with the stored on db, i got always false, whatever it's wrong or correct
const changePass = async (req, res, next) => {
//email and password
const CurrentPassword = req.body.currPassword
let password1 = ''+req.body.password1
let password2 = ''+req.body.password2
const hashedPassword = await bcrypt.hash(password1, 10);
let id = "" + req.body.id
User.findById( id )
.then(user => {
bcrypt.compare(CurrentPassword, user.password, (err, data) => {
if (err) throw err
if (data) {
User.findByIdAndUpdate(id, {password : hashedPassword }, {new: false}, (err) => {
if (err) throw err
})
} else {
return res.status(401).json({ msg: "Invalid" })
}
})
})
}
If you want to learn bcrypt I recommend you to visit bcrypt NPM because it will save you too much time later,
in your case I made some modification on your code in order to check for the current password OLD and then compare between the newPassword1 and the confirmation passwordConfirmation
feel free to use console.log('') when you have doubts about anything it will give you a good vision about your code status
const changePassword = async (req, res, next) => {
let id = req.body.nid;
if(id){
console.log('Im here')
const old = req.body.old;
const newP = req.body.newP;
const newP2 = req.body.newP2;
User.findById(id,(err,user)=>{
if(user){
console.log(user)
const hash = user.password;
bcrypt.compare(old,hash,function (err,res){
if(res){
if(newP === newP2){
bcrypt.hash(newP,10, (err,hash)=>{
user.password = hash;
user.save( (err,user) =>{
if(err) return console.error(err);
console.log(user.userName +' your password has been changed');
});
});
};
};
});
}
})
}
}

Categories

Resources