Skip to content

Instantly share code, notes, and snippets.

@emil-alexandrescu
Created February 26, 2019 16:35
Show Gist options
  • Save emil-alexandrescu/c0edef61870a7093f0108d1ad3eb47df to your computer and use it in GitHub Desktop.
Save emil-alexandrescu/c0edef61870a7093f0108d1ad3eb47df to your computer and use it in GitHub Desktop.
const _ = require('lodash');
const moment = require('moment');
const shortid = require('shortid');
const Promise = require('bluebird');
const User = require('../models/user');
const Team = require('../models/team');
const RoleService = require('../services/role.service');
const APIError = require('../utils/api-error');
const sendEmail = require('../utils/send-mail');
const config = require('../../config/config');
const { STATUS }
= require('../constants/user');
function getQuery(filterName, filterValue) {
return Promise.resolve({ [filterName]: filterValue });
}
function getSorts(sorts) {
return sorts.map(sort => sort.split(' '));
}
function revokeToken(user) {
user.revokeTokenBefore = new Date();
return user.save();
}
/**
* @param {Object} data
*/
function create(data) {
const newUser = new User(
_.pick(data, 'email', 'username', 'lastname', 'avatar', 'role', 'firstname', 'teams')
);
// explicitly set status to PENDING, since user hasn't accepted invitation yet
newUser.status = STATUS.PENDING;
return User.hash(shortid.generate())
.then((password) => {
newUser.password = password;
newUser.resetToken = shortid.generate();
newUser.resetExpiresIn = moment().add(config.resetPasswordExpiresIn, 'minutes').toDate();
return newUser.save();
})
.then((user) => {
if (data.teams) {
return Team.update(
{ _id: { $in: data.teams || [] } },
{ $addToSet: { members: user._id } },
{ multi: true }
).then(() => user);
}
return user;
})
.then(user =>
sendEmail({
to: [{ email: user.email, name: `${user.firstname} ${user.lastname}` }],
template: 'torrent-welcome',
emailVars: {
first_name: user.firstname,
last_name: user.lastname,
welcome_link: `${config.siteUrl}/reset-password?token=${user.resetToken}`
}
}).then(() => user)
);
}
/**
* @param {User} user
* @param {Object} data
*/
function update(user, data) {
let promise = Promise.resolve();
if (data.teams) {
promise = Team.update(
{},
{ $pull: { members: user._id } },
{ multi: true }
)
.then(() => Team.update(
{ _id: { $in: data.teams || [] } },
{ $addToSet: { members: user._id } },
{ multi: true }
));
}
if (data.password) {
promise = promise
.then(() => User.hash(data.password))
.then((password) => {
user.password = password;
});
}
// revoke tokens in such cases
if (data.role && data.role !== user.role._id.toString()) {
promise = promise.then(() => revokeToken(user));
} else if (data.password) {
promise = promise.then(() => revokeToken(user));
}
return promise
.then(() => {
Object.assign(user, data);
return user.save();
});
}
/**
* @param {User} user
*/
function remove(user) {
return user.update({
status: STATUS.DELETED,
name: `DELETED_ON_${new Date().getTime()}_${user.username}`
})
.then(result => Team.update(
{ _id: { $in: user.teams || [] } },
{ $pull: { members: user._id } },
{ multi: true }
).then(() => result));
}
/**
* @param {Number} page
* @param {Number} size
* @param {Object} filters
* @param {Array<String>} sorts
*/
function list(filters = {}, sorts = [], page = 0, size = 1000, currentUser) {
return Promise.all(
Object.keys(filters).map(
filterName => getQuery(filterName, filters[filterName])
).concat([RoleService.getManagableRoles(currentUser.role)])
)
.then((result) => {
const managableRoles = result.pop();
const queries = result;
const where = {
status: { $ne: STATUS.DELETED },
role: { $in: managableRoles.map(r => r._id || r) }
};
queries.forEach(query => Object.assign(where, query));
return Promise.all([
User.find(where)
.populate([{
path: 'role', select: '_id name permissions'
}, {
path: 'teams', select: 'name'
}])
.sort(getSorts(sorts))
.skip(page * size)
.limit(size)
.lean(),
User.count(where)
])
.spread((users, count) => ({
pagination: {
currentPage: page,
numPages: Math.ceil(count / size)
},
users
}));
});
}
/**
* @param username {string} - user username
* @returns {Promise}
**/
function findByUsername(username) {
return User.findOne({
username,
status: {
$ne: STATUS.DELETED
}
})
.populate([
{ path: 'role', select: '_id name permissions' },
{ path: 'teams', select: '_id name' }
])
.then((user) => {
if (!user) {
const err = new APIError('User with provided username not found', 401, true);
throw err;
}
return user;
});
}
module.exports = {
create,
update,
remove,
list,
findByUsername,
revokeToken
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment