How to avoid setting fields to empty strings if value exists - javascript

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();
})

Related

In a MERN and Axios app, using the mongoose populate function, the field populates in the server/terminal but not on the front end

I am developing a MERN app with axios and trying to populate a field(songList) in a model (User) that is referencing the Schema.Types.ObjectId of another schema (Song).
The _id populates when I create a new Song appropriately.
I can see the entire referenced field is populated in the terminal server side with a console.log but the I cannot get the field to populate on the client side.
My Model; I am trying to populate songList.
const { Schema, model } = require('mongoose')
const bcrypt = require('bcrypt');
const userSchema = new Schema({
username: {
type: String,
required: true,
},
password: {
type: String,
required: true,
},
songList: [
{
type: Schema.Types.ObjectId,
ref: 'Song'
}
],
});
userSchema.pre('save', async function (next) {
if (this.isNew || this.isModified('password')) {
const saltRounds = 10;
this.password = await bcrypt.hash(this.password, saltRounds);
}
next();
});
userSchema.methods.isCorrectPassword = async function (password) {
return bcrypt.compare(password, this.password);
};
const User = model("User", userSchema);
module.exports = User;
My server side query, console.log(userSongs) and console.log(user.songList) shows the array of songs appropriately in the terminal:
//login
router.post('/login', async (req, res) => {
const username = req.body.username;
const password = req.body.password;
User.findOne({ username: username })
.populate({path: "songList"})
.exec((err, user) => {
if (err) {
res.status(500).send({ message: err });
return;
}
if (!user) {
res.status(404).json({ message: 'User Not Found' });
}
const passwordIsValid = bcrypt.compareSync(
password,
user.password
);
if (!passwordIsValid) {
return res.status(401).send({ message: "Invalid Password" });
}
let userSongs = [];
for (let i = 0; i < user.songList.length; i++) {
userSongs.push(user.songList[i])
}
const accessToken = sign(
{ username: user.username, _id: user._id },
"importantsecret");
// res.json({
// token: accessToken,
// username: username,
// _id: user._id,
// songList: user.songList
// });
res.status(200).send({
token: accessToken,
username: username,
_id: user._id,
userSongs: userSongs
});
console.log(userSongs)
});
});
The client side request for the user information where I am hoping to see a populated songList with console.log(singleUser):
const [singleUser, setSingleUser] = useState({})
const [userSongs, setUserSongs] = useState([])
useEffect(() => {
Axios.get(`http://localhost:3001/api/user/${id}`).then((response) => {
setSingleUser(response.data)
})
}, [authState])
Client side login request.
const login = () => {
const data = { username: username, password: password };
Axios
.post("http://localhost:3001/api/user/login", data)
.then((response) => {
if (response.data.error) {
console.log(response.data.error)
} else {
localStorage.setItem('accessToken', response.data.token)
setAuthState({
username: response.data.username,
_id: response.data._id,
status: true
});
window.location.replace('/')
}
})
}
Here is where I create a new Song and add it to the user that is logged in.
router.post('/insert', (req, res) => {
const id = req.body.id;
const songTitle = req.body.songTitle;
const artist = req.body.artist;
const album = req.body.album;
Song.create({ songTitle: songTitle, artist: artist, album: album })
.then((song) => {
return User.findOneAndUpdate(
{ _id: id },
{ $addToSet: { songList: song._id } },
{ new: true }
)
})
.then((user) =>
!user
? res.status(404).json({
message: 'Song created, but found no user with that ID',
})
: res.json('Created the song')
)
.catch((err) => {
console.log(err);
res.status(500).json(err)
})
});
Any suggests on how I can get songList to populate using the populate() mongoose function is much appreciated.
Thank you,
Brandon
I've read articles on stack overflow, 'Mongoose 'populate' not populating",
"Mongoose .populate() not working correctly". Medium articles, and the mongoose documentation.
I've tried sending the user songs in the response back as res.json() and res.send(). The field shows up but is not populated.
I've tried being more specific with songList.songTitle and {path: "songList")
All of these show the field populated in the terminal but not on the front side.

