Mongoose FindOneandUpdate - Only update Specific fields leave others with the previous values - reactjs

I would like to update just the fields that the user chose to update, but right now if I don't fill all the inputs, they became null in the data base.
this is the back end
router.put('/user/:id', async (req, res) => {
let id = req.params.id
const salt = await bcrypt.genSalt(10)
const hashPassword = await bcrypt.hash(req.body.password, salt)
const emailExist = await User.findOne({ email: req.body.email })
if (emailExist) {
return res.status(400).send('Email already exists')
}
//creat user
const update = {
name: req.body.name,
lastName: req.body.lastName,
phone: req.body.phone,
email: req.body.email,
bio: req.body.bio,
password: hashPassword,
role: "basic"
}
User.findByIdAndUpdate(id, { $set: update }, { new: true }, (error, userObj) => {
if (error) {
res.status(400).send(err)
} else {
res.send('user updated')
}
})
})
Front end (react)
const updateUser = async (event) => {
event.preventDefault()
const response = await axios.put(`/userinfo/user/${localStorage.getItem('id')}`, {
name: name,
lastName: lastName,
phone: phone,
email: email,
password: password,
confPassword: confPassword,
bio: bio,
})
history.push('/home-login')
const reload = window.location.reload()
}
Thank you

Try handy methods of lodash NPM, install it and import in your node.js file,
Pick update fields from body:
let update = _.pick(req.body, ["name", "lastName", "phone", "email", "bio"]);
Remove undefined, null and "" empty fields:
update = _.pickBy(update, _.identity);
Merge other fields:
update = _.merge(update, {
password: hashPassword,
role: "basic"
});
Use in query:
User.findByIdAndUpdate(id, { $set: update }, { new: true }, (error, userObj) => {
if (error) res.status(400).send(err)
else res.send('user updated')
})

Related

Defining Role in backend MERN

