im dealing with a problem i can't solve. I've got 2 tables - 'users' and 'login'. The 'users' table keep all my user's info and the 'login' table keep the user's emails and hashes:
this is my backend code:
const handleRegister = (req, res, db, bcrypt, saltRounds) => {
const { email, first_name, last_name, password } = req.body;
// creating hash for password
const salt = bcrypt.genSaltSync(saltRounds);
const hash = bcrypt.hashSync(password, salt);
// form validation
if ( !email || !first_name || !last_name || !password) {
return res.status(400).json('incorrect form submission');
}
// updating login and users tables in the database
db.transaction(trx => {
trx.insert({
first_name: first_name,
last_name: last_name,
email: email,
joined: new Date()
})
.into('users')
.returning('email')
.then(loginEmail => {
return trx('login')
.returning('email')
.insert ({
hash: hash,
email: loginEmail[0]
})
.then(userEmail => {
db.select('*').from('users').where('email', userEmail[0])
.then(user => {
userInfo = Object.assign(user[0], {lists: []} , {tasks: []});
res.json({user: userInfo});
})
.catch(err => {res.status(400).json('unable to get user')})
})
})
.then(trx.commit)
.catch(trx.rollback)
})
.catch(err => {
res.status(400).json('unable to register');
console.log(err);
})
At first when i try to add new user through postman everything is ok, but when i try to add another user i got an error: "TypeError: Cannot convert undefined or null to object" because for some reason the db.select in line 28 does not get any result and passes user=[]. The thing is that when i check my database - the user that i just added is there. it looks like its doing the db.select in line 28 before the insert in line 23...
thank you for your help.
Related
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,
})
I have a register screen that contains "username, email, phone number, Password"
and in this case, I use Phone Number Authentication to verify the number so after user verify his number I save his data into firebase DB,
so after that, I navigate hem to login screen! that should contain Email, Password "he registered by them before"
So I don't just compare if his data exist in DB or not,
So it should be used Email/Password Firebase Auth,
But I think it's will take a lot of hits to my Bill or something,
so what you think to achieve these cases because I'm forced to register by Email For Reset Password later?
here is my register Code
signUp = async () => {
const {phoneNumber} = this.state;
this.setState({message: 'code was sent'});
const phoneWithAreaCode = phoneNumber.replace(/^0+/, '+972');
console.log(phoneWithAreaCode);
auth()
.signInWithPhoneNumber(phoneWithAreaCode, true)
.then(confirmResult => {
console.log('confirmResult', confirmResult);
this.setState({confirmResult, message: 'code was sent'});
// this.createUserDatabase();
})
.then(() => {
this.props.navigation.navigate('Confirmation', {
message: this.state.message,
confirmResult: this.state.confirmResult,
createUser: uid => this.createUserDatabase(uid),
phoneWithAreaCode: phoneWithAreaCode,
signInPhoneNumber: phone => auth().signInWithPhoneNumber(phone),
});
});
};
createUserDatabase = uid => {
const {userName, phoneNumber, email} = this.state;
// const uid = auth().currentUser.uid;
const data = {
uid,
name: userName,
email: email,
phoneNumber: phoneNumber,
};
database()
.ref(`users/${uid}`)
.set(data)
.then(() => {
console.log('New poll data sent!');
})
.catch(error => console.log('Error when creating new poll.', error));
};
my idea is very simple, it is get the update value from req.body, but it doenst work properly, the data never change in the mongodb.
already tried the {$set: email, password}
const id = { _id: req.params.id };
const { email, password } = req.body;
let user = await User.findByIdAndUpdate(id, { email, password });
if (!user) {
return res.json({ message: "error"});
}
return res.json({
user: user,
updated: true
})
you passing a object into to id field when all it needs is a string/ObjectId
const { email, password } = req.body;
let user = await User.findByIdAndUpdate(req.params.id, { email, password });
if (!user) {
return res.json({ message: "error"});
}
return res.json({
user: user,
updated: true
})
The Expression :
const id = { _id: req.params.id };
creates an Object named id with a field _id set to the actual id value from the params.
So eventually what you end up doing is passing an entire object instead of an id in the first argument to findByIdAndUpdate which only accepts an id as the first parameter.
If you still want to use the object just replace id with id._id in the method findByIdAndUpdate. Which you would then write as :
let user = await User.findByIdAndUpdate(id._id, { email, password });
I'm trying to create a record on two tables when a user registers.
user.js
const db = require('../database');
const User = db.Model.extend({
tableName: 'login_user',
hasSecurePassword: true,
hasTimestamps: true,
team : () =>{
return this.hasMany('Team', 'owner_id');
}
});
module.exports = User;
team.js
const db = require('../database');
const Team = db.Model.extend({
tableName: 'team_master',
hasTimestamps: true,
user: () => {
return this.belongsTo('User', 'owner_id');
},
});
module.exports = Team;
knex migration file
exports.up = function (knex, Promise) {
return knex.schema.createTable('login_user', t => {
t.increments('id').unsigned().primary();
t.string('email').notNull();
t.string('password_digest').notNull();
t.string('fName').notNull();
t.string('lName').notNull();
t.timestamp('created_at').defaultTo(knex.fn.now())
t.timestamp('updated_at').defaultTo(knex.fn.now())
})
.createTable('team_master', t => {
t.increments('id').unsigned().primary();
t.integer('owner_id').references('id').inTable('login_user');
t.string('teamName').notNull();
t.timestamp('created_at').defaultTo(knex.fn.now())
t.timestamp('updated_at').defaultTo(knex.fn.now())
});
};
exports.down = function (knex, Promise) {
return knex.schema.dropTable('login_user').dropTable('team_master');
};
My insert code looks like the following
const user = new User({
email: req.body.email,
password: req.body.password,
fName: req.body.fName,
lName: req.body.fName,
//teamName: req.body.teamName,
});
user.save().then(() => {
res.send('User Created');
});
So in this case what I want to do is insert teamName into the team_master table with the newly created unique user ID inserted into the owner_id in team_master table.
Can someone point me in the right direction around this? Thank you.
You should be able to use the generated ID from the saved User to populate the Team, like this:
user.save()
.then(user => {
// user.id should be populated with the generated ID
return new Team({
owner_id: user.id,
// set your other team properties
}).save()
})
.then(team => {
// do something with team
})
I'm working on my first node.js application and I need some help.
I use MongoDb as database.
In my application I have created a (sign up) method that reads user input such as email & password and sets the other fields like first-name & last-name to empty strings.
exports.postSignup = (request, response, next) => {
const email = request.body.email;
const password = request.body.password;
const fonfirmPassword = request.body.confirmPassword;
User.findOne({ email: email })
.then(userDoc => {
if (userDoc) {
request.flash('error', 'Email already exists, please pick another!')
return response.redirect('/auth/signup');
}
return bcrypt.hash(password, 12)
.then(hashedPassword => {
const user = new User({
firstName: '',
lastName: '',
email: email,
photoUrl: '',
password: hashedPassword,
cart: { items: [] }
})
return user.save();
})
.then(result => {
response.redirect('/auth/login');
const signup = {
to: email,
from: 'support#company.com',
templateId: keys.SIGNUP_TEMPLATE_ID,
dynamic_template_data: {
subject: 'Signup succeeded successfully!',
},
};
sgMail.send(signup);
})
.catch(err => {
console.log(err);
});
})
.catch(err => {
console.log(err);
});
};
The code above works fine...
After a User has logged in to their account, that user is able to navigate to their profile page and set their first-name and last-name just as shown in the attached image.
enter image description here
So I have created another method that allows a User to set their first-name, last-name and photo-Url
exports.postAddProfile = (request, response, next) => {
const firstName = request.body.firstName;
const lastName = request.body.lastName;
const photoUrl = request.body.photoUrl;
User.findOne({ userId: request.user.userId })
.then(user => {
user.firstName = firstName;
user.lastName = lastName;
user.photoUrl = photoUrl;
return user.save();
})
.then(result => {
console.log('Added Profile Info');
response.redirect('/');
})
.catch(err => {
console.log(err)
});
};
This code also works fine But the issue is if a User sets their first-name, last-name and photo-Url the first time like (Jonas, Jsk and https://photourl.com)
Then the second time if a User only changes the first-name then last-name and photo-Url are again set to empty strings.
How can I avoid that?
Empty strings are falsies in JS, so just check if the response has a value that's not a empty string:
User.findOne({ userId: request.user.userId })
.then(user => {
user.firstName = firstName ? firstName : user.firstName;
user.lastName = lastName ? lastName : user.lastName;
user.photoUrl = photoUrl ? photoUrl : user.photoUrl;
return user.save();
})