Initial commit
This commit is contained in:
339
server/src/api/routes/UserRoutes.js
Normal file
339
server/src/api/routes/UserRoutes.js
Normal 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))
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user