How to define role to Admin, Customer, Worker and Log in with different roles in frontend ? Im new in coding.
For authentication, I'm using JWT. Now I want to add roles and rules for admin, worker and customer.
1 -> Permissions can be assigned to a role ( Create, Update, delete etc...).
As a result, a user can have one or more roles. A user can use APIs for which he has rights, such as creating data, deleting data, updating data, and so on.
Here is the user schema:
UserSchema.model
const mongoose = require("mongoose");
const bcrypt = require("bcryptjs");
const userSchema = mongoose.Schema(
{
role: {
type: String,
enum: ["admin", "customer", "worker"],
},
firstname: {
type: String,
required: true,
},
lastname: {
type: String,
required: true,
},
email: {
type: String,
required: true,
unique: true,
},
password: {
type: String,
required: true,
},
role: {
type: Boolean,
// required: true,
default: false,
enum: ["admin", "customer", "worker"],
},
pic: {
type: String,
// required: true,
default:
"https://icon-library.com/images/anonymous-avatar-icon/anonymous-avatar-icon-25.jpg",
},
},
{
timestamps: true,
}
);
userSchema.methods.matchPassword = async function (enteredPassword) {
return await bcrypt.compare(enteredPassword, this.password);
};
// will encrypt password everytime its saved
userSchema.pre("save", async function (next) {
if (!this.isModified("password")) {
next();
}
const salt = await bcrypt.genSalt(10);
this.password = await bcrypt.hash(this.password, salt);
});
const User = mongoose.model("User", userSchema);
module.exports = User;
UserController
const asyncHandler = require("express-async-handler");
const User = require("../models/user.model");
const generateToken = require("../utility/token");
const authUser = asyncHandler(async (req, res) => {
const { email, password } = req.body;
const user = await User.findOne({ email });
if (user && (await user.matchPassword(password))) {
res.json({
_id: user._id,
firstname: user.firstname,
lastname: user.lastname,
email: user.email,
isAdmin: user.isAdmin,
pic: user.pic,
token: generateToken(user._id),
});
} else {
res.status(401);
throw new Error("Invalid Email or Password");
}
});
const registerUser = asyncHandler(async (req, res) => {
const { firstname, lastname, email, password, pic } = req.body;
try {
const userExists = await User.findOne({ email });
if (userExists) {
return res.status(400).json({ error: "User already exist." });
}
const user = await User.create({
firstname,
lastname,
email,
password,
pic,
});
res.status(201).json({
_id: user._id,
firstname: user.firstname,
lastname: user.lastname,
email: user.email,
isAdmin: user.isAdmin,
pic: user.pic,
token: generateToken(user._id),
});
} catch (error) {
res.status(500).json({ error: error.message });
}
});
const updateUserProfile = asyncHandler(async (req, res) => {
const user = await User.findById(req.user._id);
if (user) {
user.firstname = req.body.firstname || user.firstname;
user.lastname = req.body.lastname || user.lastname;
user.email = req.body.email || user.email;
user.pic = req.body.pic || user.pic;
if (req.body.password) {
user.password = req.body.password;
}
const updatedUser = await user.save();
res.json({
_id: updatedUser._id,
firstname: updatedUser.firstname,
lastname: updatedUser.lastname,
email: updatedUser.email,
pic: updatedUser.pic,
isAdmin: updatedUser.isAdmin,
token: generateToken(updatedUser._id),
});
} else {
res.status(404);
throw new Error("User Not Found");
}
});
module.exports = { authUser, updateUserProfile, registerUser };
UserSchema.model
const mongoose = require("mongoose");
const bcrypt = require("bcryptjs");
const userSchema = mongoose.Schema(
{
role: {
type: String,
enum: ["admin", "customer", "worker"],
},
firstname: {
type: String,
required: true,
},
lastname: {
type: String,
required: true,
},
email: {
type: String,
required: true,
unique: true,
},
password: {
type: String,
required: true,
},
role: {
type: Boolean,
// required: true,
default: false,
enum: ["admin", "customer", "worker"],
},
pic: {
type: String,
// required: true,
default:
"https://icon-library.com/images/anonymous-avatar-icon/anonymous-avatar-icon-25.jpg",
},
},
{
timestamps: true,
}
);
userSchema.methods.matchPassword = async function (enteredPassword) {
return await bcrypt.compare(enteredPassword, this.password);
};
// will encrypt password everytime its saved
userSchema.pre("save", async function (next) {
if (!this.isModified("password")) {
next();
}
const salt = await bcrypt.genSalt(10);
this.password = await bcrypt.hash(this.password, salt);
});
const User = mongoose.model("User", userSchema);
module.exports = User;
UserController
const asyncHandler = require("express-async-handler");
const User = require("../models/user.model");
const generateToken = require("../utility/token");
const authUser = asyncHandler(async (req, res) => {
const { email, password } = req.body;
const user = await User.findOne({ email });
if (user && (await user.matchPassword(password))) {
res.json({
_id: user._id,
firstname: user.firstname,
lastname: user.lastname,
email: user.email,
isAdmin: user.isAdmin,
pic: user.pic,
token: generateToken(user._id),
});
} else {
res.status(401);
throw new Error("Invalid Email or Password");
}
});
const registerUser = asyncHandler(async (req, res) => {
const { firstname, lastname, email, password, pic } = req.body;
try {
const userExists = await User.findOne({ email });
if (userExists) {
return res.status(400).json({ error: "User already exist." });
}
const user = await User.create({
firstname,
lastname,
email,
password,
pic,
});
res.status(201).json({
_id: user._id,
firstname: user.firstname,
lastname: user.lastname,
email: user.email,
isAdmin: user.isAdmin,
pic: user.pic,
token: generateToken(user._id),
});
} catch (error) {
res.status(500).json({ error: error.message });
}
});
const updateUserProfile = asyncHandler(async (req, res) => {
const user = await User.findById(req.user._id);
if (user) {
user.firstname = req.body.firstname || user.firstname;
user.lastname = req.body.lastname || user.lastname;
user.email = req.body.email || user.email;
user.pic = req.body.pic || user.pic;
if (req.body.password) {
user.password = req.body.password;
}
const updatedUser = await user.save();
res.json({
_id: updatedUser._id,
firstname: updatedUser.firstname,
lastname: updatedUser.lastname,
email: updatedUser.email,
pic: updatedUser.pic,
isAdmin: updatedUser.isAdmin,
token: generateToken(updatedUser._id),
});
} else {
res.status(404);
throw new Error("User Not Found");
}
});
module.exports = { authUser, updateUserProfile, registerUser };

