| 
									
										
										
										
											2016-04-14 17:07:40 -07:00
										 |  |  | import { Observable } from 'rx'; | 
					
						
							| 
									
										
										
										
											2016-12-30 13:05:29 -06:00
										 |  |  | import uuid from 'uuid'; | 
					
						
							| 
									
										
										
										
											2015-07-29 15:00:24 -07:00
										 |  |  | import moment from 'moment'; | 
					
						
							| 
									
										
										
										
											2015-12-28 12:41:37 -08:00
										 |  |  | import dedent from 'dedent'; | 
					
						
							| 
									
										
										
										
											2016-04-14 17:07:40 -07:00
										 |  |  | import debugFactory from 'debug'; | 
					
						
							| 
									
										
										
										
											2016-05-12 15:48:34 -07:00
										 |  |  | import { isEmail } from 'validator'; | 
					
						
							| 
									
										
										
										
											2016-05-07 17:46:39 +05:30
										 |  |  | import path from 'path'; | 
					
						
							| 
									
										
										
										
											2016-04-14 17:07:40 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | import { saveUser, observeMethod } from '../../server/utils/rx'; | 
					
						
							| 
									
										
										
										
											2015-07-29 15:00:24 -07:00
										 |  |  | import { blacklistedUsernames } from '../../server/utils/constants'; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-04-14 17:07:40 -07:00
										 |  |  | const debug = debugFactory('fcc:user:remote'); | 
					
						
							| 
									
										
										
										
											2015-08-15 12:19:36 -07:00
										 |  |  | const BROWNIEPOINTS_TIMEOUT = [1, 'hour']; | 
					
						
							| 
									
										
										
										
											2016-06-02 13:49:34 -07:00
										 |  |  | const isDev = process.env.NODE_ENV !== 'production'; | 
					
						
							| 
									
										
										
										
											2015-07-29 15:00:24 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | function getAboutProfile({ | 
					
						
							|  |  |  |   username, | 
					
						
							| 
									
										
										
										
											2016-04-14 17:07:40 -07:00
										 |  |  |   githubProfile: github, | 
					
						
							|  |  |  |   progressTimestamps = [], | 
					
						
							|  |  |  |   bio | 
					
						
							| 
									
										
										
										
											2015-07-29 15:00:24 -07:00
										 |  |  | }) { | 
					
						
							|  |  |  |   return { | 
					
						
							|  |  |  |     username, | 
					
						
							| 
									
										
										
										
											2016-04-14 17:07:40 -07:00
										 |  |  |     github, | 
					
						
							|  |  |  |     browniePoints: progressTimestamps.length, | 
					
						
							|  |  |  |     bio | 
					
						
							| 
									
										
										
										
											2015-07-29 15:00:24 -07:00
										 |  |  |   }; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | function nextTick(fn) { | 
					
						
							|  |  |  |   return process.nextTick(fn); | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2015-06-11 19:11:07 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | module.exports = function(User) { | 
					
						
							|  |  |  |   // NOTE(berks): user email validation currently not needed but build in. This
 | 
					
						
							|  |  |  |   // work around should let us sneak by
 | 
					
						
							|  |  |  |   // see:
 | 
					
						
							|  |  |  |   // https://github.com/strongloop/loopback/issues/1137#issuecomment-109200135
 | 
					
						
							|  |  |  |   delete User.validations.email; | 
					
						
							| 
									
										
										
										
											2015-06-12 11:38:00 -07:00
										 |  |  |   // set salt factor for passwords
 | 
					
						
							|  |  |  |   User.settings.saltWorkFactor = 5; | 
					
						
							| 
									
										
										
										
											2015-08-15 12:19:36 -07:00
										 |  |  |   // set user.rand to random number
 | 
					
						
							| 
									
										
										
										
											2015-08-15 12:28:19 -07:00
										 |  |  |   User.definition.rawProperties.rand.default = | 
					
						
							|  |  |  |     User.definition.properties.rand.default = function() { | 
					
						
							| 
									
										
										
										
											2015-08-15 12:19:36 -07:00
										 |  |  |       return Math.random(); | 
					
						
							|  |  |  |     }; | 
					
						
							| 
									
										
										
										
											2016-04-21 20:35:19 -07:00
										 |  |  |   // increase user accessToken ttl to 900 days
 | 
					
						
							|  |  |  |   User.settings.ttl = 900 * 24 * 60 * 60 * 1000; | 
					
						
							| 
									
										
										
										
											2015-06-12 13:54:38 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-06-12 16:23:20 -07:00
										 |  |  |   // username should not be in blacklist
 | 
					
						
							|  |  |  |   User.validatesExclusionOf('username', { | 
					
						
							| 
									
										
										
										
											2016-04-15 23:48:02 +08:00
										 |  |  |     in: blacklistedUsernames, | 
					
						
							| 
									
										
										
										
											2015-06-12 16:23:20 -07:00
										 |  |  |     message: 'is taken' | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // username should be unique
 | 
					
						
							|  |  |  |   User.validatesUniquenessOf('username'); | 
					
						
							| 
									
										
										
										
											2015-08-17 23:57:38 -07:00
										 |  |  |   User.settings.emailVerificationRequired = false; | 
					
						
							| 
									
										
										
										
											2015-06-12 13:54:38 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-02-09 14:33:25 -08:00
										 |  |  |   User.on('dataSourceAttached', () => { | 
					
						
							|  |  |  |     User.findOne$ = Observable.fromNodeCallback(User.findOne, User); | 
					
						
							|  |  |  |     User.update$ = Observable.fromNodeCallback(User.updateAll, User); | 
					
						
							|  |  |  |     User.count$ = Observable.fromNodeCallback(User.count, User); | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-08-05 18:51:15 -07:00
										 |  |  |   User.observe('before save', function({ instance: user }, next) { | 
					
						
							|  |  |  |     if (user) { | 
					
						
							| 
									
										
										
										
											2016-05-12 15:48:34 -07:00
										 |  |  |       if (user.email && !isEmail(user.email)) { | 
					
						
							|  |  |  |         return next(new Error('Email format is not valid')); | 
					
						
							|  |  |  |       } | 
					
						
							| 
									
										
										
										
											2015-08-05 18:51:15 -07:00
										 |  |  |       user.username = user.username.trim().toLowerCase(); | 
					
						
							| 
									
										
										
										
											2015-08-05 22:56:23 -07:00
										 |  |  |       user.email = typeof user.email === 'string' ? | 
					
						
							|  |  |  |         user.email.trim().toLowerCase() : | 
					
						
							|  |  |  |         user.email; | 
					
						
							| 
									
										
										
										
											2015-08-10 20:55:01 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |       if (!user.progressTimestamps) { | 
					
						
							|  |  |  |         user.progressTimestamps = []; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       if (user.progressTimestamps.length === 0) { | 
					
						
							|  |  |  |         user.progressTimestamps.push({ timestamp: Date.now() }); | 
					
						
							|  |  |  |       } | 
					
						
							| 
									
										
										
										
											2016-06-23 00:55:15 +05:30
										 |  |  |       // this is workaround for preventing a server crash
 | 
					
						
							|  |  |  |       // refer strongloop/loopback/#1364
 | 
					
						
							|  |  |  |       if (user.password === '') { | 
					
						
							|  |  |  |         user.password = null; | 
					
						
							|  |  |  |       } | 
					
						
							| 
									
										
										
										
											2015-08-05 18:51:15 -07:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2016-05-12 15:48:34 -07:00
										 |  |  |     return next(); | 
					
						
							| 
									
										
										
										
											2015-08-05 18:51:15 -07:00
										 |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-04-14 17:07:40 -07:00
										 |  |  |   debug('setting up user hooks'); | 
					
						
							| 
									
										
										
										
											2016-06-23 11:11:56 +05:30
										 |  |  | 
 | 
					
						
							|  |  |  |   User.beforeRemote('confirm', function(ctx, _, next) { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (!ctx.req.query) { | 
					
						
							|  |  |  |       return ctx.res.redirect('/'); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const uid = ctx.req.query.uid; | 
					
						
							|  |  |  |     const token = ctx.req.query.token; | 
					
						
							|  |  |  |     const redirect = ctx.req.query.redirect; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return User.findById(uid, (err, user) => { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (err || !user) { | 
					
						
							|  |  |  |           ctx.req.flash('error', { | 
					
						
							|  |  |  |             msg: dedent`Oops, something went wrong, please try again later` | 
					
						
							|  |  |  |           }); | 
					
						
							|  |  |  |           return ctx.res.redirect('/'); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (!user.verificationToken && !user.emailVerified) { | 
					
						
							|  |  |  |           ctx.req.flash('info', { | 
					
						
							|  |  |  |             msg: dedent`Looks like we have your email. But you haven't
 | 
					
						
							|  |  |  |              verified it yet, please login and request a fresh verification | 
					
						
							|  |  |  |              link.`
 | 
					
						
							|  |  |  |           }); | 
					
						
							|  |  |  |           return ctx.res.redirect(redirect); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (!user.verificationToken && user.emailVerified) { | 
					
						
							|  |  |  |           ctx.req.flash('info', { | 
					
						
							|  |  |  |             msg: dedent`Looks like you have already verified your email.
 | 
					
						
							|  |  |  |              Please login to continue.`
 | 
					
						
							|  |  |  |           }); | 
					
						
							|  |  |  |           return ctx.res.redirect(redirect); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (user.verificationToken && user.verificationToken !== token) { | 
					
						
							|  |  |  |           ctx.req.flash('info', { | 
					
						
							|  |  |  |             msg: dedent`Looks like you have clicked an invalid link.
 | 
					
						
							|  |  |  |              Please login and request a fresh one.`
 | 
					
						
							|  |  |  |           }); | 
					
						
							|  |  |  |           return ctx.res.redirect(redirect); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return next(); | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-06-11 21:08:40 -04:00
										 |  |  |   User.afterRemote('confirm', function(ctx) { | 
					
						
							| 
									
										
										
										
											2016-06-23 11:11:56 +05:30
										 |  |  |     if (!ctx.req.query) { | 
					
						
							|  |  |  |       return ctx.res.redirect('/'); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     const redirect = ctx.req.query.redirect; | 
					
						
							| 
									
										
										
										
											2015-06-11 21:03:01 -04:00
										 |  |  |     ctx.req.flash('success', { | 
					
						
							|  |  |  |       msg: [ | 
					
						
							| 
									
										
										
										
											2016-06-08 01:13:11 -07:00
										 |  |  |         'Your email has been confirmed!' | 
					
						
							| 
									
										
										
										
											2015-06-11 21:03:01 -04:00
										 |  |  |       ] | 
					
						
							|  |  |  |     }); | 
					
						
							| 
									
										
										
										
											2016-06-23 11:11:56 +05:30
										 |  |  |     return ctx.res.redirect(redirect); | 
					
						
							| 
									
										
										
										
											2015-06-11 21:03:01 -04:00
										 |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-28 13:52:28 -08:00
										 |  |  |   User.beforeRemote('create', function({ req, res }, _, next) { | 
					
						
							| 
									
										
										
										
											2015-08-05 19:29:17 -07:00
										 |  |  |     req.body.username = 'fcc' + uuid.v4().slice(0, 8); | 
					
						
							| 
									
										
										
										
											2015-12-28 13:52:28 -08:00
										 |  |  |     if (!req.body.email) { | 
					
						
							|  |  |  |       return next(); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2016-05-12 15:48:34 -07:00
										 |  |  |     if (!isEmail(req.body.email)) { | 
					
						
							|  |  |  |       return next(new Error('Email format is not valid')); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2015-12-28 13:52:28 -08:00
										 |  |  |     return User.doesExist(null, req.body.email) | 
					
						
							|  |  |  |       .then(exists => { | 
					
						
							|  |  |  |         if (!exists) { | 
					
						
							|  |  |  |           return next(); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         req.flash('error', { | 
					
						
							| 
									
										
										
										
											2015-12-28 12:41:37 -08:00
										 |  |  |           msg: dedent`
 | 
					
						
							|  |  |  |       The ${req.body.email} email address is already associated with an account. | 
					
						
							|  |  |  |       Try signing in with it here instead. | 
					
						
							|  |  |  |           `
 | 
					
						
							| 
									
										
										
										
											2015-12-28 13:52:28 -08:00
										 |  |  |         }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return res.redirect('/email-signin'); | 
					
						
							|  |  |  |       }) | 
					
						
							|  |  |  |       .catch(err => { | 
					
						
							|  |  |  |         console.error(err); | 
					
						
							|  |  |  |         req.flash('error', { | 
					
						
							|  |  |  |           msg: 'Oops, something went wrong, please try again later' | 
					
						
							|  |  |  |         }); | 
					
						
							|  |  |  |         return res.redirect('/email-signup'); | 
					
						
							|  |  |  |       }); | 
					
						
							| 
									
										
										
										
											2015-08-05 19:29:17 -07:00
										 |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-08-16 09:54:34 -07:00
										 |  |  |   User.on('resetPasswordRequest', function(info) { | 
					
						
							| 
									
										
										
										
											2016-05-12 15:48:34 -07:00
										 |  |  |     if (!isEmail(info.email)) { | 
					
						
							|  |  |  |       console.error(new Error('Email format is not valid')); | 
					
						
							|  |  |  |       return null; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2015-08-18 01:32:16 -07:00
										 |  |  |     let url; | 
					
						
							| 
									
										
										
										
											2015-08-16 09:54:34 -07:00
										 |  |  |     const host = User.app.get('host'); | 
					
						
							| 
									
										
										
										
											2015-08-18 01:32:16 -07:00
										 |  |  |     const { id: token } = info.accessToken; | 
					
						
							|  |  |  |     if (process.env.NODE_ENV === 'development') { | 
					
						
							|  |  |  |       const port = User.app.get('port'); | 
					
						
							|  |  |  |       url = `http://${host}:${port}/reset-password?access_token=${token}`; | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |       url = | 
					
						
							| 
									
										
										
										
											2015-08-18 01:40:26 -07:00
										 |  |  |         `http://freecodecamp.com/reset-password?access_token=${token}`; | 
					
						
							| 
									
										
										
										
											2015-08-18 01:32:16 -07:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2015-08-16 09:54:34 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |     // the email of the requested user
 | 
					
						
							| 
									
										
										
										
											2016-04-14 17:07:40 -07:00
										 |  |  |     debug(info.email); | 
					
						
							| 
									
										
										
										
											2015-08-16 09:54:34 -07:00
										 |  |  |     // the temp access token to allow password reset
 | 
					
						
							| 
									
										
										
										
											2016-04-14 17:07:40 -07:00
										 |  |  |     debug(info.accessToken.id); | 
					
						
							| 
									
										
										
										
											2015-08-16 09:54:34 -07:00
										 |  |  |     // requires AccessToken.belongsTo(User)
 | 
					
						
							|  |  |  |     var mailOptions = { | 
					
						
							|  |  |  |       to: info.email, | 
					
						
							|  |  |  |       from: 'Team@freecodecamp.com', | 
					
						
							|  |  |  |       subject: 'Password Reset Request', | 
					
						
							|  |  |  |       text: `
 | 
					
						
							|  |  |  |         Hello,\n\n | 
					
						
							|  |  |  |         This email is confirming that you requested to | 
					
						
							| 
									
										
										
										
											2017-01-14 19:49:01 -05:00
										 |  |  |         reset your password for your freeCodeCamp account. | 
					
						
							| 
									
										
										
										
											2015-08-16 09:54:34 -07:00
										 |  |  |         This is your email: ${ info.email }. | 
					
						
							|  |  |  |         Go to ${ url } to reset your password. | 
					
						
							|  |  |  |         \n | 
					
						
							|  |  |  |         Happy Coding! | 
					
						
							|  |  |  |         \n | 
					
						
							|  |  |  |       `
 | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-05-12 15:48:34 -07:00
										 |  |  |     return User.app.models.Email.send(mailOptions, function(err) { | 
					
						
							| 
									
										
										
										
											2015-08-16 09:54:34 -07:00
										 |  |  |       if (err) { console.error(err); } | 
					
						
							| 
									
										
										
										
											2016-04-14 17:07:40 -07:00
										 |  |  |       debug('email reset sent'); | 
					
						
							| 
									
										
										
										
											2015-08-16 09:54:34 -07:00
										 |  |  |     }); | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-08-25 22:27:01 -07:00
										 |  |  |   User.beforeRemote('login', function(ctx, notUsed, next) { | 
					
						
							|  |  |  |     const { body } = ctx.req; | 
					
						
							|  |  |  |     if (body && typeof body.email === 'string') { | 
					
						
							| 
									
										
										
										
											2016-05-12 15:48:34 -07:00
										 |  |  |       if (!isEmail(body.email)) { | 
					
						
							|  |  |  |         return next(new Error('Email format is not valid')); | 
					
						
							|  |  |  |       } | 
					
						
							| 
									
										
										
										
											2015-08-25 22:27:01 -07:00
										 |  |  |       body.email = body.email.toLowerCase(); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2016-05-12 15:48:34 -07:00
										 |  |  |     return next(); | 
					
						
							| 
									
										
										
										
											2015-08-25 22:27:01 -07:00
										 |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-08-17 23:57:38 -07:00
										 |  |  |   User.afterRemote('login', function(ctx, accessToken, next) { | 
					
						
							| 
									
										
										
										
											2015-06-11 19:11:07 -04:00
										 |  |  |     var res = ctx.res; | 
					
						
							|  |  |  |     var req = ctx.req; | 
					
						
							| 
									
										
										
										
											2015-06-15 15:43:12 -07:00
										 |  |  |     // var args = ctx.args;
 | 
					
						
							| 
									
										
										
										
											2015-06-11 19:11:07 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-06-15 15:43:12 -07:00
										 |  |  |     var config = { | 
					
						
							|  |  |  |       signed: !!req.signedCookies, | 
					
						
							|  |  |  |       maxAge: accessToken.ttl | 
					
						
							|  |  |  |     }; | 
					
						
							| 
									
										
										
										
											2015-08-17 23:57:38 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-06-15 15:43:12 -07:00
										 |  |  |     if (accessToken && accessToken.id) { | 
					
						
							| 
									
										
										
										
											2016-04-14 17:07:40 -07:00
										 |  |  |       debug('setting cookies'); | 
					
						
							| 
									
										
										
										
											2015-06-15 15:43:12 -07:00
										 |  |  |       res.cookie('access_token', accessToken.id, config); | 
					
						
							|  |  |  |       res.cookie('userId', accessToken.userId, config); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2015-08-17 23:57:38 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |     return req.logIn({ id: accessToken.userId.toString() }, function(err) { | 
					
						
							| 
									
										
										
										
											2015-12-28 12:41:37 -08:00
										 |  |  |       if (err) { return next(err); } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-04-14 17:07:40 -07:00
										 |  |  |       debug('user logged in'); | 
					
						
							| 
									
										
										
										
											2015-12-28 12:41:37 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  |       if (req.session && req.session.returnTo) { | 
					
						
							| 
									
										
										
										
											2016-01-17 11:15:05 +04:00
										 |  |  |         var redirectTo = req.session.returnTo; | 
					
						
							|  |  |  |         if (redirectTo === '/map-aside') { | 
					
						
							|  |  |  |           redirectTo = '/map'; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         return res.redirect(redirectTo); | 
					
						
							| 
									
										
										
										
											2015-12-28 12:41:37 -08:00
										 |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-08 01:13:11 -07:00
										 |  |  |       req.flash('success', { msg: 'Success! You are now logged in.' }); | 
					
						
							| 
									
										
										
										
											2015-06-15 15:43:12 -07:00
										 |  |  |       return res.redirect('/'); | 
					
						
							|  |  |  |     }); | 
					
						
							| 
									
										
										
										
											2015-06-11 19:11:07 -04:00
										 |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-06-16 00:29:32 -04:00
										 |  |  |   User.afterRemoteError('login', function(ctx) { | 
					
						
							| 
									
										
										
										
											2015-06-16 00:27:32 -04:00
										 |  |  |     var res = ctx.res; | 
					
						
							|  |  |  |     var req = ctx.req; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     req.flash('errors', { | 
					
						
							|  |  |  |       msg: 'Invalid username or password.' | 
					
						
							|  |  |  |     }); | 
					
						
							| 
									
										
										
										
											2015-08-19 15:55:40 -07:00
										 |  |  |     return res.redirect('/email-signin'); | 
					
						
							| 
									
										
										
										
											2015-06-16 00:27:32 -04:00
										 |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-06-11 19:11:07 -04:00
										 |  |  |   User.afterRemote('logout', function(ctx, result, next) { | 
					
						
							| 
									
										
										
										
											2015-08-17 23:57:38 -07:00
										 |  |  |     var res = ctx.res; | 
					
						
							| 
									
										
										
										
											2015-06-11 19:11:07 -04:00
										 |  |  |     res.clearCookie('access_token'); | 
					
						
							|  |  |  |     res.clearCookie('userId'); | 
					
						
							|  |  |  |     next(); | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-28 13:52:28 -08:00
										 |  |  |   User.doesExist = function doesExist(username, email) { | 
					
						
							| 
									
										
										
										
											2016-05-12 15:48:34 -07:00
										 |  |  |     if (!username && (!email || !isEmail(email))) { | 
					
						
							| 
									
										
										
										
											2015-12-28 13:52:28 -08:00
										 |  |  |       return Promise.resolve(false); | 
					
						
							| 
									
										
										
										
											2015-06-11 19:11:07 -04:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2016-04-14 17:07:40 -07:00
										 |  |  |     debug('checking existence'); | 
					
						
							| 
									
										
										
										
											2015-06-11 16:46:31 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |     // check to see if username is on blacklist
 | 
					
						
							|  |  |  |     if (username && blacklistedUsernames.indexOf(username) !== -1) { | 
					
						
							| 
									
										
										
										
											2015-12-28 13:52:28 -08:00
										 |  |  |       return Promise.resolve(true); | 
					
						
							| 
									
										
										
										
											2015-06-11 16:46:31 -07:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-06-11 19:11:07 -04:00
										 |  |  |     var where = {}; | 
					
						
							|  |  |  |     if (username) { | 
					
						
							|  |  |  |       where.username = username.toLowerCase(); | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |       where.email = email ? email.toLowerCase() : email; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2016-04-14 17:07:40 -07:00
										 |  |  |     debug('where', where); | 
					
						
							| 
									
										
										
										
											2015-12-28 13:52:28 -08:00
										 |  |  |     return User.count(where) | 
					
						
							|  |  |  |     .then(count => count > 0); | 
					
						
							| 
									
										
										
										
											2015-06-11 19:11:07 -04:00
										 |  |  |   }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   User.remoteMethod( | 
					
						
							|  |  |  |     'doesExist', | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       description: 'checks whether a user exists using email or username', | 
					
						
							|  |  |  |       accepts: [ | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |           arg: 'username', | 
					
						
							|  |  |  |           type: 'string' | 
					
						
							|  |  |  |         }, | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |           arg: 'email', | 
					
						
							|  |  |  |           type: 'string' | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       ], | 
					
						
							|  |  |  |       returns: [ | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |           arg: 'exists', | 
					
						
							|  |  |  |           type: 'boolean' | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       ], | 
					
						
							|  |  |  |       http: { | 
					
						
							|  |  |  |         path: '/exists', | 
					
						
							|  |  |  |         verb: 'get' | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   ); | 
					
						
							| 
									
										
										
										
											2015-07-29 11:32:16 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |   User.about = function about(username, cb) { | 
					
						
							|  |  |  |     if (!username) { | 
					
						
							|  |  |  |       // Zalgo!!
 | 
					
						
							| 
									
										
										
										
											2015-07-29 15:00:24 -07:00
										 |  |  |       return nextTick(() => { | 
					
						
							| 
									
										
										
										
											2015-08-04 10:52:41 -07:00
										 |  |  |         cb(new TypeError( | 
					
						
							|  |  |  |             `username should be a string but got ${ username }` | 
					
						
							|  |  |  |         )); | 
					
						
							| 
									
										
										
										
											2015-07-29 11:32:16 -07:00
										 |  |  |       }); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2016-04-14 17:07:40 -07:00
										 |  |  |     return User.findOne({ where: { username } }, (err, user) => { | 
					
						
							|  |  |  |       if (err) { | 
					
						
							|  |  |  |         return cb(err); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       if (!user || user.username !== username) { | 
					
						
							|  |  |  |         return cb(new Error(`no user found for ${ username }`)); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       const aboutUser = getAboutProfile(user); | 
					
						
							|  |  |  |       return cb(null, aboutUser); | 
					
						
							|  |  |  |     }); | 
					
						
							| 
									
										
										
										
											2015-07-29 11:32:16 -07:00
										 |  |  |   }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   User.remoteMethod( | 
					
						
							|  |  |  |     'about', | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       description: 'get public info about user', | 
					
						
							|  |  |  |       accepts: [ | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |           arg: 'username', | 
					
						
							|  |  |  |           type: 'string' | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       ], | 
					
						
							|  |  |  |       returns: [ | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |           arg: 'about', | 
					
						
							|  |  |  |           type: 'object' | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       ], | 
					
						
							|  |  |  |       http: { | 
					
						
							|  |  |  |         path: '/about', | 
					
						
							|  |  |  |         verb: 'get' | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   ); | 
					
						
							| 
									
										
										
										
											2015-07-29 15:00:24 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-04-22 02:17:59 +05:30
										 |  |  |   User.prototype.updateEmail = function updateEmail(email) { | 
					
						
							| 
									
										
										
										
											2016-06-02 15:23:49 -07:00
										 |  |  |     const fiveMinutesAgo = moment().subtract(5, 'minutes'); | 
					
						
							|  |  |  |     const lastEmailSentAt = moment(new Date(this.emailVerifyTTL || null)); | 
					
						
							|  |  |  |     const ownEmail = email === this.email; | 
					
						
							|  |  |  |     const isWaitPeriodOver = this.emailVerifyTTL ? | 
					
						
							|  |  |  |       lastEmailSentAt.isBefore(fiveMinutesAgo) : | 
					
						
							|  |  |  |       true; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-19 16:36:34 -07:00
										 |  |  |     if (!isEmail('' + email)) { | 
					
						
							|  |  |  |       return Observable.throw( | 
					
						
							| 
									
										
										
										
											2016-06-02 15:23:49 -07:00
										 |  |  |         new Error('The submitted email not valid.') | 
					
						
							| 
									
										
										
										
											2016-05-12 15:48:34 -07:00
										 |  |  |       ); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2016-06-02 15:23:49 -07:00
										 |  |  |     // email is already associated and verified with this account
 | 
					
						
							|  |  |  |     if (ownEmail && this.emailVerified) { | 
					
						
							| 
									
										
										
										
											2016-07-19 16:36:34 -07:00
										 |  |  |       return Observable.throw(new Error( | 
					
						
							| 
									
										
										
										
											2016-04-22 02:17:59 +05:30
										 |  |  |         `${email} is already associated with this account.` | 
					
						
							| 
									
										
										
										
											2016-05-02 17:20:41 -07:00
										 |  |  |       )); | 
					
						
							| 
									
										
										
										
											2016-04-22 02:17:59 +05:30
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2016-06-02 15:23:49 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |     if (ownEmail && !isWaitPeriodOver) { | 
					
						
							|  |  |  |       const minutesLeft = 5 - | 
					
						
							|  |  |  |         (moment().minutes() - lastEmailSentAt.minutes()); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       const timeToWait = minutesLeft ? | 
					
						
							|  |  |  |         `${minutesLeft} minute${minutesLeft > 1 ? 's' : ''}` : | 
					
						
							|  |  |  |         'a few seconds'; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-19 16:36:34 -07:00
										 |  |  |       return Observable.throw(new Error( | 
					
						
							| 
									
										
										
										
											2016-06-02 15:23:49 -07:00
										 |  |  |         `Please wait ${timeToWait} to resend email verification.` | 
					
						
							|  |  |  |       )); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-19 16:36:34 -07:00
										 |  |  |     return Observable.fromPromise(User.doesExist(null, email)) | 
					
						
							|  |  |  |       .flatMap(exists => { | 
					
						
							| 
									
										
										
										
											2016-06-02 15:23:49 -07:00
										 |  |  |         // not associated with this account, but is associated with another
 | 
					
						
							|  |  |  |         if (!ownEmail && exists) { | 
					
						
							| 
									
										
										
										
											2016-05-02 17:20:41 -07:00
										 |  |  |           return Promise.reject( | 
					
						
							|  |  |  |             new Error(`${email} is already associated with another account.`) | 
					
						
							|  |  |  |           ); | 
					
						
							| 
									
										
										
										
											2016-04-22 02:17:59 +05:30
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2016-05-07 17:46:39 +05:30
										 |  |  | 
 | 
					
						
							|  |  |  |         const emailVerified = false; | 
					
						
							|  |  |  |         return this.update$({ | 
					
						
							| 
									
										
										
										
											2016-06-02 15:23:49 -07:00
										 |  |  |           email, | 
					
						
							|  |  |  |           emailVerified, | 
					
						
							|  |  |  |           emailVerifyTTL: new Date() | 
					
						
							| 
									
										
										
										
											2016-05-07 17:46:39 +05:30
										 |  |  |         }) | 
					
						
							|  |  |  |         .do(() => { | 
					
						
							|  |  |  |           this.email = email; | 
					
						
							|  |  |  |           this.emailVerified = emailVerified; | 
					
						
							| 
									
										
										
										
											2016-06-02 15:23:49 -07:00
										 |  |  |           this.emailVerifyTTL = new Date(); | 
					
						
							| 
									
										
										
										
											2016-07-19 16:36:34 -07:00
										 |  |  |         }); | 
					
						
							|  |  |  |       }) | 
					
						
							|  |  |  |       .flatMap(() => { | 
					
						
							|  |  |  |         const mailOptions = { | 
					
						
							|  |  |  |           type: 'email', | 
					
						
							|  |  |  |           to: email, | 
					
						
							|  |  |  |           from: 'Team@freecodecamp.com', | 
					
						
							| 
									
										
										
										
											2017-01-14 19:49:01 -05:00
										 |  |  |           subject: 'Welcome to freeCodeCamp!', | 
					
						
							| 
									
										
										
										
											2016-07-19 16:36:34 -07:00
										 |  |  |           protocol: isDev ? null : 'https', | 
					
						
							|  |  |  |           host: isDev ? 'localhost' : 'freecodecamp.com', | 
					
						
							|  |  |  |           port: isDev ? null : 443, | 
					
						
							|  |  |  |           template: path.join( | 
					
						
							|  |  |  |             __dirname, | 
					
						
							|  |  |  |             '..', | 
					
						
							|  |  |  |             '..', | 
					
						
							|  |  |  |             'server', | 
					
						
							|  |  |  |             'views', | 
					
						
							|  |  |  |             'emails', | 
					
						
							|  |  |  |             'user-email-verify.ejs' | 
					
						
							|  |  |  |           ) | 
					
						
							|  |  |  |         }; | 
					
						
							|  |  |  |         return this.verify(mailOptions); | 
					
						
							|  |  |  |       }) | 
					
						
							|  |  |  |       .map(() => dedent`
 | 
					
						
							|  |  |  |         Please check your email. | 
					
						
							|  |  |  |         We sent you a link that you can click to verify your email address. | 
					
						
							|  |  |  |       `);
 | 
					
						
							| 
									
										
										
										
											2016-04-22 02:17:59 +05:30
										 |  |  |   }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-07-29 15:00:24 -07:00
										 |  |  |   User.giveBrowniePoints = | 
					
						
							| 
									
										
										
										
											2015-08-01 20:08:32 -07:00
										 |  |  |     function giveBrowniePoints(receiver, giver, data = {}, dev = false, cb) { | 
					
						
							| 
									
										
										
										
											2016-04-14 17:07:40 -07:00
										 |  |  |       const findUser = observeMethod(User, 'findOne'); | 
					
						
							| 
									
										
										
										
											2015-07-29 15:00:24 -07:00
										 |  |  |       if (!receiver) { | 
					
						
							|  |  |  |         return nextTick(() => { | 
					
						
							| 
									
										
										
										
											2015-07-31 13:45:21 -07:00
										 |  |  |           cb( | 
					
						
							|  |  |  |             new TypeError(`receiver should be a string but got ${ receiver }`) | 
					
						
							|  |  |  |           ); | 
					
						
							| 
									
										
										
										
											2015-07-29 15:00:24 -07:00
										 |  |  |         }); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       if (!giver) { | 
					
						
							|  |  |  |         return nextTick(() => { | 
					
						
							| 
									
										
										
										
											2015-07-31 13:45:21 -07:00
										 |  |  |           cb(new TypeError(`giver should be a string but got ${ giver }`)); | 
					
						
							| 
									
										
										
										
											2015-07-29 15:00:24 -07:00
										 |  |  |         }); | 
					
						
							|  |  |  |       } | 
					
						
							| 
									
										
										
										
											2015-07-31 12:45:34 -07:00
										 |  |  |       let temp = moment(); | 
					
						
							|  |  |  |       const browniePoints = temp | 
					
						
							|  |  |  |         .subtract.apply(temp, BROWNIEPOINTS_TIMEOUT) | 
					
						
							|  |  |  |         .valueOf(); | 
					
						
							| 
									
										
										
										
											2016-04-14 17:07:40 -07:00
										 |  |  |       const user$ = findUser({ where: { username: receiver }}); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       return user$ | 
					
						
							|  |  |  |         .tapOnNext((user) => { | 
					
						
							| 
									
										
										
										
											2015-07-29 15:00:24 -07:00
										 |  |  |           if (!user) { | 
					
						
							| 
									
										
										
										
											2015-07-31 13:45:21 -07:00
										 |  |  |             throw new Error(`could not find receiver for ${ receiver }`); | 
					
						
							| 
									
										
										
										
											2015-07-29 15:00:24 -07:00
										 |  |  |           } | 
					
						
							|  |  |  |         }) | 
					
						
							|  |  |  |         .flatMap(({ progressTimestamps = [] }) => { | 
					
						
							| 
									
										
										
										
											2016-04-14 17:07:40 -07:00
										 |  |  |           return Observable.from(progressTimestamps); | 
					
						
							| 
									
										
										
										
											2015-07-29 15:00:24 -07:00
										 |  |  |         }) | 
					
						
							|  |  |  |         // filter out non objects
 | 
					
						
							|  |  |  |         .filter((timestamp) => !!timestamp || typeof timestamp === 'object') | 
					
						
							|  |  |  |         // filterout timestamps older then an hour
 | 
					
						
							| 
									
										
										
										
											2016-04-14 17:07:40 -07:00
										 |  |  |         .filter(({ timestamp = 0 }) => { | 
					
						
							|  |  |  |           return timestamp >= browniePoints; | 
					
						
							|  |  |  |         }) | 
					
						
							| 
									
										
										
										
											2015-07-29 15:00:24 -07:00
										 |  |  |         // filter out brownie points given by giver
 | 
					
						
							| 
									
										
										
										
											2016-04-14 17:07:40 -07:00
										 |  |  |         .filter((browniePoint) => { | 
					
						
							|  |  |  |           return browniePoint.giver === giver; | 
					
						
							|  |  |  |         }) | 
					
						
							| 
									
										
										
										
											2015-07-29 15:00:24 -07:00
										 |  |  |         // no results means this is the first brownie point given by giver
 | 
					
						
							|  |  |  |         // so return -1 to indicate receiver should receive point
 | 
					
						
							| 
									
										
										
										
											2015-10-15 00:33:45 -07:00
										 |  |  |         .first({ defaultValue: -1 }) | 
					
						
							| 
									
										
										
										
											2016-04-14 17:07:40 -07:00
										 |  |  |         .flatMap((browniePointsFromGiver) => { | 
					
						
							| 
									
										
										
										
											2015-07-29 15:00:24 -07:00
										 |  |  |           if (browniePointsFromGiver === -1) { | 
					
						
							| 
									
										
										
										
											2016-04-14 17:07:40 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |             return user$.flatMap((user) => { | 
					
						
							|  |  |  |               user.progressTimestamps.push({ | 
					
						
							|  |  |  |                 giver, | 
					
						
							|  |  |  |                 timestamp: Date.now(), | 
					
						
							|  |  |  |                 ...data | 
					
						
							| 
									
										
										
										
											2015-07-29 15:00:24 -07:00
										 |  |  |               }); | 
					
						
							| 
									
										
										
										
											2016-04-14 17:07:40 -07:00
										 |  |  |               return saveUser(user); | 
					
						
							|  |  |  |             }); | 
					
						
							| 
									
										
										
										
											2015-07-29 15:00:24 -07:00
										 |  |  |           } | 
					
						
							|  |  |  |           return Observable.throw( | 
					
						
							| 
									
										
										
										
											2015-07-31 13:45:21 -07:00
										 |  |  |             new Error(`${ giver } already gave ${ receiver } points`) | 
					
						
							| 
									
										
										
										
											2015-07-29 15:00:24 -07:00
										 |  |  |           ); | 
					
						
							|  |  |  |         }) | 
					
						
							|  |  |  |         .subscribe( | 
					
						
							| 
									
										
										
										
											2016-04-14 17:07:40 -07:00
										 |  |  |           (user) => { | 
					
						
							|  |  |  |             return cb( | 
					
						
							|  |  |  |               null, | 
					
						
							|  |  |  |               getAboutProfile(user), | 
					
						
							|  |  |  |               dev ? | 
					
						
							|  |  |  |                 { giver, receiver, data } : | 
					
						
							|  |  |  |                 null | 
					
						
							|  |  |  |             ); | 
					
						
							|  |  |  |           }, | 
					
						
							|  |  |  |           (e) => cb(e, null, dev ? { giver, receiver, data } : null), | 
					
						
							| 
									
										
										
										
											2015-07-29 15:00:24 -07:00
										 |  |  |           () => { | 
					
						
							| 
									
										
										
										
											2016-04-14 17:07:40 -07:00
										 |  |  |             debug('brownie points assigned completed'); | 
					
						
							| 
									
										
										
										
											2015-07-29 15:00:24 -07:00
										 |  |  |           } | 
					
						
							|  |  |  |         ); | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   User.remoteMethod( | 
					
						
							|  |  |  |     'giveBrowniePoints', | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       description: 'Give this user brownie points', | 
					
						
							|  |  |  |       accepts: [ | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |           arg: 'receiver', | 
					
						
							|  |  |  |           type: 'string', | 
					
						
							|  |  |  |           required: true | 
					
						
							|  |  |  |         }, | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |           arg: 'giver', | 
					
						
							|  |  |  |           type: 'string', | 
					
						
							|  |  |  |           required: true | 
					
						
							|  |  |  |         }, | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |           arg: 'data', | 
					
						
							|  |  |  |           type: 'object' | 
					
						
							| 
									
										
										
										
											2015-08-01 20:08:32 -07:00
										 |  |  |         }, | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |           arg: 'debug', | 
					
						
							|  |  |  |           type: 'boolean' | 
					
						
							| 
									
										
										
										
											2015-07-29 15:00:24 -07:00
										 |  |  |         } | 
					
						
							|  |  |  |       ], | 
					
						
							|  |  |  |       returns: [ | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |           arg: 'about', | 
					
						
							|  |  |  |           type: 'object' | 
					
						
							| 
									
										
										
										
											2015-08-01 20:08:32 -07:00
										 |  |  |         }, | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |           arg: 'debug', | 
					
						
							|  |  |  |           type: 'object' | 
					
						
							| 
									
										
										
										
											2015-07-29 15:00:24 -07:00
										 |  |  |         } | 
					
						
							|  |  |  |       ], | 
					
						
							|  |  |  |       http: { | 
					
						
							|  |  |  |         path: '/give-brownie-points', | 
					
						
							| 
									
										
										
										
											2015-08-01 20:08:32 -07:00
										 |  |  |         verb: 'POST' | 
					
						
							| 
									
										
										
										
											2015-07-29 15:00:24 -07:00
										 |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2015-07-31 12:15:23 -07:00
										 |  |  |   ); | 
					
						
							| 
									
										
										
										
											2016-02-09 14:33:25 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-05-12 18:52:03 -07:00
										 |  |  |   User.themes = { | 
					
						
							|  |  |  |     night: true, | 
					
						
							|  |  |  |     default: true | 
					
						
							|  |  |  |   }; | 
					
						
							| 
									
										
										
										
											2016-05-07 17:46:39 +05:30
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-05-12 18:52:03 -07:00
										 |  |  |   User.prototype.updateTheme = function updateTheme(theme) { | 
					
						
							|  |  |  |     if (!this.constructor.themes[theme]) { | 
					
						
							|  |  |  |       const err = new Error( | 
					
						
							|  |  |  |         'Theme is not valid.' | 
					
						
							|  |  |  |       ); | 
					
						
							|  |  |  |       err.messageType = 'info'; | 
					
						
							|  |  |  |       err.userMessage = err.message; | 
					
						
							|  |  |  |       return Promise.reject(err); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return this.update$({ theme }) | 
					
						
							|  |  |  |       .map({ updatedTo: theme }) | 
					
						
							|  |  |  |       .toPromise(); | 
					
						
							|  |  |  |   }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-05 14:05:57 -07:00
										 |  |  |   // deprecated. remove once live
 | 
					
						
							| 
									
										
										
										
											2016-05-12 18:52:03 -07:00
										 |  |  |   User.remoteMethod( | 
					
						
							|  |  |  |     'updateTheme', | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |       isStatic: false, | 
					
						
							|  |  |  |       description: 'updates the users chosen theme', | 
					
						
							|  |  |  |       accepts: [ | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |           arg: 'theme', | 
					
						
							|  |  |  |           type: 'string', | 
					
						
							|  |  |  |           required: true | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       ], | 
					
						
							|  |  |  |       returns: [ | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |           arg: 'status', | 
					
						
							|  |  |  |           type: 'object' | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       ], | 
					
						
							|  |  |  |       http: { | 
					
						
							|  |  |  |         path: '/update-theme', | 
					
						
							|  |  |  |         verb: 'POST' | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   ); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-04-14 17:07:40 -07:00
										 |  |  |   // user.updateTo$(updateData: Object) => Observable[Number]
 | 
					
						
							| 
									
										
										
										
											2016-02-09 14:33:25 -08:00
										 |  |  |   User.prototype.update$ = function update$(updateData) { | 
					
						
							|  |  |  |     const id = this.getId(); | 
					
						
							|  |  |  |     const updateOptions = { allowExtendedOperators: true }; | 
					
						
							|  |  |  |     if ( | 
					
						
							|  |  |  |         !updateData || | 
					
						
							|  |  |  |         typeof updateData !== 'object' || | 
					
						
							| 
									
										
										
										
											2016-02-10 10:05:51 -08:00
										 |  |  |         !Object.keys(updateData).length | 
					
						
							| 
									
										
										
										
											2016-02-09 14:33:25 -08:00
										 |  |  |     ) { | 
					
						
							|  |  |  |       return Observable.throw(new Error( | 
					
						
							| 
									
										
										
										
											2016-02-10 10:05:51 -08:00
										 |  |  |         dedent`
 | 
					
						
							|  |  |  |           updateData must be an object with at least one key, | 
					
						
							|  |  |  |           but got ${updateData} with ${Object.keys(updateData).length} | 
					
						
							|  |  |  |         `.split('\n').join(' ')
 | 
					
						
							| 
									
										
										
										
											2016-02-09 14:33:25 -08:00
										 |  |  |       )); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return this.constructor.update$({ id }, updateData, updateOptions); | 
					
						
							|  |  |  |   }; | 
					
						
							| 
									
										
										
										
											2016-04-14 17:07:40 -07:00
										 |  |  |   User.prototype.getPoints$ = function getPoints$() { | 
					
						
							| 
									
										
										
										
											2016-04-06 21:08:19 -07:00
										 |  |  |     const id = this.getId(); | 
					
						
							|  |  |  |     const filter = { | 
					
						
							|  |  |  |       where: { id }, | 
					
						
							|  |  |  |       fields: { progressTimestamps: true } | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  |     return this.constructor.findOne$(filter) | 
					
						
							|  |  |  |       .map(user => { | 
					
						
							|  |  |  |         this.progressTimestamps = user.progressTimestamps; | 
					
						
							|  |  |  |         return user.progressTimestamps; | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  |   }; | 
					
						
							|  |  |  |   User.prototype.getChallengeMap$ = function getChallengeMap$() { | 
					
						
							|  |  |  |     const id = this.getId(); | 
					
						
							|  |  |  |     const filter = { | 
					
						
							|  |  |  |       where: { id }, | 
					
						
							|  |  |  |       fields: { challengeMap: true } | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  |     return this.constructor.findOne$(filter) | 
					
						
							|  |  |  |       .map(user => { | 
					
						
							|  |  |  |         this.challengeMap = user.challengeMap; | 
					
						
							|  |  |  |         return user.challengeMap; | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  |   }; | 
					
						
							| 
									
										
										
										
											2015-06-11 19:11:07 -04:00
										 |  |  | }; |