As the name states, I keep getting a "Query was already executed" while running Mongoose.find queries. Using '.clone' does not seem to be fixing the issue...
My calling code is:
let result = mongo.isValidUsername(req.body.username).then((data) => {
return data;
});
if ((await result) == false) {
res.send("Sorry, that username is unavailable");
} else {
mongo
.addUser(
req.body.username,
req.body.password,
req.body.firstName,
req.body.lastName,
req.body.email,
req.body.phoneNumber
)
.then(() => {
let profileData = mongo.getProfileData(req.body.username);
profileData
.then((data) => {
res.render("accountDisplay", {
results: data,
trans: [9.99],
});
})
.catch((err) => {
console.log(err);
});
});
}
I call a query twice - Once in isValidUsername() at the beginning (where I have not used .clone) and then again in getProfileData( where I HAVE used .clone).
I keep getting this error. Any idea what could be causing it?
Here is the code for isValidUsername() and getProfileData(), just in case...
async function isValidUsername(usernameToQuery) {
//connect to mongoose database
mongoose.connect("mongodb://localhost:27017/bankDB");
try {
let isValid = UserModel.findOne({ username: usernameToQuery }).then(
(data) => {
if (data == null) {
return true;
} else {
return false;
}
}
);
return await isValid;
} catch (err) {
return err;
}
}
async function getProfileData(usernameToQuery) {
mongoose.connect("mongodb://localhost:27017/bankDB");
let profileData = UserModel.findOne({ username: usernameToQuery }).clone();
console.log(await profileData);
let profileArray = await profileData.then((data) => {
return [
data._doc.firstName,
data._doc.lastName,
data._doc.email,
data._doc.phoneNumber,
];
});
return await profileArray;
}
Related
I have the following code to update Users information by Id:
router.put("/:id", async (req, res) => {
if (req.body.userId === req.params.id || req.body.isAdmin) {
if (req.body.password) {
try {
const salt = await bcrypt.genSalt(10);
req.body.password = await bcrypt.hash(req.body.password, salt);
} catch (err) {
return res.status(500).json(err);
}
}
try {
await User.findByIdAndUpdate(req.params.id, { $set: req.body });
res.status(200).json("Account has been updated");
} catch (err) {
return res.status(500).json(err);
}
} else {
return res.status(403).json("You can update only your account!");
}
});
I pass the new fields and findByIdAndUpdate is returning the catch error, I'm new to node and following a tutorial.
I have this function:
module.exports = async function(username, password, mongo_uri, cluster, collection) {
let client = new MongoClient(mongo_uri)
let database = client.db(cluster)
let collection_name = database.collection(collection)
if (!username || !password || !username && !password) {
return false
} else {
//query mongo
MongoClient.connect(mongo_uri, async function(err, db) {
if (err) {
console.log(err);
}
collection_name.find({'email': username}).toArray(async function(e,doc){
if(doc.length === 0) {
db.close();
return false
} else {
db.close();
return true
}
});
})
}
}
and I have this in the front-end:
let results = await mongo_login_api(username, password, mongo_uri, cluster, collection)
console.log("results")
console.log(results)
when I run this function, it doesn't wait for the backend to return anything. How can I fix this?
My suggestion is:
You put all body of your funcion in Promise.
For example:
module.exports = function(username, password, mongo_uri, cluster, collection) {
return new Promise((resolve, reject) => {
let client = new MongoClient(mongo_uri)
let database = client.db(cluster)
let collection_name = database.collection(collection)
if (!username || !password || !username && !password) {
resolve(false)
} else {
//query mongo
MongoClient.connect(mongo_uri, async function(err, db) {
if (err) {
console.log(err);
}
collection_name.find({'email': username}).toArray(function(e,doc){
if(doc.length === 0) {
db.close();
resolve(false)
} else {
db.close();
resolve(true)
}
});
})
}
}
}
}
You need to promise the MongoClient.connect callback to await it.
module.exports = async function(username, password, mongo_uri, cluster, collection) {
let client = new MongoClient(mongo_uri)
let database = client.db(cluster)
let collection_name = database.collection(collection)
if (!username || !password || !username && !password) {
return false
} else {
const result = await new Promise((resolve, reject) => {
//query mongo
MongoClient.connect(mongo_uri, function(err, db) {
if (err) {
console.log(err);
}
collection_name.find({
'email': username
}).toArray(function(e, doc) {
if (doc.length === 0) {
db.close();
resolve(false)
} else {
db.close();
resolve(true)
}
});
})
})
return result
}
}
I'm using passport strategies for different socialMedia logins and getting the following two errors
InternalOAuthError: Failed to fetch user profile
Cannot set headers after they are sent to the client
I have doubt there somewhere I have returned a callback or response so getting 2nd error but for 1st don't know reasons scope seems to be correct!
strategy code
passport.use(new GoogleStrategy({
clientID: GOOGLE_CLIENT_ID,
clientSecret: GOOGLE_SECRET_KEY,
callbackURL: GOOGLE_CALLBACK_URL
}, async (acessToken, refreshToken, profile, done) => {
await User.findOne({ email: profile._json.email }, async (err, user) => {
if (err) {
console.log("passport.config --> err", err);
done(err, null);
} else if (user) {
if (user.socialType !== "GOOGLE" || user.socialType === null)
done(`LOGIN_CREDENTIALS_WITH_${(user.socialType || "PASSWORD").toUpperCase()}`, false);
else {
done(null, user);
}
} else {
// console.log(profile);
const user = {
email: profile._json.email,
socialId: profile.id,
socialType: "GOOGLE",
firstName: profile.name.givenName,
lastName: profile.name.familyName,
isActive: profile._json.email_verified,
isVerified: profile._json.email_verified,
socialImageUrl: profile._json.picture,
userType: "CUSTOMER"
};
const newUser = new User({ ...user });
const newUserData = await newUser.save();
done(null, newUserData);
}
});
}));
route code:
router.get('/auth/:socialType', customerCtrl.socialTypeLogin);
router.get('/auth/:socialType/callback', customerCtrl.socialTypeLoginCallback);
controller code:
const socialTypeLogin = async (req, res) => {
await customerService.socialTypeLogin(req, res);
};
const socialTypeLoginCallback = async (req,res) => {
await customerService.socialTypeLoginCallback(req,res);
};
service code:
const socialTypeLogin = async (req, res) => {
try {
const socialType = (req.params.socialType || '').toLowerCase();
const GOOGLE_SCOPE = ['email', 'profile'];
const FACEBOOK_SCOPE = ['email'];
let scope = [];
if (socialType === 'google') {
scope = GOOGLE_SCOPE;
} else if (socialType === 'facebook') {
scope = FACEBOOK_SCOPE;
}
let oauthOptions = { scope: scope};
const { returnUrl } = req.query;
if(returnUrl && returnUrl.trim().length !== 0) {
oauthOptions['state'] =JSON.stringify({ returnUrl: returnUrl });
}
passport.authenticate(socialType, oauthOptions)(req, res);
}
catch (error) {
}
}
/**
* #param {string} socialType
*/
const socialTypeLoginCallback = async (req, res) => {
const socialType = (req.params.socialType || '').toLowerCase();
// return new Promise((resolve, reject) => {
try {
passport.authenticate(socialType, async (err, user) => {
let webappRedirectURL = WEBAPP_LOGIN_URL;
try {
const state = req.query.state;
if(state) {
const stateObj = JSON.parse(state);
webappRedirectURL = stateObj.returnUrl;
}
} catch (err1) {
console.log("customer.service --> parsing error",err1);
}
if (err || !user) {
console.log("customer.service --> !user",err);
res.render('oauth-redirect', {
webappRedirectURL: webappRedirectURL,
success: false,
error: err,
timerCounter: 5,
accessToken: undefined
});
}
else {
console.log("customer.service --> Generating Token",user.generateJWT());
res.render('oauth-redirect', {
webappRedirectURL: webappRedirectURL,
success: true,
timerCounter: 5,
accessToken: user.generateJWT(),
error: undefined
});
}
})(req, res);
}
catch (error) {
console.log("customerService.js ==> socialTypeLoginCallback -->",error);
}
};
Thanks for help in advance!
I have doubt there somewhere I have returned a callback or response so getting 2nd error but for 1st don't know reasons scope seems to be correct!
In socialTypeLogin
add line
oauthOptions['session'] = false;
I've got myself two functions, first is responsible for adding a user model to database and second one for comparing passwords. But.. comparing never works..
module.exports.signup = function (req, res) {
if (req.body == null) {
res.status(400);
return res.end('Bad juju');
} else {
let exists;
User.findOne({ username: req.body.username }),
(err, doc) => {
if (doc) {
exists = true;
return;
}
};
if (exists) {
res.setHeader('user-exists', true);
res.redirect('/signup');
} else {
bcrypt.hash(req.body.password, 10, function (hashE, hash) {
if (hashE) {
throw hashE;
}
new User({
username: req.body.username,
email: req.body.email,
password: hash,
}).save();
});
return res.redirect('/login');
}
}
};
module.exports.login = function (req, res) {
if (req.body.tosignup) {
return res.redirect('/signup');
}
if (req.body == null) {
res.status(400);
return res.end('Bad request');
} else {
User.findOne({ username: req.body.username }, (err, doc) => {
if (err) throw console.log(err);
console.log(doc.password);
console.log(req.body.password);
bcrypt.hash(req.body.password, 10, (err, s) => {
console.log(s);
});
bcrypt.compare(req.body.password, doc.password, (err, succ) => {
if (err) {
throw err;
}
console.log(err);
console.log(succ);
if (succ) {
res.setHeader('username', doc.username);
return res.redirect('/welcome');
} else {
res.setHeader('password-wrong', true);
return res.redirect('/login');
}
});
});
}
};
I've looked for different sources and all of them told that this one method is the correct one, but every time I try using it, it just doesn't work
I had a similar problem using bcrypt in nodejs. To solve the problem i switched from npm bcrypt to npm bcryptjs (https://www.npmjs.com/package/bcryptjs) and used the following:
NPM require:
const bcrypt = require('bcryptjs');
To compare the passwords you can use the following code:
async function compareIt(password, hashedPassword) {
const validPassword = await bcrypt.compare(password, hashedPassword);
return validPassword;
}
compareIt(password, passwordBD).then(v => {
if (v == true) {
console.log("Equal");
} else {
console.log("Not equal");
}
});
To hash the password you can use this function:
async function hashIt(password) {
const salt = await bcrypt.genSalt(6);
const hashed = await bcrypt.hash(password, salt);
return hashed;
}
I currently have a check I'm running to see if a username is taken or not. I am querying to see if the username has been taken, and if so provide a value to my errors object. I want to pass my errors defined within my if statement to the outer return statement. Is there a way to go about this?? Im unsure of what to do here.
exports.reduceUserDetails = data => {
let errors = {}
const userRef = db.collection('users').where('username', '==', data.username)
userRef.get().then(snapshot => {
if (!snapshot.empty) {
errors.username = 'username taken'
} else {
console.log('im not taken')
}
})
return {
errors,
valid: Object.keys(errors).length === 0 ? true : false
}
}
here is where I'm using the reduce user details:
exports.profileUpdate = (req, res) => {
let userDetails = req.body
const { valid, errors } = reduceUserDetails(userDetails)
if (!valid) return res.status(400).json(errors)
let document = db
.collection('users')
.where('username', '==', req.user.username)
document
.get()
.then(snapshot => {
snapshot.forEach(doc => {
const data = doc.id
db.collection('users').doc(data).update(req.body)
})
res.json({ message: 'Updated Successfully' })
})
.catch(error => {
console.error(error)
return res.status(400).json({
message: 'Cannot Update the value'
})
})
}
May be abstracting the call in a new function and awaiting it in caller might work, otherwise you will need to add await before reduceUserDetails() wherever you will call
exports.reduceUserDetails = async data => {
let check = await dupChk(data);
return {
check.errors,
valid: check.result
}
}
var dupChk = (data) => (
new Promise((resolve, reject) => {
let errors = {}
const userRef = db.collection('users').where('username', '==', data.username)
userRef.get().then(snapshot => {
if (!snapshot.empty) {
errors.username = 'username taken'
resolve({result:false,errors:errors})
} else {
resolve({result:true, errors: errors});//console.log('im not taken')
}
})
})
);
UPDATE:
Ok no need to do the above stuff just change the reduceUserDetails() like this
exports.reduceUserDetails = data => {
return new Promise((resolve, reject) => {
let errors = {}
const userRef = db.collection('users').where('username', '==', data.username)
userRef.get().then(snapshot => {
if (!snapshot.empty) {
errors.username = 'username taken'
resolve({valid:false,errors:errors})
} else {
resolve({valid:true, errors: errors});//console.log('im not taken')
}
})
.catch(()=>resolve({result:false,errors:errors}))
})
}
And in profileUpdate() add await keyword before the reduceUserDetails() call
const { valid, errors } = await reduceUserDetails(userDetails)