State don't update after login by google in react native

i hope you are doing well.
I have a few problem when i try to log in with Google in react native.
I have a global state for user :
const initialState = {
userId: null,
token: null,
email: "",
firstName: "",
lastName: "",
nom: "",
dateOfBirth: null,
roles: [],
photoUrl: "",
phoneNumber: "",
favorites: [],
};
With Login, it work well and my global state is update. The logic :
export const login = (email, password) => {
return async (dispatch) => {
const response = await auth.signInWithEmailAndPassword(email, password).then().catch((error) => {
Alert.alert("L'email ou le mot de passe est invalide");
throw new Error()
} );
if (response && response.user.uid === null) {
throw new Error("Something went wrong!");
}
const user = await db.collection("Users").doc(response.user.uid).get();
const expirationTime = (await response.user.getIdTokenResult())
.expirationTime;
const newDate = new Date(expirationTime).getTime();
const token = (await response.user.getIdTokenResult()).token;
await dispatch(authenticate(response.user.uid, token, newDate));
await dispatch(
addUserInfos(
user.get("email"),
user.get("username"),
user.get("firstName"),
user.get("lastName"),
user.get("dateOfBirth")?.toDate(),
user.get("roles"),
user.get("photoUrl"),
user.get("phoneNumber"),
user.get("favorites"),
)
);
const expirationDate = new Date(new Date().getTime() + newDate);
saveDataToStorage(token, response.user.uid, expirationDate);
};
};
But when i want to connect with Google. The app connect me but my global state only contain value userId and token. I need the reload the app for set my global state. However the 2 functions use the same logic.
export const addUserOnFirestore = (id, email, firstName, lastName, photoUrl, response, navigation) => {
return async (dispatch) => {
let isNeedOnboarding = false;
const user = await db
.collection('Users')
.doc(id)
.get();
// Create user in firestore in it not exist
if (!user.exists) {
isNeedOnboarding = true;
console.log('onboard : ', isNeedOnboarding);
await db.collection('Users').doc(id).set({
email: email,
username: '',
firstName: firstName ? firstName : '',
lastName: lastName ? lastName : '',
dateOfBirth: null,
roles: [],
photoUrl: photoUrl ? photoUrl : '',
phoneNumber: '',
favorites: [],
}).then(res => {
console.log("Effectué")
})
.catch(error => {
console.log("firestore error:", error);
});
} else {
console.log('Document data:', user.data());
}
const expirationTime = (await response.user.getIdTokenResult())
.expirationTime;
const newDate = new Date(expirationTime).getTime() * 1000;
const token = (await response.user.getIdTokenResult()).token;
await dispatch(authenticate(token, id, newDate));
if (isNeedOnboarding) {
await dispatch(addUserInfos(email, '', firstName, lastName, '', '', photoUrl, '', []));
navigation.navigate("Onboarding");
} else {
await dispatch(
addUserInfos(
user.get("email"),
user.get("username"),
user.get("firstName"),
user.get("lastName"),
user.get("dateOfBirth")?.toDate(),
user.get("roles"),
user.get("photoUrl"),
user.get("phoneNumber"),
user.get("favorites"),
)
);
}
const expirationDate = new Date(new Date().getTime() + newDate);
saveDataToStorage(token, id, expirationDate);
if (isNeedOnboarding) {
navigation.navigate("Onboarding");
}
};
};
The dispatch function addUserInfo :
const addUserInfos = (
email,
username,
firstName,
lastName,
dateOfBirth,
roles,
photoUrl,
phoneNumber,
favorites,
) => {
return (dispatch) => {
dispatch({
type: ADD_USER_INFO,
payload: {
email: email,
username: username,
firstName: firstName,
lastName: lastName,
dateOfBirth: dateOfBirth,
roles: roles,
photoUrl: photoUrl,
phoneNumber: phoneNumber,
favorites: favorites,
},
});
};
};
And the authenticate :
export const authenticate = (userId, token, expiryTime) => {
return (dispatch) => {
dispatch(setLogoutTimer(expiryTime));
dispatch({ type: AUTHENTICATE, payload: { userId, token } });
};
};
Do you have any idea why it works in the simple login but not when I want to connect with google?
Then why it is only when I reload the application that my global state is updated ?
Thanks in advance.

