Delete specific object in MongoDB collectionn - javascript

In MongoDB I have a User collection, where each user has a games object Array with many objects inside. I need to delete a specific game of a certain user (in this case lOrrlB).
JS
I make a Delete request where gameViewing = lOrrlB
$.ajax({
type: 'DELETE',
url: '/games/' + gameViewing,
data: {
toDelete: gameViewing
},
dataType: "json"
})
App.JS
I search for the user and try to deletOne the corresponding game, but in Node I get "Cannot read property 'gameToDelete' of undefined". Also I'm not convinced about the "key" parameter.
app.route('/games/:gameId')
.delete(function(req, res){
let gameToDelete = req.body.toDelete;
User.findById(req.user.id, function(err, foundUser) {
if (err) {
console.log(err)
} else {
if (foundUser) {
User.games[gameToDelete].deleteOne(
{key: req.body.toDelete},
function(err) {
if(!err){
console.log("Deleted!");
} else {
console.log("Error!");
}
}
)
}
}
})
});
Model
const userSchema = new mongoose.Schema({
googleId: String,
profileImage: String,
myCollection: {
type: Object,
default: Object
},
games: {
type: Object,
default: Object
}
});
userSchema.plugin(passportLocalMongoose);
userSchema.plugin(findOrCreate);
const User = new mongoose.model("User", userSchema);
passport.use(new GoogleStrategy({
clientID: process.env.CLIENT_ID,
clientSecret: process.env.CLIENT_SECRET,
callbackURL: "http://localhost:3000/auth/google/12345",
userProfileUrl: "https://www.googleapis.com/oauth2/v3/userinfo",
passReqToCallback: true
},
function(req, accessToken, refreshToken, profile, cb) {
console.log(profile);
User.findOrCreate({
googleId: profile.id,
username: profile.displayName,
profileImage: profile.photos[0].value,
myCollection: catalogDb,
games: {}
},
function(err, user) {
return cb(err, user);
});
}
));

Look like $unset is what you need:
User.findByIdAndUpdate(req.user.id, {
$unset: {
[`games.${gameToDelete}`]: ""
}
}, ...)

Related

Mongoose gives no response when updating object in Mongo

I have a simple ExpressJS/Node backend that contains a MongoDB database for which I use mongoose to interact. I can add objects to the db based on the UserSchema:
const userSchema = mongoose.Schema({
email : {
type: String,
required: true,
trim: true,
unique: 1
},
password : {
type: String,
required: true,
minlength: 5
},
name : {
type: String,
required: true,
maxlength: 30
},
lastname : {
type: String,
required: true,
maxlength: 30
},
cart : {
type : Array,
default: []
},
history : {
type: Array,
default: []
},
role : {
type: Number,
default : 0
},
token : {
type: String
}
});
From the express Server, I can register and add a new user to the DB and I know this works
Server.js
//========================================
// Register User
//========================================
app.post('/api/users/register', (req, res) => {
//create new User
const user = new User(req.body);
//save user
user.save((err, doc) => {
if(err)
return res.json({success: false, err});
res.status(200).json({
success : true,
userdata: doc
});
});
})
In User.js
//========================================
// SAVE in DB
//========================================
const User = mongoose.model('User', userSchema);
Now when I want to login, operation where I need to check the email and password match I encounter a problem when everything is fine and I want to add the JWT to the object all is good until it gets to the save method, there nothing happens and it doesn't respond anymore. It's like it goes in an infinite loop. I get error when something is wrong, but on the positive case, it disappears and sends no response, to either mongo, node, debug anything.
Server.js
app.post('/api/users/login', (req, res) => {
//find the email for the user
User.findOne({'email' : req.body.email} , (err, user) =>{
if(!user)
return res.json({loginSuccess : false, message : 'Authentication failed, email not found'});
//check the password
user.comparePassword(req.body.password, (error, isMatch) => {
if(!isMatch)
return res.json({loginSuccess : false, message : 'Wrong password'});
//generate token
user.generateToken((err, user) => {
if(err)
return res.status(400).send(err);
//store token as a cookie
res.cookie('w_auth', user.token).status(200).json({
loginSuccess : true
})
})
})
})
})
User.js
const mongoose = require('mongoose');
const bcrypt = require('bcrypt');
const jwt = require('jsonwebtoken');
const SALT_I = 10;
require('dotenv').config();
//========================================
// User Login
//========================================
userSchema.methods.comparePassword = function (candidatePassword, cb) {
bcrypt.compare(candidatePassword, this.password, function(error, isMatch){
if(error)
return cb(error);
cb(null, isMatch);
})
}
userSchema.methods.generateToken = function (cb) {
var user = this;
var token = jwt.sign(user._id.toHexString(),process.env.SECRET)
user.token = token;
user.markModified('anything');
user.save(function(err,user){
if(err) return cb(err);
cb(null,user);
})
}
I get no more feedback in node console, debug, Mongo or even Postmen(I can wait here for minutes ) after user.save(...). I know it gets the good user and everything but I don't really know where to get from here. Also in Mongo I see no field for the token, I initially add an object with no token, can this affect everything? Is there another procedure to update an existing object in the collection?
In case GitHub is needed to see the code: Link
Indeed it's really strange, couldn't really debug what's wrong with this 'save' method. As a workaround, however, this one seems to work fine:
userSchema.methods.generateToken = function (cb) {
var user = this;
var token = jwt.sign(user._id.toHexString(), "mystupidsecret");
console.log("in generateToken");
console.log(user);
user.token = token;
console.log(user.token);
var email = user.email;
//save token
User.updateOne({ _id: user._id }, { $set: { token: token } }, function(err, user){
if(err) {
console.log(err);
return cb(err);
}
cb(null, user);
// this one is for debug only!
User.findOne({'email' : email} , (err, user) =>{
console.log("After update: ", user)
});
});
console.log('done');
}
It yields the following:
After update: { cart: [],
history: [],
role: 0,
_id: 5f3e48f09c7edc3f1c24a860,
email: 'abc233#wp.pl',
password:
'$2b$10$iDeeehLOzbQi3dawqW8Lg.HPOvcRBDIS/YD9D1EmqBOH9Be31WpX2',
name: 'ABCDEFGH',
lastname: 'Doeasdasdas',
__v: 0,
token:
'eyJhbGciOiJIUzI1NiJ9.NWYzZTQ4ZjA5YzdlZGMzZjFjMjRhODYw.aH9tCMbIK9t3CReiQg3Azln9Ca8xS7W0xL3qCMOKniY' }

