I'm using this route to allow a user to change their email address. However, it currently lets them add an email address that is already used by another user. How can I prevent this (and send an alert in this situation). Also, I'm wondering if .findOneAndUpdate() is appropriate here as it may stop after finding the first one.
Thanks
app.post('/changeUserEmail', function (req, res) {
db.collection("userDB").findOneAndUpdate(
{username: req.user.username},
{ $set: {email: req.body.newEmail}},
{new: true},
(err, data) => {
if (err) {
console.log(err);
}
console.log(null, data);
}
)
res.render(process.cwd() + "/views/options", {
username: req.user.username,
email: req.body.newEmail,
alert: "Email changed"
});
});
You could first check if the email exists before you update something
app.post("/changeUserEmail", async function(req, res) {
let { username } = req.user;
let { newEmail } = req.body;
let emailExist = await db.collection("userDB").findOne({ email: newEmail });
if (emailExist)
return res.render(process.cwd() + "/views/options", {
alert: "Email already exists"
});
await db
.collection("userDB")
.findOneAndUpdate(
{ username },
{ $set: { email: newEmail } },
{ new: true }
);
res.render(process.cwd() + "/views/options", {
username,
email,
alert: "Email changed"
});
});
Related
When I send this patch request with axios, the backend receives the data, but response.data comes back empty. Please and thanks!
// ACTION IN VUEX STORE
async updateMe({ commit }, payload) {
let id = localStorage.getItem('userId');
let user = { name: payload.name, email: payload.email, id: id };
try {
const response = await axios.patch(
`http://localhost:3000/api/v1/users/updateMe`,
user
);
commit('setUpdatedUser', response.data);
} catch (err) {
console.log(err);
}
}
// CONTROLLER
exports.updateMe = catchAsync(async (req, res, next) => {
const updatedUser = await User.findByIdAndUpdate(
req.body.id,
{
name: req.body.name,
email: req.body.email
},
{ new: true, runValidators: true }
);
res.status(204).json({ data: updatedUser });
});
204 is a No Content response code.
When I am sending POST Request for Login, then show this Error. I have used mongoose & MongoDB Atlas.
If I send POST request with valid email & password, it also shows this error.
Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent
to the client
But POST request for registration is working well.
User Model
const mongoose = require('mongoose')
const Schema = mongoose.Schema
const userSchema = new Schema({
name: {
type: String,
required: true,
trim: true
},
email: {
type: String,
required: true,
},
password: {
type: String,
required: true
},
balance: Number,
income: Number,
expense: Number,
transactions: {
type: [{
type: Schema.Types.ObjectId,
ref: 'Transaction'
}]
}
})
const User = mongoose.model('User', userSchema)
module.exports = User
User Controller
const registorValidate = require('../validator/registrationValidate')
const User = require('../models/userModel')
const bcrypt = require('bcrypt')
const loginValidate = require('../validator/loginValidator')
const jwt = require('jsonwebtoken')
module.exports = {
login: (req, res) => {
const { email, password } = req.body
let logValidate = loginValidate({ email, password })
if (!logValidate.isValid) {
res.status(400).json(logValidate.error)
return
}
User.findOne({ email })
.then(user => {
if (!user) {
console.log(`${email} not found`)
res.json({
msg: `${email} not found`
})
}
bcrypt.compare(password, user.password, (err, result) => {
if (err) {
res.status(400).json({
msg: 'Error occured'
})
}
if (!result) {
res.status(404).json({
msg: `Password doesn't match`
})
}
let token = jwt.sign({
_id: user._id,
name: user.name,
email: user.email
}, 'SECRET', { expiresIn: '2h' })
res.status(200).json({
msg: 'Login successful',
token: `Bearer ${token}`
})
})
return
})
.catch(err => {
res.status(500).json({
msg: 'Error occured'
})
})
res.end()
},
registration: (req, res) => {
let { name, email, password, confirmPassword } = req.body
let validate = registorValidate({ name, email, password, confirmPassword })
if (!validate.isValid) {
res.status(400).json(validate.error)
} else {
User.findOne({ email })
.then(user => {
if (user) {
res.json({
msg: `${email} is already exist`
})
} else {
bcrypt.hash(password, 11, (err, hash) => {
if (err) {
res.status(500).json({
msg: 'Server error occured'
})
}
let user = new User({
name,
email,
password: hash
})
user.save()
.then(user => {
res.status(201).json({
msg: `Thanks ${name} for your registration`,
user
})
})
.catch(err => {
res.status(500).json({
msg: 'Error occured'
})
})
})
}
})
.catch(err => {
res.status(500).json({
msg: 'Error occured'
})
})
}
}
}
Login Validator
const validator = require('validator')
const validate = user => {
let error = {}
// Email validator
if (!user.email) {
error.email = 'Please provide an Email'
} else if (!validator.isEmail(user.email)) {
error.email = 'Please provide a valid Email'
}
// Password validate
if (!user.password) {
error.password = 'Please provide a password'
} else if (user.password.length < 6) {
error.password = 'Password Must be greater or Equal to 6 characters'
}
return {
error,
isValid: Object.keys(error).length === 0
}
}
module.exports = validate
Thanks.
You don't need to put res.end() because when you called res.json() earlier, it already sent the response.
Please be advised that you should return when you call res.end(), res.send(), 'res.json()' and other operations that send the response, just like what you did with res.status(400).json(logValidate.error)
This should be one of the ways prevent you from getting ERR_HTTP_HEADERS_SENT, but keep in mind that if you have nested callbacks, you should return from the outer scope as well
Hello everybody i have this bad code for me how i can optimize it ?
If i used SQL i can do used inner Queries in one query...
"User" it's only object from mongoose
getProfile: async (req, res) => {
const { id } = req.params;
try {
const {
image,
name,
gender,
about,
email,
phone,
address
} = await User.findById({ _id: id }).select('image name gender about email phone address');
const subscriptions = await Subscriber.countDocuments({ userId: id });
const subscribers = await Subscriber.countDocuments({ subscriberId: id });
const user = {
image,
name,
gender,
subscriptions,
subscribers,
about,
email,
phone,
address
};
res.json(user);
} catch (err) {
console.log(err);
}
}
PS.
I only study with this technologies
If i used spread operator of result of my query from User i have like this:
And that what i have in result
module.exports = {
getProfile: async (req, res) => {
const { id } = req.params;
try {
const [data, subscriptions, subscribers] = await Promise.all([
User.findById( { _id: id },
{
__v: false,
password: false,
date: false,
_id: false
},
),
Subscriber.countDocuments({ userId: id }),
Subscriber.countDocuments({ subscriberId: id })
])
const user = {
...data._doc,
subscriptions,
subscribers
}
res.json(user);
} catch (err) {
console.log(err);
}
}
Since all of your queries are independent, the best we can do is execute all of them parallelly with Promise.all(). Try something like this:
getProfile: async (req, res) => {
const { id = _id } = req.params;
try {
const getUser = User.findById({ _id }).select('image name gender about email phone address');
const getSubscriptions = Subscriber.countDocuments({ userId: id });
const getSubscriber = Subscriber.countDocuments({ subscriberId: id });
const [userData, subscriptions, subscribers] = await Promise.all([getUser, getSubscriptions, getSubscriber]);
const user = {
...userData,
subscriptions,
subscribers,
};
res.json(user);
} catch (err) {
console.log(err);
}
}
Hope this helps :)
You can embed subscriptions [array of documents] in the User model. But bear in mind that could put limitations on your api, if subscriptions might be accessed regardless of its user.
I am trying to display a list of users but the logged-in user shouldn't see himself in the list. I can't make the request to get all users but current user to work.
router.get("/", auth, async (req, res) => {
try {
const users = await User.find({ user: { $ne: req.user.id } }).select([
"email",
"username",
"bio"
]);
res.json(users);
} catch (err) {
console.error(err.message);
res.status(500).send("Servor Error");
}
});
module.exports = router;
This request below gets the current user and it works.
router.get("/", auth, async (req, res) => {
try {
const user = await User.findOne({
user: req.user.id
});
if (!user) {
return res.status(400).json({ msg: "There is no profile for this user" });
}
res.json(user);
} catch (err) {
console.error(err.message);
res.status(500).send("Servor error");
}
});
module.exports = router;
You need to use _id field inside the query filter instead of user:
const users = await User.find({ _id: { $ne: req.user.id } }).select([
"email",
"username",
"bio"
]);
I have an application where the user logs in using facebook account, saving the id, name and email. I'm using https://github.com/jaredhanson/passport-facebook
UserSchema.statics.findOrCreateFaceBookUser = function(profile, done) {
var User = this;
User.findOne({
'email': profile.emails[0].value
}, function(err, user) {
if (err) {
throw err;
}
if (user) {
user.facebook = {
id: profile.id,
email: profile.emails[0].value,
name: profile.displayName
};
user.save(function(err) {
if (err) {
return next(err);
} else {
// done(null, user);
}
});
done(null, user);
} else {
User.findOne({
'facebook.id': profile.id
}, function(err, user) {
if (err) {
throw err;
}
//if (err) return done(err);
if (user) {
done(null, user);
} else {
User.create({
firstName:profile.name.givenName,
lastName:profile.name.familyName,
email: profile.emails[0].value,
gender:profile.gender,
image:"http://graph.facebook.com/"+profile.id+"/picture?type=normal",
facebook: {
id: profile.id,
email: profile.emails[0].value,
name: profile.displayName
}
}, function(err, user) {
if (err) {
throw err;
}
done(null, user);
});
}
});
}
});
The problem is that I need to fetch facebook friends who are also using the application, but the "id" returned by facebook graph does not match what I'm persisting in the application.
https://graph.facebook.com/me/friends?fields=id,name&access_token=xpto
The "profile.id" received the passport-facebook is different from the facebook graph field "id"