How to request with nested json in react native?

I am trying to send the nested object to the server with authorization token, I searched for this issue but not getting a solution from any sites
This is how I send Data:
CallUpdateProfile = () => {
AsyncStorage.getItem('token', (err, token) => {
tokenAuth = token
});
AsyncStorage.getItem('userId', (err, userID) => {
UserID = userID
});
const insideData={
userId : UserID,
contactparam :{
firstName: this.state.firstName,
lastName: this.state.lastName,
emailAddress: this.state.emailAddress,
cellPhoneNumber: this.state.cellPhoneNumber,
}
};
console.log(insideData);
updateProfile(insideData)
.then(response => {
let data = response.data.data;
})
.catch(error => {
console.error(error);
})
}
but result is
{
"contactparam": {
"firstName": "DK2",
"lastName": "KHATRI",
"emailAddress": "abc#hmail.com",
"cellPhoneNumber": "9999999990"
}
}
what is the expected result ?
userId: 1,
{
"contactparam": {
"firstName": "DK2",
"lastName": "KHATRI",
"emailAddress": "abc#hmail.com",
"cellPhoneNumber": "9999999990"
}
}
In your code insideData object create before AsyncStorage fetch userId. So make CallUpdateProfile as async & wait until AsyncStorage fetch userId as below.
CallUpdateProfile = async () => {
await AsyncStorage.getItem("token", (err, token) => {
tokenAuth = token;
});
await AsyncStorage.getItem("userId", (err, userID) => {
UserID = userID;
});
const insideData = {
userId: UserID,
contactparam: {
firstName: this.state.firstName,
lastName: this.state.lastName,
emailAddress: this.state.emailAddress,
cellPhoneNumber: this.state.cellPhoneNumber
}
};
console.log(insideData);
updateProfile(insideData)
.then(response => {
let data = response.data.data;
})
.catch(error => {
console.error(error);
});
};
You can use the following snippet to archive the desired object
CallUpdateProfile = async () => {
const UserID = await AsyncStorage.getItem('userId');
if (UserID !== null) {
const insideData = {
userId: UserID,
contactparam: {
firstName: this.state.firstName,
lastName: this.state.lastName,
emailAddress: this.state.emailAddress,
cellPhoneNumber: this.state.cellPhoneNumber,
}
}
// continue you implentation
}
};

how to remove unverfied users from the database automatically?

