2017-04-11 19:38:07 -06:00
'use strict' ;
const slug = require ( 'slug' ) ,
xss = require ( 'xss' ) ,
2017-04-15 08:22:13 -06:00
mellt = require ( 'mellt' ) ,
2017-04-27 14:44:49 -06:00
moment = require ( 'moment' ) ,
2017-04-11 19:38:07 -06:00
mw = require ( '../middleware.js' ) ,
User = require ( '../models.js' ) . user ,
mail = require ( '../mail.js' ) ,
2017-04-26 21:13:14 -06:00
env = require ( '../env/env.js' ) ,
2017-05-06 21:25:32 -06:00
winston = require ( 'winston' ) ,
2017-04-11 19:38:07 -06:00
router = require ( 'express' ) . Router ( ) ;
2017-04-17 22:34:53 -06:00
// Validate email addresses
function validateEmail ( email ) {
var re = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/ ;
return re . test ( email ) ;
}
2017-04-11 19:38:07 -06:00
// Settings form
router . route ( '/' )
2017-04-13 16:53:18 -06:00
. all ( mw . ensureAuth , ( req , res , next ) => {
2017-04-11 19:38:07 -06:00
next ( ) ;
2017-04-13 16:53:18 -06:00
} )
2017-04-11 19:38:07 -06:00
// Get settings form
2017-04-14 20:10:52 -06:00
. get ( ( req , res ) => {
res . render ( 'settings' ) ;
2017-04-13 16:53:18 -06:00
} )
2017-04-11 19:38:07 -06:00
// Set new settings
2017-04-13 16:53:18 -06:00
. post ( ( req , res , next ) => {
2017-04-14 20:10:52 -06:00
2017-04-27 15:25:16 -06:00
// Validate email
const checkEmail = new Promise ( ( resolve , reject ) => {
// Check validity
if ( ! validateEmail ( req . body . email ) ) {
req . flash ( 'warning' , ` <u> ${ req . body . email } </u> is not a valid email address. ` ) ;
resolve ( ) ;
}
// Check if unchanged
else if ( req . user . email === req . body . email ) {
resolve ( ) ;
}
// Check uniqueness
else {
User . findOne ( { email : req . body . email } )
. then ( ( existingUser ) => {
// Not unique!
if ( existingUser && existingUser . id !== req . user . id ) {
2017-05-06 21:25:32 -06:00
winston . debug ( "Email not unique!" ) ;
2017-04-27 15:25:16 -06:00
req . flash ( 'warning' , ` That email, <u> ${ req . body . email } </u>, is already in use by another user! ` ) ;
resolve ( ) ;
}
// It's unique
else {
2017-05-06 21:25:32 -06:00
winston . debug ( "Email is unique" ) ;
2017-04-27 15:25:16 -06:00
req . user . newEmail = req . body . email ;
// Create token
2017-05-06 21:25:32 -06:00
winston . debug ( ` Creating email token... ` ) ;
2017-04-27 15:25:16 -06:00
req . user . createEmailToken ( ( err , token ) => {
if ( err ) { reject ( err ) ; }
// Send token to user by email
2017-05-06 21:25:32 -06:00
winston . debug ( ` Mailing new email token to ${ req . body . email } ... ` ) ;
2017-04-27 15:25:16 -06:00
mail . send ( {
to : ` " ${ req . user . name } " < ${ req . body . email } > ` ,
from : mail . from ,
subject : 'Confirm your new email address for Tracman' ,
text : mail . text ( ` A request has been made to change your Tracman email address. If you did not initiate this request, please disregard it. \n \n To confirm your email, follow this link: \n ${ env . url } /settings/email/ ${ token } . ` ) ,
html : mail . html ( ` <p>A request has been made to change your Tracman email address. If you did not initiate this request, please disregard it. </p><p>To confirm your email, follow this link:<br><a href=" ${ env . url } /settings/email/ ${ token } "> ${ env . url } /settings/email/ ${ token } </a>. </p> ` )
} )
. then ( ( ) => {
req . flash ( 'warning' , ` An email has been sent to <u> ${ req . body . email } </u>. Check your inbox to confirm your new email address. ` ) ;
resolve ( ) ;
} )
. catch ( reject ) ;
} ) ;
}
} )
. catch ( reject ) ;
}
} ) ;
// Validate slug
const checkSlug = new Promise ( ( resolve , reject ) => {
// Check existence
if ( req . body . slug === '' ) {
req . flash ( 'warning' , ` You must supply a slug. ` ) ;
resolve ( ) ;
}
// Check if unchanged
else if ( req . user . slug === slug ( xss ( req . body . slug ) ) ) {
resolve ( ) ;
}
// Check uniqueness
else {
User . findOne ( { slug : req . body . slug } )
. then ( ( existingUser ) => {
// Not unique!
if ( existingUser && existingUser . id !== req . user . id ) {
req . flash ( 'warning' , ` That slug, <u> ${ req . body . slug } </u>, is already in use by another user! ` ) ;
}
// It's unique
else {
req . user . slug = slug ( xss ( req . body . slug ) ) ;
}
} )
. then ( resolve )
. catch ( reject ) ;
}
} ) ;
// Set settings when done
Promise . all ( [ checkEmail , checkSlug ] )
. then ( ( ) => {
2017-05-06 21:25:32 -06:00
winston . debug ( 'Setting settings... ' ) ;
2017-04-18 11:03:24 -06:00
2017-04-19 19:37:00 -06:00
// Set values
req . user . name = xss ( req . body . name ) ;
req . user . settings = {
2017-05-02 07:21:53 -06:00
units : req . body . units ,
defaultMap : req . body . map ,
defaultZoom : req . body . zoom ,
showScale : ( req . body . showScale ) ? true : false ,
showSpeed : ( req . body . showSpeed ) ? true : false ,
showAlt : ( req . body . showAlt ) ? true : false ,
showStreetview : ( req . body . showStreet ) ? true : false
} ;
2017-04-26 20:47:23 -06:00
2017-04-25 18:01:35 -06:00
// Save user and send response
2017-05-06 21:25:32 -06:00
winston . debug ( ` Saving new settings for user ${ req . user . name } ... ` ) ;
2017-04-25 18:01:35 -06:00
req . user . save ( )
. then ( ( ) => {
2017-05-06 21:25:32 -06:00
winston . debug ( ` DONE! Redirecting user... ` ) ;
2017-04-25 18:01:35 -06:00
req . flash ( 'success' , 'Settings updated. ' ) ;
res . redirect ( '/settings' ) ;
} )
. catch ( ( err ) => {
mw . throwErr ( err , req ) ;
res . redirect ( '/settings' ) ;
} ) ;
2017-04-18 11:03:24 -06:00
2017-04-27 15:25:16 -06:00
} )
. catch ( ( err ) => {
mw . throwErr ( err , req ) ;
2017-04-18 11:10:43 -06:00
res . redirect ( '/settings' ) ;
2017-04-27 15:25:16 -06:00
} ) ;
2017-04-17 22:34:53 -06:00
2017-04-13 16:53:18 -06:00
} )
2017-04-11 19:38:07 -06:00
// Delete user account
2017-04-13 16:53:18 -06:00
. delete ( ( req , res , next ) => {
2017-04-14 20:10:52 -06:00
User . findByIdAndRemove ( req . user )
2017-04-18 01:08:57 -06:00
. then ( ( ) => {
2017-04-13 16:53:18 -06:00
req . flash ( 'success' , 'Your account has been deleted. ' ) ;
2017-04-12 11:41:27 -06:00
res . redirect ( '/' ) ;
2017-04-14 20:10:52 -06:00
} )
2017-04-18 01:08:57 -06:00
. catch ( ( err ) => {
2017-04-14 20:10:52 -06:00
mw . throwErr ( err , req ) ;
res . redirect ( '/settings' ) ;
} ) ;
2017-04-13 16:53:18 -06:00
} ) ;
2017-04-11 19:38:07 -06:00
2017-04-18 01:08:57 -06:00
// Confirm email address
router . get ( '/email/:token' , mw . ensureAuth , ( req , res , next ) => {
// Check token
if ( req . user . emailToken === req . params . token ) {
// Set new email
req . user . email = req . user . newEmail ;
2017-04-18 11:10:43 -06:00
req . user . save ( )
. then ( ( ) => {
2017-04-18 01:08:57 -06:00
// Delete token and newEmail
req . user . emailToken = undefined ;
req . user . newEmail = undefined ;
req . user . save ( ) ;
2017-04-20 21:07:35 -06:00
} )
. then ( ( ) => {
2017-04-18 01:08:57 -06:00
// Report success
req . flash ( 'success' , ` Your email has been set to <u> ${ req . user . email } </u>. ` ) ;
res . redirect ( '/settings' ) ;
} )
. catch ( ( err ) => {
mw . throwErr ( err , req ) ;
res . redirect ( req . session . next || '/settings' ) ;
} ) ;
}
// Invalid token
else {
req . flash ( 'danger' , 'Email confirmation token is invalid. ' ) ;
res . redirect ( '/settings' ) ;
}
} ) ;
2017-04-11 19:38:07 -06:00
// Set password
2017-04-14 20:10:52 -06:00
router . route ( '/password' )
2017-04-13 16:53:18 -06:00
. all ( mw . ensureAuth , ( req , res , next ) => {
2017-04-11 19:38:07 -06:00
next ( ) ;
2017-04-13 16:53:18 -06:00
} )
2017-04-12 11:41:27 -06:00
// Email user a token, proceed at /password/:token
2017-04-13 16:53:18 -06:00
. get ( ( req , res , next ) => {
2017-04-12 11:41:27 -06:00
// Create token for password change
2017-04-27 14:44:49 -06:00
req . user . createPassToken ( ( err , token , expires ) => {
2017-04-18 01:08:57 -06:00
if ( err ) {
mw . throwErr ( err , req ) ;
2017-04-27 14:44:49 -06:00
res . redirect ( ( req . user ) ? '/settings' : '/login' ) ;
2017-04-18 01:08:57 -06:00
}
2017-04-28 12:21:10 -06:00
else {
2017-04-18 01:08:57 -06:00
2017-04-28 12:21:10 -06:00
// Figure out expiration time
let expirationTimeString = ( req . query . tz ) ?
moment ( expires ) . utcOffset ( req . query . tz ) . toDate ( ) . toLocaleTimeString ( req . acceptsLanguages [ 0 ] ) :
moment ( expires ) . toDate ( ) . toLocaleTimeString ( req . acceptsLanguages [ 0 ] ) + " UTC" ;
// Confirm password change request by email.
mail . send ( {
to : mail . to ( req . user ) ,
from : mail . from ,
subject : 'Request to change your Tracman password' ,
text : mail . text ( ` A request has been made to change your tracman password. If you did not initiate this request, please contact support at keith@tracman.org. \n \n To change your password, follow this link: \n ${ env . url } /settings/password/ ${ token } . \n \n This request will expire at ${ expirationTimeString } . ` ) ,
html : mail . html ( ` <p>A request has been made to change your tracman password. If you did not initiate this request, please contact support at <a href="mailto:keith@tracman.org">keith@tracman.org</a>. </p><p>To change your password, follow this link:<br><a href=" ${ env . url } /settings/password/ ${ token } "> ${ env . url } /settings/password/ ${ token } </a>. </p><p>This request will expire at ${ expirationTimeString } . </p> ` )
} )
. then ( ( ) => {
// Alert user to check email.
req . flash ( 'success' , ` An link has been sent to <u> ${ req . user . email } </u>. Click on the link to complete your password change. This link will expire in one hour ( ${ expirationTimeString } ). ` ) ;
res . redirect ( ( req . user ) ? '/settings' : '/login' ) ;
} )
. catch ( ( err ) => {
mw . throwErr ( err , req ) ;
res . redirect ( ( req . user ) ? '/settings' : '/login' ) ;
} ) ;
2017-04-18 01:08:57 -06:00
2017-04-28 12:21:10 -06:00
}
2017-04-18 01:08:57 -06:00
} ) ;
2017-04-13 16:53:18 -06:00
} ) ;
2017-04-12 11:41:27 -06:00
router . route ( '/password/:token' )
2017-04-13 16:53:18 -06:00
2017-04-12 11:41:27 -06:00
// Check token
2017-04-13 16:53:18 -06:00
. all ( ( req , res , next ) => {
2017-04-12 11:41:27 -06:00
User
. findOne ( { 'auth.passToken' : req . params . token } )
2017-04-18 01:08:57 -06:00
. where ( 'auth.passTokenExpires' ) . gt ( Date . now ( ) )
2017-04-13 16:59:46 -06:00
. then ( ( user ) => {
2017-04-12 11:41:27 -06:00
if ( ! user ) {
req . flash ( 'danger' , 'Password reset token is invalid or has expired. ' ) ;
res . redirect ( ( req . isAuthenticated ) ? '/settings' : '/login' ) ;
} else {
res . locals . passwordUser = user ;
next ( ) ;
}
2017-04-14 20:10:52 -06:00
} )
. catch ( ( err ) => {
mw . throwErr ( err , req ) ;
res . redirect ( '/password' ) ;
2017-04-12 11:41:27 -06:00
} ) ;
2017-04-13 16:53:18 -06:00
} )
2017-04-12 11:41:27 -06:00
// Show password change form
2017-04-13 16:53:18 -06:00
. get ( ( req , res ) => {
2017-04-12 11:41:27 -06:00
res . render ( 'password' ) ;
2017-04-13 16:53:18 -06:00
} )
2017-04-15 08:22:13 -06:00
// Set new password
2017-04-13 16:53:18 -06:00
. post ( ( req , res , next ) => {
2017-04-14 20:10:52 -06:00
2017-04-15 08:22:13 -06:00
// Validate password
let daysToCrack = mellt . CheckPassword ( req . body . password ) ;
if ( daysToCrack < 10 ) {
mw . throwErr ( new Error ( ` That password could be cracked in ${ daysToCrack } days! Come up with a more complex password that would take at least 10 days to crack. ` ) ) ;
res . redirect ( ` /settings/password/ ${ req . params . token } ` ) ;
2017-04-16 15:23:15 -06:00
}
2017-04-25 15:22:23 -06:00
2017-04-16 15:23:15 -06:00
else {
2017-04-15 08:22:13 -06:00
2017-04-28 13:37:22 -06:00
// Create hashed password and save to db
res . locals . passwordUser . generateHashedPassword ( req . body . password , ( err ) => {
2017-04-15 08:22:13 -06:00
if ( err ) {
mw . throwErr ( err , req ) ;
res . redirect ( ` /password/ ${ req . params . token } ` ) ;
}
2017-04-28 13:37:22 -06:00
// User changed password
else if ( req . user ) {
req . flash ( 'success' , 'Your password has been changed. ' ) ;
res . redirect ( '/settings' ) ;
}
// New user created password
2017-04-15 08:22:13 -06:00
else {
2017-04-28 13:37:22 -06:00
req . flash ( 'success' , 'Password set. You can use it to log in now. ' ) ;
2017-05-02 07:38:16 -06:00
res . redirect ( '/login?next=/settings' ) ;
2017-04-15 08:22:13 -06:00
}
2017-04-28 13:37:22 -06:00
2017-04-15 08:22:13 -06:00
} ) ;
}
2017-04-14 20:10:52 -06:00
2017-04-13 16:53:18 -06:00
} ) ;
2017-04-11 19:38:07 -06:00
// Tracman pro
2017-04-12 11:41:27 -06:00
router . route ( '/pro' )
2017-04-13 16:53:18 -06:00
. all ( mw . ensureAuth , ( req , res , next ) => {
2017-04-11 19:38:07 -06:00
next ( ) ;
2017-04-13 16:53:18 -06:00
} )
2017-04-11 19:38:07 -06:00
// Get info about pro
2017-04-13 16:53:18 -06:00
. get ( ( req , res , next ) => {
2017-04-12 11:41:27 -06:00
res . render ( 'pro' ) ;
2017-04-13 16:53:18 -06:00
} )
2017-04-11 19:38:07 -06:00
// Join Tracman pro
2017-04-13 16:53:18 -06:00
. post ( ( req , res ) => {
2017-04-12 11:41:27 -06:00
User . findByIdAndUpdate ( req . user . id ,
2017-04-14 20:10:52 -06:00
{ $set : { isPro : true } } )
. then ( ( user ) => {
req . flash ( 'success' , 'You have been signed up for pro. ' ) ;
2017-04-25 15:22:23 -06:00
res . redirect ( req . session . next || '/settings' ) ;
2017-04-14 20:10:52 -06:00
} )
. catch ( ( err ) => {
mw . throwErr ( err , req ) ;
res . redirect ( '/pro' ) ;
} ) ;
2017-04-13 16:53:18 -06:00
} ) ;
2017-04-17 22:34:53 -06:00
module . exports = router ;