Initial commit

This commit is contained in:
John Lyon-Smith
2018-02-22 17:57:27 -08:00
commit e80f5490d5
196 changed files with 38982 additions and 0 deletions

View File

@@ -0,0 +1,339 @@
import passport from 'passport'
import createError from 'http-errors'
import crypto from 'crypto'
import urlSafeBase64 from 'urlsafe-base64'
import url from 'url'
import util from 'util'
import autoBind from 'auto-bind2'
import config from 'config'
export class UserRoutes {
constructor(container) {
const app = container.app
this.log = container.log
this.db = container.db
this.mq = container.mq
this.ws = container.ws
this.maxEmailTokenAgeInHours = config.get('email.maxEmailTokenAgeInHours')
this.sendEmail = config.get('email.sendEmail')
autoBind(this)
app.route('/users')
.get(passport.authenticate('bearer', { session: false }), this.listUsers)
// Add a new user, send email confirmation email
.post(passport.authenticate('bearer', { session: false }), this.createUser)
.put(passport.authenticate('bearer', { session: false }), this.updateUser)
app.route('/users/brokers')
.get(passport.authenticate('bearer', { session: false }), this.listBrokerUsers)
app.route('/users/:_id([a-f0-9]{24})')
.get(passport.authenticate('bearer', { session: false }), this.getUser)
.delete(passport.authenticate('bearer', { session: false }), this.deleteUser)
app.route('/users/set-image')
.put(passport.authenticate('bearer', { session: false }), this.setImage)
app.route('/users/enter-room/:roomName')
.put(passport.authenticate('bearer', { session: false }), this.enterRoom)
app.route('/users/leave-room')
.put(passport.authenticate('bearer', { session: false }), this.leaveRoom)
}
listUsers(req, res, next) {
const User = this.db.User
const limit = req.params.limit || 20
const skip = req.params.skip || 0
const role = req.user.role
if (role !== 'executive' && role !== 'administrator') {
return next(new createError.Forbidden())
}
User.count({}).then((total) => {
let users = []
let cursor = User.find({}).limit(limit).skip(skip).cursor().map((doc) => {
return doc.toClient(req.user)
})
cursor.on('data', (doc) => {
users.push(doc)
})
cursor.on('end', () => {
res.json({
total: total,
offset: skip,
count: users.length,
items: users
})
})
cursor.on('error', (err) => {
next(createError.InternalServerError(err.message))
})
}).catch((err) => {
next(createError.InternalServerError(err.message))
})
}
listBrokerUsers(req, res, next) {
let User = this.db.User
const role = req.user.role
if (role !== 'executive' && role !== 'administrator') {
return next(new createError.Forbidden())
}
let users = []
let cursor = User.find({ role: 'broker' })
.select('_id firstName lastName thumbnailImageId t12 aum numHouseholds cellPhone').cursor()
cursor.on('data', (doc) => {
users.push(doc)
})
cursor.on('end', () => {
res.json({
total: users.length,
offset: 0,
count: users.length,
items: users
})
})
cursor.on('error', (err) => {
next(createError.InternalServerError(err.message))
})
}
getUser(req, res, next) {
let User = this.db.User
const _id = req.params._id
const isSelf = (_id === req.user._id)
// User can see themselves, otherwise must be super user
if (!isSelf && role !== 'executive' && role !== 'administrator') {
return next(new createError.Forbidden())
}
User.findById(_id).then((user) => {
if (!user) {
return Promise.reject(createError.NotFound(`User with _id ${_id} was not found`))
}
res.json(user.toClient(req.user))
}).catch((err) => {
if (err instanceof createError.HttpError) {
next(err)
} else {
next(createError.InternalServerError(err.message))
}
})
}
createUser(req, res, next) {
const role = req.user.role
if (role !== 'executive' && role !== 'administrator') {
return next(new createError.Forbidden())
}
let User = this.db.User
let user = new User(req.body)
// Add email confirmation required token
util.promisify(crypto.randomBytes)(32).then((buf) => {
user.emailToken = {
value: urlSafeBase64.encode(buf),
created: new Date()
}
return user.save()
}).then((savedUser) => {
const userFullName = `${savedUser.firstName} ${savedUser.lastName}`
const senderFullName = `${req.user.firstName} ${req.user.lastName}`
const siteUrl = url.parse(req.headers.referer)
const msg = {
toEmail: savedUser.email,
templateName: 'welcome',
templateData: {
recipientFullName: userFullName,
senderFullName: senderFullName,
confirmEmailLink: `${siteUrl.protocol}//${siteUrl.host}/confirm-email?email-token%3D${savedUser.emailToken.value}`,
confirmEmailLinkExpirationHours: this.maxEmailTokenAgeInHours,
supportEmail: this.supportEmail
}
}
res.json(savedUser.toClient())
return this.sendEmail ? this.mq.request('dar-email', 'sendEmail', msg) : Promise.resolve()
}).catch((err) => {
next(createError.InternalServerError(err.message))
})
}
updateUser(req, res, next) {
const role = req.user.role
// Do this here because Mongoose will add it automatically otherwise
if (!req.body._id) {
return next(createError.BadRequest('No user _id given in body'))
}
const isSelf = (req.body._id === req.user._id.toString())
// User can change themselves, otherwise must be super user
if (!isSelf && role !== 'executive' && role !== 'administrator') {
return next(new createError.Forbidden())
}
const User = this.db.User
let userUpdates = null
try {
userUpdates = new User(req.body)
} catch (err) {
return next(createError.BadRequest('Invalid data'))
}
if (isSelf && userUpdates.role && userUpdates.role !== req.user.role) {
return next(createError.BadRequest('Cannot modify own role'))
}
User.findById(userUpdates._id).then((foundUser) => {
if (!foundUser) {
return Promise.reject(createError.NotFound(`User with _id ${user._id} was not found`))
}
// We don't allow direct updates to the email field so remove it if present
delete userUpdates.email
foundUser.merge(userUpdates)
return foundUser.save()
}).then((savedUser) => {
res.json(savedUser.toClient(req.user))
}).catch((err) => {
next(createError.InternalServerError(err.message))
})
}
setImage(req, res, next) {
const role = req.user.role
const { _id, imageId } = req.body
if (!_id || !imageId) {
return next(createError.BadRequest('Must specify _id and imageId'))
}
const isSelf = (_id === req.user._id.toString())
// User can change themselves, otherwise must be super user
if (!isSelf && role !== 'executive' && role !== 'administrator') {
return next(new createError.Forbidden())
}
const { bigSize = {}, smallSize = {} } = req.body
Promise.all([
this.mq.request('dar-image', 'scaleImage', {
newWidth: bigSize.width || 200,
newHeight: bigSize.height || 200,
scaleMode: 'aspectFill',
inputAssetId: imageId,
passback: {
userId: _id,
rooms: [ req.user._id ],
routerName: 'userRoutes',
funcName: 'completeSetBigImage'
}
}),
this.mq.request('dar-image', 'scaleImage', {
newWidth: smallSize.width || 25,
newHeight: smallSize.height || 25,
scaleMode: 'aspectFill',
inputAssetId: imageId,
passback: {
userId: _id,
rooms: [ req.user._id, 'users' ],
routerName: 'userRoutes',
funcName: 'completeSetSmallImage'
}
})
]).then((correlationIds) => {
res.json({ correlationIds })
}).catch((err) => {
next(createError.InternalServerError('Unable to scale user images'))
})
}
completeSetBigImage(passback, error, data) {
if (error || !passback.userId || !passback.rooms) {
return
}
const User = this.db.User
User.findByIdAndUpdate(passback.userId, { imageId: data.outputAssetId }).then((foundUser) => {
if (foundUser) {
this.ws.notify(passback.rooms, 'newProfileImage', { imageId: data.outputAssetId })
}
}).catch((err) => {
this.log.error(`Unable to notify [${passback.rooms.join(', ')}] of new image'`)
})
}
completeSetSmallImage(passback, error, data) {
if (error || !passback.userId || !passback.rooms) {
return
}
const User = this.db.User
User.findByIdAndUpdate(passback.userId, { thumbnailImageId: data.outputAssetId }).then((foundUser) => {
if (foundUser) {
this.ws.notify(passback.rooms, 'newThumbnailImage', { imageId: data.outputAssetId })
}
}).catch((err) => {
this.log.error(`Unable to notify [${passback.rooms.join(', ')}] of new thumbnail image'`)
})
}
enterRoom(req, res, next) {
this.ws.enterRoom(req.user._id, req.params.roomName)
res.json({})
}
leaveRoom(req, res, next) {
this.ws.enterRoom(req.user._id)
res.json({})
}
deleteUser(req, res, next) {
const role = req.user.role
if (role !== 'executive' && role !== 'administrator') {
return new createError.Forbidden()
}
let User = this.db.User
const _id = req.params._id
User.remove({ _id }).then((deletedUser) => {
if (!deletedUser) {
return next(createError.NotFound(`User with _id ${_id} was not found`))
}
const userFullName = `${deletedUser.firstName} ${deletedUser.lastName}`
const senderFullName = `${req.user.firstName} ${req.user.lastName}`
const msg = {
toEmail: deletedUser.newEmail,
templateName: 'accountDeleted',
templateData: {
recipientFullName: userFullName,
senderFullName: senderFullName,
supportEmail: this.supportEmail
}
}
return this.sendEmail ? this.mq.request('dar-email', 'sendEmail', msg) : Promise.resolve()
}).then(() => {
res.json({})
}).catch((err) => {
next(createError.InternalServerError(err.message))
})
}
}