diff --git a/src/controllers/auth.js b/src/controllers/auth.js index b6a4d17f18..7ebf4bf7f2 100644 --- a/src/controllers/auth.js +++ b/src/controllers/auth.js @@ -41,10 +41,9 @@ api.auth = function(req, res, next) { }; api.authWithSession = function(req, res, next) { //[todo] there is probably a more elegant way of doing this... - var uid = req.session.userId; if (!(req.session && req.session.userId)) return res.json(401, NO_SESSION_FOUND); - User.findOne({_id: uid}, function(err, user) { + User.findOne({_id: req.session.userId}, function(err, user) { if (err) return next(err); if (_.isEmpty(user)) return res.json(401, NO_USER_FOUND); res.locals.user = user; @@ -62,33 +61,25 @@ api.authWithUrl = function(req, res, next) { } api.registerUser = function(req, res, next) { - var confirmPassword, e, email, password, username, _ref; - _ref = req.body, email = _ref.email, username = _ref.username, password = _ref.password, confirmPassword = _ref.confirmPassword; - if (!(username && password && email)) { - return res.json(401, {err: ":username, :email, :password, :confirmPassword required"}); - } - if (password !== confirmPassword) { - return res.json(401, {err: ":password and :confirmPassword don't match"}); - } - if (!validator.isEmail(email)) { - return res.json(401, {err: ":email invalid"}); - } + var confirmPassword = req.body.confirmPassword, + email = req.body.email, + password = req.body.password, + username = req.body.username; + if (!(username && password && email)) return res.json(401, {err: ":username, :email, :password, :confirmPassword required"}); + if (password !== confirmPassword) return res.json(401, {err: ":password and :confirmPassword don't match"}); + if (!validator.isEmail(email)) return res.json(401, {err: ":email invalid"}); async.waterfall([ function(cb) { User.findOne({'auth.local.email': email}, cb); }, function(found, cb) { - if (found) { - return cb("Email already taken"); - } + if (found) return cb("Email already taken"); User.findOne({'auth.local.username': username}, cb); }, function(found, cb) { var newUser, salt, user; - if (found) { - return cb("Username already taken"); - } + if (found) return cb("Username already taken"); salt = utils.makeSalt(); - var newUser = { + newUser = { auth: { local: { username: username, @@ -138,10 +129,9 @@ api.registerUser = function(req, res, next) { ga.event('register', 'Local').send() } ], function(err, saved) { - if (err) { - return res.json(401, {err: err}); - } + if (err) return res.json(401, {err: err}); res.json(200, saved); + email = password = username = null; }); }; @@ -167,6 +157,7 @@ api.loginLocal = function(req, res, next) { if (err) return next(err); if (!user) return res.json(401,{err:"Username or password incorrect. Click 'Forgot Password' for help with either. (Note: usernames are case-sensitive)"}); res.json({id: user._id,token: user.apiToken}); + password = null; }); }); }; @@ -177,10 +168,8 @@ api.loginLocal = function(req, res, next) { api.loginFacebook = function(req, res, next) { - var email, facebook_id, name, _ref; - _ref = req.body, facebook_id = _ref.facebook_id, email = _ref.email, name = _ref.name; - if (!facebook_id) - return res.json(401, {err: 'No facebook id provided'}); + var facebook_id = req.body.facebook_id; + if (!facebook_id) return res.json(401, {err: 'No facebook id provided'}); User.findOne({'auth.facebook.id': facebook_id}, function(err, user) { if (err) { return res.json(401, {err: err}); @@ -215,31 +204,31 @@ api.resetPassword = function(req, res, next){ html: "Password for " + user.auth.local.username + " has been reset to " + newPassword + ". Log in at " + nconf.get('BASE_URL') }); user.save(); - return res.send('New password sent to '+ email); + res.send('New password sent to '+ email); + email = salt = newPassword = hashed_password = null; }); }; api.changeUsername = function(req, res, next) { var user = res.locals.user, - password = req.body.password + password = req.body.password, newUsername = req.body.newUsername; User.findOne({'auth.local.username': newUsername}, function(err, result) { if (err) next(err); + if(result) return res.json(401, {err: "Username already taken"}); - if(result) - return res.json(401, {err: "Username already taken"}); - - var salt = user.auth.local.salt, - hashed_password = utils.encryptPassword(password, salt); + var salt = user.auth.local.salt; + var hashed_password = utils.encryptPassword(password, salt); if (hashed_password !== user.auth.local.hashed_password) return res.json(401, {err:"Incorrect password"}); user.auth.local.username = newUsername; user.save(function(err, saved){ - if (err) next(err); - res.send(200); + if (err) next(err); + res.send(200); + user = password = newUsername = null; }) }); } diff --git a/src/controllers/challenges.js b/src/controllers/challenges.js index ba692a0d1e..b5fe4d6982 100644 --- a/src/controllers/challenges.js +++ b/src/controllers/challenges.js @@ -38,9 +38,9 @@ api.list = function(req, res, next) { }) .select('name leader description group memberCount prize official') .select({members:{$elemMatch:{$in:[user._id]}}}) + .sort('-official -timestamp') .populate('group', '_id name') .populate('leader', 'profile.name') - .sort('-official -timestamp') .exec(cb); } ], function(err, challenges){ @@ -49,6 +49,7 @@ api.list = function(req, res, next) { c._isMember = c.members.length > 0; }) res.json(challenges); + user = null; }); } @@ -77,14 +78,13 @@ api.csv = function(req, res, next) { function(_challenge,cb) { challenge = _challenge; if (!challenge) return cb('Challenge ' + cid + ' not found'); - User.aggregate([ - {$match:{'_id':{ '$in': challenge.members}}}, //yes, we want members - {$project:{'profile.name':1,tasks:{$setUnion:["$habits","$dailys","$todos","$rewards"]}}}, - {$unwind:"$tasks"}, - {$match:{"tasks.challenge.id":cid}}, - {$sort:{'tasks.type':1,'tasks.id':1}}, - {$group:{_id:"$_id", "tasks":{$push:"$tasks"},"name":{$first:"$profile.name"}}} - + User.aggregate([ + {$match:{'_id':{ '$in': challenge.members}}}, //yes, we want members + {$project:{'profile.name':1,tasks:{$setUnion:["$habits","$dailys","$todos","$rewards"]}}}, + {$unwind:"$tasks"}, + {$match:{"tasks.challenge.id":cid}}, + {$sort:{'tasks.type':1,'tasks.id':1}}, + {$group:{_id:"$_id", "tasks":{$push:"$tasks"},"name":{$first:"$profile.name"}}} ], cb); } ],function(err,users){ @@ -107,6 +107,7 @@ api.csv = function(req, res, next) { }); res.header('Content-disposition', 'attachment; filename='+cid+'.csv'); res.csv(output); + challenge = cid = null; }) } @@ -137,8 +138,9 @@ api.getMember = function(req, res, next) { .project(proj) .exec(function(err, member){ if (err) return next(err); - if (!member) return res.json(404, {err: 'Member '+uid+' for challenge '+cid+' not found'}); + if (!member) return res.json(404, {err: 'Member '+uid+' for challenge '+cid+' not found'}); res.json(member[0]); + uid = cid = null; }); } @@ -181,7 +183,7 @@ api.create = function(req, res, next){ // User pays for all of prize user.balance -= prizeCost; } - cb(null) + cb(null); }); } @@ -206,6 +208,7 @@ api.create = function(req, res, next){ async.waterfall(waterfall, function(err){ if (err) return next(err); res.json(chal); + user = group = chal = null; }); } @@ -229,8 +232,6 @@ api.update = function(req, res, next){ Challenge.findByIdAndUpdate(cid, {$set:attrs}, cb); }, function(saved, cb) { - // after saving, we're done as far as the client's concerned. We kick of syncing (heavy task) in the background - cb(null, saved); // Compare whether any changes have been made to tasks. If so, we'll want to sync those changes to subscribers if (before.isOutdated(req.body)) { @@ -243,10 +244,13 @@ api.update = function(req, res, next){ }) } + // after saving, we're done as far as the client's concerned. We kick off syncing (heavy task) in the background + cb(null, saved); } ], function(err, saved){ if(err) next(err); res.json(saved); + cid = user = before = null; }) } @@ -284,6 +288,7 @@ function closeChal(cid, broken, cb) { }) }) async.parallel(parallel, cb2); + removed = null; } ], cb); } @@ -306,6 +311,7 @@ api['delete'] = function(req, res, next){ ], function(err){ if (err) return next(err); res.send(200); + user = cid = null; }); } @@ -340,6 +346,7 @@ api.selectWinner = function(req, res, next) { ], function(err){ if (err) return next(err); res.send(200); + user = cid = chal = null; }) } @@ -369,6 +376,7 @@ api.join = function(req, res, next){ if(err) return next(err); chal._isMember = true; res.json(chal); + user = cid = null; }); } @@ -401,6 +409,7 @@ api.leave = function(req, res, next){ if(err) return next(err); if (chal) chal._isMember = false; res.json(chal); + user = cid = keep = null; }); } @@ -417,5 +426,6 @@ api.unlink = function(req, res, next) { user.unlink({cid:cid, keep:req.query.keep, tid:tid}, function(err, saved){ if (err) return next(err); res.send(200); + user = tid = cid = null; }); } diff --git a/src/controllers/coupon.js b/src/controllers/coupon.js index 233be59078..b8450d34f3 100644 --- a/src/controllers/coupon.js +++ b/src/controllers/coupon.js @@ -5,8 +5,7 @@ var csv = require('express-csv'); var async = require('async'); api.ensureAdmin = function(req, res, next) { - var user = res.locals.user; - if (!user.contributor.sudo) return res.json(401, {err:"You don't have admin access"}); + if (!res.locals.user.contributor.sudo) return res.json(401, {err:"You don't have admin access"}); next(); } @@ -34,4 +33,4 @@ api.enterCode = function(req,res,next) { if (err) return res.json(400,{err:err}); res.json(user); }); -} \ No newline at end of file +} diff --git a/src/controllers/dataexport.js b/src/controllers/dataexport.js index 6e5f6703b2..48a08b1d38 100644 --- a/src/controllers/dataexport.js +++ b/src/controllers/dataexport.js @@ -39,8 +39,7 @@ var userdata = function(user) { } dataexport.leanuser = function(req, res, next) { - var user = res.locals.user; - User.findOne({_id: user._id,}).lean().exec(function(err, user) { + User.findOne({_id: res.locals.user._id}).lean().exec(function(err, user) { if (err) return res.json(500, {err: err}); if (_.isEmpty(user)) return res.json(401, NO_USER_FOUND); res.locals.user = user; @@ -56,7 +55,7 @@ dataexport.userdata = { json: function(req, res) { var user = userdata(res.locals.user); return res.jsonstring(user); - }, + } } /* diff --git a/src/controllers/groups.js b/src/controllers/groups.js index 02638f5432..cd7c768fad 100644 --- a/src/controllers/groups.js +++ b/src/controllers/groups.js @@ -117,6 +117,8 @@ api.list = function(req, res, next) { return m.concat(_.isArray(v) ? v : [v]); }, []) res.json(arr); + + user = groupFields = sort = type = null; }) }; @@ -139,6 +141,7 @@ api.get = function(req, res, next) { if (err) return next(err); if (!group && gid!=='party') return res.json(404,{err: "Group not found or you don't have access."}); res.json(group); + gid = null; }); }; @@ -162,6 +165,7 @@ api.create = function(req, res, next) { ],function(err,saved){ if (err) return next(err); res.json(saved); + group = user = null; }); }else{ @@ -180,6 +184,7 @@ api.create = function(req, res, next) { if (err == 'Already in a party, try refreshing.') return res.json(400,{err:err}); if (err) return next(err); return res.json(populated); + group = user = null; }) } } @@ -236,6 +241,7 @@ api.postChat = function(req, res, next) { group.save(function(err, saved){ if (err) return next(err); return chatUpdated ? res.json({chat: group.chat}) : res.json({message: saved.chat[0]}); + group = chatUpdated = null; }); } @@ -254,7 +260,8 @@ api.deleteChatMessage = function(req, res, next){ Group.update({_id:group._id}, {$pull:{chat:{id: req.params.messageId}}}, function(err){ if(err) return next(err); - return chatUpdated ? res.json({chat: group.chat}) : res.send(204); + chatUpdated ? res.json({chat: group.chat}) : res.send(204); + group = chatUpdated = null; }); } @@ -324,6 +331,7 @@ api.join = function(req, res, next) { // Return the group? Or not? res.json(results[1]); + group = null; }); } @@ -400,13 +408,13 @@ api.leave = function(req, res, next) { ],function(err){ if (err) return next(err); return res.send(204); + user = group = keep = null; }) } api.invite = function(req, res, next) { var group = res.locals.group; var uuid = req.query.uuid; - var user = res.locals.user; User.findById(uuid, function(err,invite){ if (err) return next(err); @@ -454,6 +462,7 @@ api.invite = function(req, res, next) { // Have to return whole group and its members for angular to show the invited user res.json(results[2]); + group = uuid = null; }); } }); @@ -463,7 +472,7 @@ api.removeMember = function(req, res, next){ var group = res.locals.group; var uuid = req.query.uuid; var user = res.locals.user; - + if(group.leader !== user._id){ return res.json(401, {err: "Only group leader can remove a member!"}); } @@ -479,7 +488,7 @@ api.removeMember = function(req, res, next){ update['$inc'] = {memberCount: -1}; Group.update({_id:group._id},update, function(err, saved){ if (err) return next(err); - + // Sending an empty 204 because Group.update doesn't return the group // see http://mongoosejs.com/docs/api.html#model_Model.update return res.send(204); @@ -506,11 +515,13 @@ api.removeMember = function(req, res, next){ // Sending an empty 204 because Group.update doesn't return the group // see http://mongoosejs.com/docs/api.html#model_Model.update return res.send(204); + group = uuid = null; }); }); }else{ return res.json(400, {err: "User not found among group's members!"}); + group = uuid = null; } } @@ -581,9 +592,10 @@ questStart = function(req, res, next) { var lastIndex = results.length -1; var groupClone = clone(group); - + groupClone.members = results[lastIndex].members; + group = null; return res.json(groupClone); }); } @@ -650,6 +662,7 @@ api.questCancel = function(req, res, next){ ], function(err){ if (err) return next(err); res.json(group); + group = null; }) } @@ -688,6 +701,6 @@ api.questAbort = function(req, res, next){ groupClone.members = results[2].members; res.json(groupClone); + group = null; }) } - diff --git a/src/controllers/payments.js b/src/controllers/payments.js index f8ce5950a9..d117d02ca5 100644 --- a/src/controllers/payments.js +++ b/src/controllers/payments.js @@ -14,6 +14,7 @@ var request = require('request'); var moment = require('moment'); var api = module.exports; var isProduction = nconf.get("NODE_ENV") === "production"; +var stripe = require("stripe")(nconf.get('STRIPE_API_KEY')); var PaypalRecurring = require('paypal-recurring'); var paypalRecurring = new PaypalRecurring({ @@ -51,7 +52,7 @@ function getMailingInfo(user) { } function emailUser(user, emailType) { - mailingInfo = getMailingInfo(user); + var mailingInfo = getMailingInfo(user); if(mailingInfo.email){ request({ url: nconf.get('EMAIL_SERVER_URL') + '/job', @@ -131,8 +132,6 @@ if (nconf.get('NODE_ENV')==='testing') { Setup Stripe response when posting payment */ api.stripeCheckout = function(req, res, next) { - var api_key = nconf.get('STRIPE_API_KEY'); - var stripe = require("stripe")(api_key); var token = req.body.id; var user = res.locals.user; @@ -164,11 +163,11 @@ api.stripeCheckout = function(req, res, next) { ], function(err, saved){ if (err) return res.send(500, err.toString()); // don't json this, let toString() handle errors res.send(200); + user = token = null; }); }; api.stripeSubscribeCancel = function(req, res, next) { - var stripe = require("stripe")(nconf.get('STRIPE_API_KEY')); var user = res.locals.user; if (!user.purchased.plan.customerId) return res.json(401, {err: "User does not have a plan subscription"}); @@ -184,11 +183,11 @@ api.stripeSubscribeCancel = function(req, res, next) { ], function(err, saved){ if (err) return res.send(500, err.toString()); // don't json this, let toString() handle errors res.redirect('/'); + user = null; }); }; api.stripeSubscribeEdit = function(req, res, next) { - var stripe = require("stripe")(nconf.get('STRIPE_API_KEY')); var token = req.body.id; var user = res.locals.user; var user_id = user.purchased.plan.customerId; @@ -210,14 +209,14 @@ api.stripeSubscribeEdit = function(req, res, next) { ], function(err, saved){ if (err) return res.send(500, err.toString()); // don't json this, let toString() handle errors res.send(200); + token = user = user_id = sub_id; }); }; api.paypalSubscribe = function(req,res,next) { - var uuid = res.locals.user._id; // Authenticate a future subscription of ~5 USD paypalRecurring.authenticate({ - RETURNURL: nconf.get('BASE_URL') + '/paypal/subscribe/success?uuid=' + uuid, + RETURNURL: nconf.get('BASE_URL') + '/paypal/subscribe/success?uuid=' + res.locals.user._id, CANCELURL: nconf.get("BASE_URL"), PAYMENTREQUEST_0_AMT: 5, L_BILLINGAGREEMENTDESCRIPTION0: "HabitRPG Subscription" @@ -265,12 +264,12 @@ api.paypalSubscribeCancel = function(req, res, next) { ], function(err, saved){ if (err) return next(err); res.redirect('/'); + user = null; }); }; api.paypalCheckout = function(req, res, next) { - var uuid = res.locals.user._id; - var opts = {RETURNURL:nconf.get('BASE_URL') + '/paypal/checkout/success?uuid=' + uuid}; + var opts = {RETURNURL:nconf.get('BASE_URL') + '/paypal/checkout/success?uuid=' + res.locals.user._id}; paypalCheckout.pay(+new Date(), 5, 'HabitRPG Gems', 'USD', opts, function(err, url) { if (err) return next(err); res.redirect(url); @@ -292,6 +291,7 @@ api.paypalCheckoutSuccess = function(req,res,next) { user.save(function(){ if (err) return next(err); res.redirect('/'); + uuid = null; }); }); }); diff --git a/src/controllers/user.js b/src/controllers/user.js index 3fa1cc2528..fe610e4f9a 100644 --- a/src/controllers/user.js +++ b/src/controllers/user.js @@ -68,6 +68,8 @@ api.score = function(req, res, next) { user = res.locals.user, task; + var clearMemory = function(){user = task = id = direction = null;} + // Send error responses for improper API call if (!id) return res.json(400, {err: ':id required'}); if (direction !== 'up' && direction !== 'down') { @@ -104,22 +106,28 @@ api.score = function(req, res, next) { _tmp: user._tmp }, saved.toJSON().stats)); - // If it's a challenge task, sync the score. Do it in the background, we've already sent down a response - // and the user doesn't care what happens back there - if (!task.challenge || !task.challenge.id || task.challenge.broken) return; - if (task.type == 'reward') return; // we don't want to update the reward GP cost + if ( + (!task.challenge || !task.challenge.id || task.challenge.broken) // If it's a challenge task, sync the score. Do it in the background, we've already sent down a response and the user doesn't care what happens back there + || (task.type == 'reward') // we don't want to update the reward GP cost + ) return clearMemory(); Challenge.findById(task.challenge.id, 'habits dailys todos rewards', function(err, chal){ if (err) return next(err); if (!chal) { task.challenge.broken = 'CHALLENGE_DELETED'; - return user.save(); + user.save(); + return clearMemory(); } var t = chal.tasks[task.id]; - if (!t) return chal.syncToUser(user); // this task was removed from the challenge, notify user + // this task was removed from the challenge, notify user + if (!t) { + chal.syncToUser(user); + return clearMemory(); + } t.value += delta; if (t.type == 'habit' || t.type == 'daily') t.history.push({value: t.value, date: +new Date}); chal.save(); + clearMemory(); }); }); }; @@ -164,7 +172,6 @@ api.getTask = function(req, res, next) { api.getBuyList = function (req, res, next) { var list = shared.updateStore(res.locals.user); - return res.json(200, list); }; @@ -231,6 +238,7 @@ api.update = function(req, res, next) { if (!_.isEmpty(errors)) return res.json(401, {err: errors}); if (err) return next(err); res.json(200, user); + user = errors = null; }); }; @@ -262,8 +270,9 @@ api.cron = function(req, res, next) { User.findById(user._id, cb); } ], function(err, saved) { - user = res.locals.user = saved; + res.locals.user = saved; next(err,saved); + user = progress = quest = null; }); }; @@ -314,11 +323,12 @@ api.addTenGems = function(req, res, next) { ------------------------------------------------------------------------ */ api.cast = function(req, res, next) { - var user = res.locals.user; - var targetType = req.query.targetType; - var targetId = req.query.targetId; - var klass = shared.content.spells.special[req.params.spell] ? 'special' : user.stats.class - var spell = shared.content.spells[klass][req.params.spell]; + var user = res.locals.user, + targetType = req.query.targetType, + targetId = req.query.targetId, + klass = shared.content.spells.special[req.params.spell] ? 'special' : user.stats.class, + spell = shared.content.spells[klass][req.params.spell]; + if (!spell) return res.json(404, {err: 'Spell "' + req.params.spell + '" not found.'}); if (spell.mana > user.stats.mp) return res.json(400, {err: 'Not enough mana to cast spell'}); @@ -327,6 +337,7 @@ api.cast = function(req, res, next) { var saved = _.size(arguments == 3) ? arguments[2] : arguments[1]; if (err) return next(err); res.json(saved); + user = targetType = targetId = klass = spell = null; } switch (targetType) { @@ -370,6 +381,8 @@ api.cast = function(req, res, next) { }) } + series.push(function(cb2){g = group = series = found = null;cb2();}) + async.series(series, cb); }, function(whatever, cb){ diff --git a/src/models/coupon.js b/src/models/coupon.js index 692cb91be9..fb99f6f4c0 100644 --- a/src/models/coupon.js +++ b/src/models/coupon.js @@ -49,6 +49,7 @@ CouponSchema.statics.apply = function(user, code, next){ ], function(err){ if (err) return next(err); next(null,_user); + _coupon = _user = null; }) } diff --git a/src/models/group.js b/src/models/group.js index 51a6044b7f..2f8f6674b9 100644 --- a/src/models/group.js +++ b/src/models/group.js @@ -281,6 +281,7 @@ GroupSchema.statics.tavernBoss = function(user,progress) { ],function(err,res){ if (err === true) return; // no current quest if (err) return logging.error(err); + dmg = rage = null; }) }