I am making a social networking site with MERN Stack. Firstly, the users could simply sign up with email and password but that meant anyone can use any random email and possibly spam the DB. So I turned to this video https://www.youtube.com/watch?v=76tKpVbjhu8 and implemented it to ask for email verification.
Now the problem is even if a user registers and didn't verify his account another user can't create an account with the same email as it says user already exists. This means if some prankster registered an account with:
name: John Doe
email: johndoe#gmail.com
password: 123456
And couldn't verify his account, if someday the real johndoe comes and tries to register an account, he won't be able to register it as it already exists.
Is there anyway for the user to be automatically deleted if it failed to verify his account during the set time.
Here's my User Schema
const mongoose = require('mongoose');
const UserSchema = new mongoose.Schema({
name: {
type: String,
required: true
},
posts: [
{
type: mongoose.Schema.Types.ObjectId,
ref: 'Post'
}
],
profile: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Profile'
},
email: {
type: String,
required: true,
unique: true
},
password: {
type: String,
required: true
},
isVerified: {
type: Boolean,
default: false
},
date: {
type: Date,
default: Date.now
}
});
const User = mongoose.model('User', UserSchema);
module.exports = User;
Register route
// #route POST api/users
// #desc REgister a user
// #access Public
router.post(
'/',
[
check('name', 'Please add name')
.not()
.isEmpty(),
check('email', 'Please include a valid email').isEmail(),
check(
'password',
'Please enter a password with 6 or more characters'
).isLength({ min: 6 })
],
async (req, res) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
const { name, email, password } = req.body;
try {
let user = await User.findOne({ email });
if (user) {
return res.status(400).json({ msg: 'User already exists' });
}
user = new User({
name,
email,
password
});
const salt = await bcrypt.genSalt(10);
user.password = await bcrypt.hash(password, salt);
await user.save();
const payload = {
user: {
id: user.id
}
};
let profile = new Profile({
user: user.id
});
await profile.save();
user.profile = profile.id;
await user.save();
jwt.sign(
payload,
config.get('jwtSecret'),
{
expiresIn: 36000
},
(err, token) => {
if (err) throw err;
const url = `http://localhost:3000/confirmation/${token}`;
transporter.sendMail({
to: user.email,
subject: 'Confirm Email',
html: `Please click this link to confirm your email for signing up for MERN Blog ${url}`
});
res.json({
msg:
'Thanks for signing up! An email has been sent to your email for verification. Go ahead and verify your account'
});
}
);
} catch (err) {
console.log(err.message);
res.status(500).send('Server Error');
}
}
);
module.exports = router;
Confirmation route
router.post('/confirmation', async (req, res) => {
const token = req.body.token;
//Check if not token
if (!token) {
return res.status(401).json({ msg: 'No token, authorization denied' });
}
try {
const decoded = jwt.verify(token, config.get('jwtSecret'));
const user = await User.findByIdAndUpdate(decoded.user.id, {
isVerified: true
});
const payload = {
user: {
id: user.id
}
};
jwt.sign(
payload,
config.get('jwtSecret'),
{
expiresIn: 36000
},
(err, token) => {
if (err) throw err;
res.json({ token });
}
);
} catch (err) {
res.status(401).json({ msg: 'Token is not valid' });
}
});
I think the best way of doing this would be to check the accounts creation date/time, if it has been over X amount of days/weeks && the email is not verified, then dispatch a delete action.

this not working in async function in React component

In my React component this.login() isn't executing. I believe this is because an await function changes the context of this, but I'm not sure how to fix it.
await this.props
.SignupUser({
variables: {
email: this.state.email,
password: this.state.password,
name: this.state.name,
},
})
.then(() => {
this.login();
})
.catch(error => {
this.setState({ wait: false });
const errorMessage = error.graphQLErrors[0].functionError;
this.setState({ error: errorMessage });
});
Remove await keyword, It should work.
Alternatively, we need to implement in a different way
The function must add async keyword in the function declaration for below code
await this.props
.SignupUser({
variables: {
email: this.state.email,
password: this.state.password,
name: this.state.name,
},
})
.then(() => {
this.login();
})
.catch(error => {
this.setState({ wait: false });
const errorMessage = error.graphQLErrors[0].functionError;
this.setState({ error: errorMessage });
});
(or)
try {
const user = await this.props
.SignupUser({
variables: {
email: this.state.email,
password: this.state.password,
name: this.state.name,
},
});
this.login();
} catch (e) {
this.setState({ wait: false });
const errorMessage = error.graphQLErrors[0].functionError;
this.setState({ error: errorMessage });
}

Resources