2017-12-12 17:40:07 -07:00
'use strict'
2017-07-04 10:09:28 -06:00
2017-12-12 17:40:07 -07:00
const slug = require ( 'slug' )
const xss = require ( 'xss' )
const zxcvbn = require ( 'zxcvbn' )
const moment = require ( 'moment' )
const mw = require ( '../middleware.js' )
const User = require ( '../models.js' ) . user
const mail = require ( '../mail.js' )
const env = require ( '../env/env.js' )
2018-01-19 14:23:43 -07:00
const debug = require ( 'debug' ) ( 'tracman-routes-settings' )
2017-12-12 17:40:07 -07:00
const router = require ( 'express' ) . Router ( )
2017-04-11 19:38:07 -06:00
// Settings form
router . route ( '/' )
2017-12-12 17:40:07 -07:00
. all ( mw . ensureAuth , ( req , res , next ) => {
next ( )
} )
// Get settings form
. get ( ( req , res ) => {
res . render ( 'settings' , { active : 'settings' } )
} )
// Set new settings
2018-01-20 20:45:25 -07:00
. post ( async ( req , res , next ) => {
2017-12-12 17:40:07 -07:00
// Validate email
2018-01-20 20:45:25 -07:00
const checkEmail = new Promise ( async ( resolve , reject ) => {
2017-12-12 17:40:07 -07:00
// Check validity
if ( ! mw . validateEmail ( req . body . email ) ) {
req . flash ( 'warning' , ` <u> ${ req . body . email } </u> is not a valid email address. ` )
resolve ( )
// Check if unchanged
2017-12-13 12:52:01 -07:00
} else if ( req . user . email === req . body . email ) resolve ( )
2017-12-12 17:40:07 -07:00
// Check uniqueness
2017-12-13 12:52:01 -07:00
else {
2018-01-20 20:45:25 -07:00
try {
let existingUser = await User . findOne ( { email : req . body . email } )
2017-12-12 17:40:07 -07:00
// Not unique!
if ( existingUser && existingUser . id !== req . user . id ) {
debug ( 'Email not unique!' )
2017-12-13 12:52:01 -07:00
req . flash ( 'warning' ,
` That email, <u> ${ req . body . email } </u>, is already in use by another user! `
)
2017-12-12 17:40:07 -07:00
resolve ( )
// It's unique
} else {
debug ( 'Email is unique' )
req . user . newEmail = req . body . email
// Create token
debug ( ` Creating email token... ` )
2018-01-20 20:45:25 -07:00
let token = await req . user . createEmailToken ( )
// Send token to user by email
debug ( ` Mailing new email token to ${ req . body . email } ... ` )
await mail . send ( {
to : ` " ${ req . user . name } " < ${ req . body . email } > ` ,
from : mail . noReply ,
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 > `
)
} )
req . flash ( 'warning' ,
` An email has been sent to <u> ${ req . body . email } </u>. Check your inbox to confirm your new email address. `
2017-12-28 18:20:06 -07:00
)
2018-01-20 20:45:25 -07:00
resolve ( )
}
} catch ( err ) { reject ( ) }
2017-12-12 17:40:07 -07:00
}
} )
// Validate slug
2018-01-20 20:45:25 -07:00
const checkSlug = new Promise ( async ( resolve , reject ) => {
2017-12-12 17:40:07 -07:00
// Check existence
if ( req . body . slug === '' ) {
req . flash ( 'warning' , ` You must supply a slug. ` )
resolve ( )
// Check if unchanged
2017-12-13 12:52:01 -07:00
} else if ( req . user . slug === slug ( xss ( req . body . slug ) ) ) resolve ( )
2017-12-18 23:42:43 -07:00
2017-12-12 17:40:07 -07:00
// Check uniqueness
2017-12-13 12:52:01 -07:00
else {
2018-01-20 20:45:25 -07:00
try {
let existingUser = await User . findOne ( { slug : req . body . slug } )
2017-12-12 17:40:07 -07:00
// Not unique!
if ( existingUser && existingUser . id !== req . user . id ) {
2017-12-18 23:42:43 -07:00
req . flash ( 'warning' ,
2017-12-13 12:52:01 -07:00
` That slug, <u> ${ req . body . slug } </u>, is already in use by another user! `
)
2017-12-12 17:40:07 -07:00
// It's unique
2017-12-13 12:52:01 -07:00
} else req . user . slug = slug ( xss ( req . body . slug ) )
2018-01-20 20:45:25 -07:00
resolve ( )
} catch ( err ) { reject ( ) }
2017-12-12 17:40:07 -07:00
}
} )
// Set settings when done
2018-01-20 20:45:25 -07:00
try {
await Promise . all ( [ checkEmail , checkSlug ] )
2017-12-12 17:40:07 -07:00
debug ( 'Setting settings... ' )
// Set values
req . user . name = xss ( req . body . name )
req . user . settings = {
units : req . body . units ,
defaultMap : req . body . map ,
defaultZoom : req . body . zoom ,
showScale : ! ! ( req . body . showScale ) ,
showSpeed : ! ! ( req . body . showSpeed ) ,
showAlt : ! ! ( req . body . showAlt ) ,
showStreetview : ! ! ( req . body . showStreet ) ,
marker : req . body . marker
}
// Save user and send response
debug ( ` Saving new settings for user ${ req . user . name } ... ` )
2018-01-20 20:45:25 -07:00
await req . user . save ( )
debug ( ` DONE! Redirecting user... ` )
req . flash ( 'success' , 'Settings updated. ' )
} catch ( err ) { mw . throwErr ( err , req ) }
finally { res . redirect ( '/settings' ) }
2017-12-12 17:40:07 -07:00
} )
2017-04-11 19:38:07 -06:00
2017-05-06 23:59:21 -06:00
// Delete account
2018-01-20 20:45:25 -07:00
router . get ( '/delete' , async ( req , res ) => {
try {
await User . findByIdAndRemove ( req . user )
2017-12-12 17:40:07 -07:00
req . flash ( 'success' , 'Your account has been deleted. ' )
res . redirect ( '/' )
2018-01-20 20:45:25 -07:00
} catch ( err ) {
2017-12-12 17:40:07 -07:00
mw . throwErr ( err , req )
res . redirect ( '/settings' )
2018-01-20 20:45:25 -07:00
}
2017-12-12 17:40:07 -07:00
} )
2017-05-06 23:59:21 -06:00
2017-04-18 01:08:57 -06:00
// Confirm email address
2018-01-20 20:45:25 -07:00
router . get ( '/email/:token' , mw . ensureAuth , async ( req , res , next ) => {
2017-12-12 17:40:07 -07:00
// Check token
if ( req . user . emailToken === req . params . token ) {
2018-01-20 20:45:25 -07:00
try {
// Set new email
req . user . email = req . user . newEmail
2017-12-12 17:40:07 -07:00
2018-01-20 20:45:25 -07:00
// Delete token and newEmail
2017-12-12 17:40:07 -07:00
req . user . emailToken = undefined
req . user . newEmail = undefined
2018-01-20 20:45:25 -07:00
await req . user . save ( )
// Report success
2017-12-12 17:40:07 -07:00
req . flash ( 'success' , ` Your email has been set to <u> ${ req . user . email } </u>. ` )
res . redirect ( '/settings' )
2018-01-20 20:45:25 -07:00
} catch ( err ) {
2017-12-12 17:40:07 -07:00
mw . throwErr ( err , req )
2018-01-22 15:05:02 -07:00
res . redirect ( '/settings' )
2018-01-20 20:45:25 -07:00
}
2017-12-12 17:40:07 -07:00
// 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-12-12 17:40:07 -07:00
. all ( mw . ensureAuth , ( req , res , next ) => {
next ( )
} )
// Email user a token, proceed at /password/:token
2018-01-20 20:45:25 -07:00
. get ( async ( req , res , next ) => {
2017-12-12 17:40:07 -07:00
// Create token for password change
2018-01-20 20:45:25 -07:00
try {
let [ token , expires ] = await req . user . createPassToken ( )
2017-12-28 18:20:06 -07: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.
2018-01-20 20:45:25 -07:00
return await mail . send ( {
2017-12-28 18:20:06 -07:00
to : mail . to ( req . user ) ,
from : mail . noReply ,
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 \ nTo 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 > `
)
} )
2018-01-20 20:45:25 -07:00
2017-12-28 18:20:06 -07:00
// 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 } ) . `
)
2018-01-20 20:45:25 -07:00
} catch ( err ) {
2017-12-28 18:20:06 -07:00
mw . throwErr ( err , req )
2018-01-20 20:45:25 -07:00
} finally {
2017-12-28 18:20:06 -07:00
res . redirect ( ( req . user ) ? '/settings' : '/login' )
2018-01-20 20:45:25 -07:00
}
2017-12-12 17:40:07 -07: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-12-12 17:40:07 -07:00
// Check token
2018-01-20 20:45:25 -07:00
. all ( async ( req , res , next ) => {
2017-12-12 17:40:07 -07:00
debug ( '/settings/password/:token .all() called' )
2018-01-20 20:45:25 -07:00
try {
let user = await User
. findOne ( { 'auth.passToken' : req . params . token } )
. where ( 'auth.passTokenExpires' ) . gt ( Date . now ( ) )
if ( ! user ) {
debug ( 'Bad token' )
req . flash ( 'danger' , 'Password reset token is invalid or has expired. ' )
res . redirect ( ( req . isAuthenticated ) ? '/settings' : '/login' )
} else {
debug ( 'setting passwordUser' )
res . locals . passwordUser = user
next ( )
}
} catch ( err ) {
mw . throwErr ( err , req )
res . redirect ( '/password' )
}
2017-12-12 17:40:07 -07:00
} )
// Show password change form
. get ( ( req , res ) => {
debug ( '/settings/password/:token .get() called' )
res . render ( 'password' )
} )
// Set new password
2018-01-20 20:45:25 -07:00
. post ( async ( req , res , next ) => {
2017-12-18 23:42:43 -07:00
debug ( '/settings/password/:token .post() called' )
// Validate password strength
2017-12-12 17:40:07 -07:00
let zxcvbnResult = zxcvbn ( req . body . password )
if ( zxcvbnResult . crack _times _seconds . online _no _throttling _10 _per _second < 864000 ) { // Less than ten days
2017-12-18 23:42:43 -07:00
req . flash ( 'danger' ,
` That password could be cracked in ${ zxcvbnResult . crack _times _display . online _no _throttling _10 _per _second } ! Come up with a more complex password that would take at least 10 days to crack. `
)
2017-12-12 17:40:07 -07:00
res . redirect ( ` /settings/password/ ${ req . params . token } ` )
} else {
2017-12-18 23:42:43 -07:00
2017-12-12 17:40:07 -07:00
// Create hashed password and save to db
2018-01-20 20:45:25 -07:00
try {
await res . locals . passwordUser . generateHashedPassword ( req . body . password )
// User changed password
if ( req . user ) {
debug ( 'User saved password' )
req . flash ( 'success' , 'Your password has been changed. ' )
res . redirect ( '/settings' )
// New user created password
} else {
debug ( 'New user created password' )
req . flash ( 'success' , 'Password set. You can use it to log in now. ' )
2018-01-22 15:05:02 -07:00
res . redirect ( '/login' )
2018-01-20 20:45:25 -07:00
}
} catch ( err ) {
debug ( 'Error creating hashed password and saving to db' )
mw . throwErr ( err , req )
res . redirect ( ` /settings/password/ ${ req . params . token } ` )
}
2017-12-18 23:42:43 -07:00
2017-12-12 17:40:07 -07:00
}
} )
2017-04-11 19:38:07 -06:00
// Tracman pro
2017-04-12 11:41:27 -06:00
router . route ( '/pro' )
2017-12-12 17:40:07 -07:00
. all ( mw . ensureAuth , ( req , res , next ) => {
next ( )
} )
// Get info about pro
. get ( ( req , res , next ) => {
res . render ( 'pro' )
} )
// Join Tracman pro
2018-01-20 20:45:25 -07:00
. post ( async ( req , res ) => {
try {
let user = await User . findByIdAndUpdate ( req . user . id ,
2017-12-12 17:40:07 -07:00
{ $set : { isPro : true } } )
2018-01-20 20:45:25 -07:00
req . flash ( 'success' , 'You have been signed up for pro. ' )
res . redirect ( '/settings' )
} catch ( err ) {
mw . throwErr ( err , req )
res . redirect ( '/settings/pro' )
}
2017-12-12 17:40:07 -07:00
} )
module . exports = router