2017-12-12 17:40:07 -07:00
|
|
|
'use strict'
|
2017-04-01 11:03:31 -06:00
|
|
|
|
2017-12-12 17:40:07 -07:00
|
|
|
const mongoose = require('mongoose')
|
|
|
|
const unique = require('mongoose-unique-validator')
|
|
|
|
const bcrypt = require('bcrypt')
|
|
|
|
const crypto = require('crypto')
|
|
|
|
const debug = require('debug')('tracman-models')
|
2017-04-01 11:03:31 -06:00
|
|
|
|
|
|
|
const userSchema = new mongoose.Schema({
|
2017-12-12 17:40:07 -07:00
|
|
|
name: {type: String},
|
|
|
|
email: {type: String, unique: true},
|
|
|
|
newEmail: String,
|
|
|
|
emailToken: String,
|
|
|
|
slug: {type: String, required: true, unique: true},
|
|
|
|
auth: {
|
|
|
|
password: String,
|
|
|
|
passToken: String,
|
|
|
|
passTokenExpires: Date,
|
|
|
|
google: String,
|
|
|
|
facebook: String,
|
|
|
|
twitter: String
|
|
|
|
},
|
|
|
|
isAdmin: {type: Boolean, required: true, default: false},
|
|
|
|
isPro: {type: Boolean, required: true, default: false},
|
|
|
|
created: {type: Date, required: true},
|
|
|
|
lastLogin: Date,
|
|
|
|
settings: {
|
|
|
|
units: {type: String, default: 'standard'},
|
|
|
|
defaultMap: {type: String, default: 'road'},
|
|
|
|
defaultZoom: {type: Number, default: 11},
|
|
|
|
showScale: {type: Boolean, default: false},
|
|
|
|
showSpeed: {type: Boolean, default: false},
|
|
|
|
showTemp: {type: Boolean, default: false},
|
|
|
|
showAlt: {type: Boolean, default: false},
|
|
|
|
showStreetview: {type: Boolean, default: false},
|
|
|
|
marker: {type: String, default: 'red'}
|
|
|
|
},
|
|
|
|
last: {
|
|
|
|
time: Date,
|
|
|
|
lat: {type: Number, default: 0},
|
|
|
|
lon: {type: Number, default: 0},
|
|
|
|
dir: {type: Number, default: 0},
|
|
|
|
alt: {type: Number},
|
|
|
|
spd: {type: Number, default: 0}
|
|
|
|
},
|
|
|
|
sk32: {type: String, required: true, unique: true}
|
|
|
|
}).plugin(unique)
|
2017-04-09 21:40:08 -06:00
|
|
|
|
2017-12-12 17:40:07 -07:00
|
|
|
/* User methods */
|
2017-04-27 14:44:49 -06:00
|
|
|
|
2017-12-12 17:40:07 -07:00
|
|
|
// Create email confirmation token
|
2017-12-13 13:29:22 -07:00
|
|
|
userSchema.methods.createEmailToken = function (next) {
|
2017-12-12 17:40:07 -07:00
|
|
|
debug('user.createEmailToken() called')
|
|
|
|
var user = this
|
|
|
|
|
2017-12-13 13:29:22 -07:00
|
|
|
// Callback next(err, token)
|
|
|
|
if (typeof next === 'function') {
|
2017-12-12 17:40:07 -07:00
|
|
|
crypto.randomBytes(16, (err, buf) => {
|
2017-12-13 12:52:01 -07:00
|
|
|
if (err) return next(err)
|
2017-12-12 17:40:07 -07:00
|
|
|
if (buf) {
|
2017-12-13 13:29:22 -07:00
|
|
|
debug(`Buffer ${buf.toString('hex')} created`)
|
|
|
|
user.emailToken = buf.toString('hex')
|
2017-12-12 17:40:07 -07:00
|
|
|
user.save()
|
|
|
|
.then(() => {
|
2017-12-13 13:29:22 -07:00
|
|
|
return next(null, user.emailToken)
|
2017-12-12 17:40:07 -07:00
|
|
|
})
|
|
|
|
.catch((err) => {
|
2017-12-13 12:52:01 -07:00
|
|
|
return next(err)
|
2017-12-12 17:40:07 -07:00
|
|
|
})
|
|
|
|
}
|
|
|
|
})
|
2017-12-13 13:29:22 -07:00
|
|
|
|
|
|
|
// Promise
|
|
|
|
} else return new Promise((resolve, reject) => {
|
|
|
|
crypto.randomBytes(16, (err, buf) => {
|
|
|
|
if (err) reject(err)
|
|
|
|
if (buf) {
|
|
|
|
debug(`Buffer ${buf.toString('hex')} created`)
|
|
|
|
user.emailToken = buf.toString('hex')
|
|
|
|
user.save()
|
|
|
|
.then(() => {
|
|
|
|
resolve(user.emailToken)
|
|
|
|
})
|
|
|
|
.catch((err) => {
|
|
|
|
reject(err)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
})
|
|
|
|
})
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create password reset token
|
|
|
|
userSchema.methods.createPassToken = function (next) {
|
|
|
|
debug('user.createPassToken() called')
|
|
|
|
var user = this
|
|
|
|
|
|
|
|
// Callback next(err, token, expires)
|
|
|
|
if (typeof next === 'function') {
|
|
|
|
|
|
|
|
// Reuse old token, resetting clock
|
|
|
|
if (user.auth.passTokenExpires >= Date.now()) {
|
|
|
|
debug(`Reusing old password token...`)
|
|
|
|
user.auth.passTokenExpires = Date.now() + 3600000 // 1 hour
|
|
|
|
user.save()
|
|
|
|
.then(() => {
|
|
|
|
return next(null, user.auth.passToken, user.auth.passTokenExpires)
|
|
|
|
})
|
|
|
|
.catch((err) => {
|
|
|
|
return next(err)
|
|
|
|
})
|
|
|
|
|
|
|
|
// Create new token
|
|
|
|
} else {
|
|
|
|
debug(`Creating new password token...`)
|
|
|
|
crypto.randomBytes(16, (err, buf) => {
|
|
|
|
if (err) return next(err)
|
|
|
|
if (buf) {
|
|
|
|
user.auth.passToken = buf.toString('hex')
|
|
|
|
user.auth.passTokenExpires = Date.now() + 3600000 // 1 hour
|
|
|
|
user.save()
|
|
|
|
.then(() => {
|
|
|
|
debug('successfully saved user in createPassToken')
|
|
|
|
return next(null, user.auth.passToken, user.auth.passTokenExpires)
|
|
|
|
})
|
|
|
|
.catch((err) => {
|
|
|
|
debug('error saving user in createPassToken')
|
|
|
|
return next(err)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
// Promise
|
|
|
|
|
|
|
|
} else return new Promise((resolve, reject) => {
|
|
|
|
|
|
|
|
// Reuse old token, resetting clock
|
|
|
|
if (user.auth.passTokenExpires >= Date.now()) {
|
|
|
|
debug(`Reusing old password token...`)
|
|
|
|
user.auth.passTokenExpires = Date.now() + 3600000 // 1 hour
|
|
|
|
user.save()
|
|
|
|
.then(() => {
|
|
|
|
resolve(user.auth.passToken, user.auth.passTokenExpires)
|
|
|
|
})
|
|
|
|
.catch((err) => {
|
|
|
|
reject(err)
|
|
|
|
})
|
|
|
|
|
|
|
|
// Create new token
|
|
|
|
} else {
|
|
|
|
debug(`Creating new password token...`)
|
|
|
|
crypto.randomBytes(16, (err, buf) => {
|
|
|
|
if (err) return next(err)
|
|
|
|
if (buf) {
|
|
|
|
user.auth.passToken = buf.toString('hex')
|
|
|
|
user.auth.passTokenExpires = Date.now() + 3600000 // 1 hour
|
|
|
|
user.save()
|
|
|
|
.then(() => {
|
|
|
|
debug('successfully saved user in createPassToken')
|
|
|
|
resolve(user.auth.passToken, user.auth.passTokenExpires)
|
|
|
|
})
|
|
|
|
.catch((err) => {
|
|
|
|
debug('error saving user in createPassToken')
|
|
|
|
reject(err)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
})
|
2017-12-12 17:40:07 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// Generate hash for new password and save it to the database
|
|
|
|
userSchema.methods.generateHashedPassword = function (password, next) {
|
|
|
|
|
|
|
|
// Delete token
|
|
|
|
this.auth.passToken = undefined
|
|
|
|
this.auth.passTokenExpires = undefined
|
|
|
|
|
2017-12-13 13:29:22 -07:00
|
|
|
// Callback next(err, token, expires)
|
|
|
|
if (typeof next === 'function') {
|
|
|
|
|
|
|
|
// Generate hash
|
|
|
|
bcrypt.genSalt(8, (err, salt) => {
|
2017-12-13 12:52:01 -07:00
|
|
|
if (err) return next(err)
|
2017-12-13 13:29:22 -07:00
|
|
|
bcrypt.hash(password, salt, (err, hash) => {
|
|
|
|
if (err) return next(err)
|
|
|
|
this.auth.password = hash
|
|
|
|
this.save()
|
|
|
|
.then(next)
|
|
|
|
.catch((err) => next(err) )
|
|
|
|
})
|
2017-12-12 17:40:07 -07:00
|
|
|
})
|
2017-12-13 13:29:22 -07:00
|
|
|
|
|
|
|
} else return new Promise((resolve, reject) => {
|
|
|
|
|
|
|
|
// Generate hash
|
|
|
|
bcrypt.genSalt(8, (err, salt) => {
|
|
|
|
if (err) reject(err)
|
|
|
|
bcrypt.hash(password, salt, (err, hash) => {
|
|
|
|
if (err) reject(err)
|
|
|
|
this.auth.password = hash
|
|
|
|
this.save()
|
|
|
|
.then(resolve)
|
|
|
|
.catch( (err) => reject(err) )
|
|
|
|
})
|
|
|
|
})
|
|
|
|
|
2017-12-12 17:40:07 -07:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check for valid password
|
|
|
|
userSchema.methods.validPassword = function (password, next) {
|
2017-12-13 13:29:22 -07:00
|
|
|
// Callback next(err, res)
|
|
|
|
if (typeof next === 'function') bcrypt.compare(password, this.auth.password, next)
|
|
|
|
else bcrypt.compare(password, this.auth.password)
|
|
|
|
.then((result) => {
|
|
|
|
if (result===true) resolve()
|
|
|
|
else reject(new Error('Passwords don\'t match'))
|
|
|
|
})
|
|
|
|
.catch( (err) => reject(err) )
|
2017-04-09 21:40:08 -06:00
|
|
|
}
|
2017-04-01 11:03:31 -06:00
|
|
|
|
|
|
|
module.exports = {
|
2017-12-12 17:40:07 -07:00
|
|
|
'user': mongoose.model('User', userSchema)
|
|
|
|
}
|