Mongoose not saving correct schema

I have small project to introduce myself to some front-end technologies. I am using Node, Express, Pug, and MongoDB.
I define the user schema in my user.js file:
var userSchema = mongoose.Schema({
username : String,
password : String,
jobs : [{ type: mongoose.Schema.Types.Mixed }]
});
Then, in my passport.js file I start the sign up process.
User.findOne({ 'username' : username }, function(err, user) {
// if there are any errors, return the error
if (err) {
console.log(err);
return done(err);
}
// check to see if theres already a user with that email
if (user) {
console.log('user exists');
return done(null, false, req.flash('signupMessage', 'That username is already taken.'));
} else {
console.log('creating new user...');
// if there is no user with that email
// create the user
var newUser = new User();
newUser.username = username;
newUser.password = newUser.generateHash(password);
newUser.jobs = [{ website: 'google.com' }];
// save the user
newUser.save(function(err) {
if (err) {
console.log(err);
throw err;
}
console.log('user saved: ', newUser);
return done(null, newUser);
});
}
});
The post successfully saves the new user as:
{
"_id": {
"$oid": "5967d2acc64d953330a3ac32"
},
"__v": 0
}
My goal is to have an array in the database where website links can be pushed into the array for that user.
Thanks for any assistance.
Set the jobs field type as array of Mixed:
var userSchema = mongoose.Schema({
local: { username : String, password : String },
jobs: [ { type: mongoose.Schema.Types.Mixed } ]
});
Then create the user passing all parameters to the constructor:
var newUser = new User({
local: {
username: username,
password: User.generateHash(password),
},
jobs: [{ website: 'google.com' }]
});
// save the user
newUser.save(function(err) {
if (err) {
console.log(err);
throw err;
}
console.log('user saved: ', newUser);
return done(null, newUser);
});
You could also create the user without instantiating it first:
// save the user
User.create({
local: {
username: username,
password: User.generateHash(password),
},
jobs: [{ website: 'google.com' }]
}, function(err, newUser) {
if (err) {
console.log(err);
throw err;
}
console.log('user saved: ', newUser);
return done(null, newUser);
})
For both these methods you will probably need to make the generateHash a static method.

Can't Get Email from Facebook Login(API Version 2.8)