select action occurs befor insert postgresql

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.

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,
})

How to use multi Auth - firebase?

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));
};

How To Fix "Unknown error status: Error: The uid must be a non-empty string with at most 128 characters." in Firebase Functions

I'm trying to create a user in my firebase app by passing the data from the UI to a callable function where I
create a user account using email password then
add a display name then create a profile in a user collection then
send a user confirmation email but I get the error
Unknown error status: Error: The uid must be a non-empty string with at most 128 characters.
at new HttpsError (/srv/node_modules/firebase-functions/lib/providers/https.js:102:19)
at admin.auth.createUser.then.then.then.catch.error (/srv/index.js:41:12)
at <anonymous>
const db = admin.firestore();
exports.createUser = functions.https.onCall((data,context)=>{
return admin.auth().createUser({
email: data.email,
password: data.password,
displayName: data.displayName,
}).then(user =>{
return db.doc('users/'+user.uid).set({
email: data.email,
displayName:data.displayName,
type:data.type,
organization:data.organization
});
})
.then(user=>{
let uid = user.uid;
if (data.type === "admin"){
return admin.auth().setCustomUserClaims(uid,{
isAdmin: true,
})
}else{
return admin.auth().setCustomUserClaims(uid,{
isAdmin: false,
})
}
})
.then(user =>{
return user.sendEmailVerification();
})
.catch(error =>{
new functions.https.HttpsError(error);
});
})
and this is my code on my React JS front end
let createUser = functions.httpsCallable('createUser')
createUser({
email: this.state.email,
password: this.state.password,
displayName:this.state.name,
type:this.state.type,
organization:this.state.organization
})
.then(result => {
console.log(result)
})
.catch(error => {
console.log(error)
})
When you do
return db.doc('users/'+user.uid).set({
email: ....});
})
.then(user => { // here, user is undefined})
the value of user (i.e. the fulfillment value, or, in other words, the argument you pass to the first callback function you pass to the then method) is undefined since the set() method returns a "non-null Promise containing void".
You need to save the value of uid in a variable in the previous then(), as shown in the code below.
Note also that, by doing,
.then(user =>{
return user.sendEmailVerification();
})
firstly you get the same problem than above (the value of user is undefined), but, in addition, in the Admin SDK there isn't a sendEmailVerification() method, which is a method of the client JavaScript SDK.
You can use the generateEmailVerificationLink() method of the Admin SDK and send the link by email (from the Cloud Function) to the user, through Sendgrid for example.
const db = admin.firestore();
exports.createUser = functions.https.onCall((data,context)=>{
let userUid;
return admin.auth().createUser({
email: data.email,
password: data.password,
displayName: data.displayName,
}).then(user =>{
userUid = user.uid;
return db.doc('users/'+userUid).set({
email: data.email,
displayName:data.displayName,
type:data.type,
organization:data.organization
});
})
.then(()=>{
if (data.type === "admin"){
return admin.auth().setCustomUserClaims(userUid,{
isAdmin: true,
})
}else{
return admin.auth().setCustomUserClaims(userUid,{
isAdmin: false,
})
}
})
.then(() =>{
//You may use the generateEmailVerificationLink() method, see
//https://firebase.google.com/docs/reference/admin/node/admin.auth.Auth#generateEmailVerificationLink
const actionCodeSettings = ....
return admin.auth()
.generateEmailVerificationLink(data.email, actionCodeSettings)
})
.then(link => {
//The link was successfully generated.
//Send an email to the user through an email service
//See https://github.com/firebase/functions-samples/tree/master/email-confirmation
//or https://stackoverflow.com/questions/50205390/send-transactional-email-with-sendgrid-in-cloud-functions-firebase/50248871
})
.catch(error =>{
throw new functions.https.HttpsError('unknown', error.message);
});
})

Categories

Resources