For my application(Node.js) i'm using passport-facebook and everything works well except that i can't get user email. I found a lot of issues on this subject but for some reason they not fix my problem. I would be really preciate for some help. So my configurations:
app.js
app.get('/auth/facebook', passport.authenticate('facebook', {scope: 'email'}));
// in scope i tried a different options like ['email'] or ['emails'] and so go on
app.get('/auth/facebook/callback',
passport.authenticate('facebook', {
successRedirect: '/',
failureRedirect: '/login'
}));
passport.js:
passport.use('facebook', new FacebookStrategy({
clientID : secret.facebook.clientID,
clientSecret : secret.facebook.clientSecret,
callbackURL : secret.facebook.callback
},
function(access_token, refreshToken, profile, done) {
console.log(profile) // here i got all profile data except email
process.nextTick(function() {
User.findOne({ 'facebook.id' : profile.id }, function(err, user) {
if (err)
return done(err);
if (user) {
return done(null, user);
} else {
var newUser = new User();
console.log(profile.email) // here i got undefined
newUser.facebook.id = profile.id;
newUser.facebook.token = access_token;
newUser.facebook.firstName = profile.name.givenName;
newUser.facebook.lastName = profile.name.familyName;
newUser.facebook.email = profile.email;
newUser.save((err) => {
if (err)
throw err;
return done(null, newUser);
});
}
});
});
}));
facebook-secret.js
module.exports = {
facebook: {
clientID: '****',
clientSecret: '********',
callback: '/auth/facebook/callback',
profileFields: ['id', 'email']
}
}
in UserSchema:
facebook: {
id: String,
token: String,
email: String,
name: String,
}
facebok - settings
graph - api
in passport.js :
passport.use('facebook', new FacebookStrategy({
clientID : secret.facebook.clientID,
clientSecret : secret.facebook.clientSecret,
callbackURL : secret.facebook.callback
},
change to:
passport.use('facebook', new FacebookStrategy({
clientID : secret.facebook.clientID,
clientSecret : secret.facebook.clientSecret,
callbackURL : secret.facebook.callback,
profileFields : secret.facebook.profileFields
},
My profileField : profileFields: ['id', 'displayName', 'email', 'first_name', 'middle_name', 'last_name']
Try to pass the scopes in url as param like you used in graph API(?fields=id,email...).

How can I reset or empty req.user in a node passport app?

I'm trying to authenticate a user when logging in to website. Upon login, the username and password get sent to a post route from an ajax request on the client side. The problem is that there is an old userID attached to req.user._id that I cannot remove. This old userID continues to authenticate any user I try.
This is my passport settings on the server:
var passport = require('passport');
var BasicStrategy = require('passport-http').BasicStrategy;
var strategy = new BasicStrategy(function(username, password, callback) {
User.findOne({
username: username
}, function(err, user) {
if (err) {
callback(err);
return;
}
if (!user) {
return callback(null, false, {
message: 'Incorrect username.'
});
}
user.validatePassword(password, function(err, isValid) {
if (err) {
return callback(err);
}
if (!isValid) {
return callback(null, false, {
message: 'Incorrect password.'
});
}
return callback(null, user);
});
});
});
passport.use(strategy);
app.use(passport.initialize());
passport.serializeUser(function(user, done) {
done(null, user.id);
});
passport.deserializeUser(function(id, done) {
User.findById(id, function(err, user) {
done(err, user);
});
});
This is my route:
app.post('/authenticate', passport.authenticate('basic', { session: false }), function(req, res) {
console.log("req.user:", req.user);
var id = req.user._id;
res.json({
message: 'You have been authenticated',
userID: id
});
});
On the client side, I am sending a POST request like this:
function getLogin(loginObject) {
$.ajax({
type: "POST",
url: "/authenticate",
dataType : "json",
data: { username: loginObject.username, password: loginObject.password }
})
.done(function(result) {
console.log("Authenticated");
loginUserID = result.userID;
})
.fail(function(jqXHR, error, errorThrown) {
};
This is a screenshot of my terminal showing the userID that won't delete:
req.user: { _id: 588a834c08c038342e420568,
username: 'mm',
password: '$2a$10$Khp1wUkHvLLn6ZVBNlLqM.Mtio0ZZ4dznGPQs0ECqf.snhUl44OxK',
__v: 0 }
From my understanding, the req.user._id is created as soon as the username and password get's authenticated. So I cannot understand what is holding on to this userID.
Your help is appreciated.

{ [FacebookTokenError: This authorization code has been used.]

I have a sails app. I was trying to implement Facebook Login. When I click on the Login with facebook button i am getting this error:
error: A server error occurred in a request:
error: FacebookTokenError: This authorization code has been used.
Full error log looks like this:
error: A server error occurred in a request:
error: FacebookTokenError: This authorization code has been used.
at Strategy.parseErrorResponse (/home/node_modules/passport-facebook/lib/strategy.js:198:12)
at Strategy.OAuth2Strategy._createOAuthError (/home/node_modules/passport-facebook/node_modules/passport-oauth2/lib/strategy.js:341:16)
at /home/node_modules/passport-facebook/node_modules/passport-oauth2/lib/strategy.js:166:45
at /home/node_modules/passport-facebook/node_modules/passport-oauth2/node_modules/oauth/lib/oauth2.js:177:18
at passBackControl (/home/node_modules/passport-facebook/node_modules/passport-oauth2/node_modules/oauth/lib/oauth2.js:123:9)
at IncomingMessage.<anonymous> (/home/node_modules/passport-facebook/node_modules/passport-oauth2/node_modules/oauth/lib/oauth2.js:143:7)
at IncomingMessage.emit (events.js:117:20)
at _stream_readable.js:944:16
at process._tickDomainCallback (node.js:492:13) { [FacebookTokenError: This authorization code has been used.]
name: 'FacebookTokenError',
message: 'This authorization code has been used.',
type: 'OAuthException',
code: 100,
subcode: undefined,
status: 500 }
Middleware code looks like this:
var passport = require('passport')
, FacebookStrategy = require('passport-facebook').Strategy
, moment= require('moment')
, momentTimeZone=require('moment-timezone')
, inflection = require('inflection')
, markdown = require('markdown').markdown
, URL =require('url')
, LocalStrategy=require('passport-local').Strategy
, config= require('./local')
, device = require('express-device')
var createUser = function (token, tokenSecret, profile, done) {
process.nextTick(function () {
User.findOne({
or: [
{uid: parseInt(profile.id)},
{uid: profile.id}
]
}
).exec(function (err, user) {
if (user) {
return done(null, user);
} else {
var data = {
provider: profile.provider,
uid: profile.id,
name: profile.displayName,
email: profile.email
};
if(profile.emails && profile.emails[0] && profile.emails[0].value) {
data.email = profile.emails[0].value;
}
if(profile.name && profile.name.givenName) {
data.firstname = profile.name.givenName;
}
if(profile.name && profile.name.familyName) {
data.lastname = profile.name.familyName;
}
User.create(data).exec(function (err, user) {
sails.log.info("Error",JSON.stringify(err))
return done(err, user);
});
}
});
});
};
module.exports = {
passport.use(new FacebookStrategy({
clientID: config.facebook.clientID,
clientSecret: config.facebook.clientSecret,
callbackURL: config.facebook.callbackURL,
profileFields: ['name', 'emails' ],
enableProof: true
},
function (accessToken, refreshToken, email, done)
{
//console.log("Auth done");
//done(null, email);
createUser
}
//createUser
//}
))
passport.serializeUser(function(user, done) {
done(null, user);
});
passport.deserializeUser(function(obj, done) {
done(null, obj);
});
/*app.get("/auth/facebook", passport.authenticate("facebook", { scope: ['email', 'public_profile'] }));*/
app.get('/auth/facebook',
passport.authenticate('facebook', { scope: ['email', 'public_profile'] }));
app.get("/auth/facebook/callback",
passport.authenticate("facebook", {
successRedirect: "/",
failureRedirect: "/login"
}),
function(req, res) {
res.redirect('/');
});
app.get('/logout', function(req, res){
req.logout();
res.redirect('/');
});
function ensureAuthenticated(req, res, next) {
if (req.isAuthenticated()) { return next(); }
res.redirect('/login')
}
app.use(passport.initialize());
app.use(passport.session());
app.use(device.capture());
device.enableDeviceHelpers(app)
}
}
};
Can anyone suggest why I am getting this error and any possible solution.
This error occurs when you logged using facebook login, after that delete the user record in your database. You must delete your APP in your facebook account and try again.
Another posibility is that you already are logged in, and your middleware is trying to login again. In your code, you are not checking if the user is already logged in before send the request to "auth/facebook. There is a simple way to prove this: Open a Chrome window in private mode, so no cookie is used, and try again your facebook login. Good Luck!
Probabily not exist some attributes of profile data. Try:
console.log(profile)
For verify all attributes of profile. In my case:
{ id: 'nnnnnnnn',
username: undefined,
displayName: 'My Name',
name:
{ familyName: undefined,
givenName: undefined,
middleName: undefined },
gender: undefined,
profileUrl: undefined,
provider: 'facebook',
_raw: '{"name":"My name","id":"nnnnnnnn"}',
_json: { name: 'My name', id: 'nnnnnnnn' } }
Not exist any attribute "email" or similar, this generate the error and not complete the cicle life of the authenticate:
error: FacebookTokenError: This authorization code has been used.
The attribute email not get because I hidden this in my account Facebook.
In your code:
...
var data = {
provider: profile.provider,
uid: profile.id,
name: profile.displayName,
email: profile.email
};
...
The line:
email: profile.email
You are already assuming that the attribute email is already exist.
Try:
email: (profile.emails && profile.emails[0]) ? profile.emails[0].value : ''

Categories

Resources