added emails for status changes

This commit is contained in:
Wyatt Johnson
2017-11-09 16:07:46 -07:00
parent d80401a49b
commit 401d8a423d
4 changed files with 204 additions and 45 deletions
+2 -37
View File
@@ -1,5 +1,4 @@
const errors = require('../../errors');
const UserModel = require('../../models/user');
const UsersService = require('../../services/users');
const {
CHANGE_USERNAME,
@@ -19,48 +18,14 @@ const setUserUsernameStatus = async (ctx, id, status) => {
};
const setUserBanStatus = async (ctx, id, status) => {
const user = await UserModel.findOneAndUpdate({id}, {
$set: {
'status.banned.status': status
},
$push: {
'status.banned.history': {
status,
assigned_by: ctx.user.id,
created_at: Date.now()
}
}
}, {
new: true
});
if (user === null) {
throw errors.ErrNotFound;
}
const user = await UsersService.setBanStatus(id, status, ctx.user.id);
if (user.banned) {
ctx.pubsub.publish('userBanned', user);
}
};
const setUserSuspensionStatus = async (ctx, id, until) => {
const user = await UserModel.findOneAndUpdate({id}, {
$set: {
'status.suspension.until': until
},
$push: {
'status.suspension.history': {
until,
assigned_by: ctx.user.id,
created_at: Date.now()
}
}
}, {
new: true
});
if (user === null) {
throw errors.ErrNotFound;
}
const user = await UsersService.setSuspensionStatus(id, until, ctx.user.id);
if (user.suspended) {
ctx.pubsub.publish('userSuspended', user);
}
+9 -4
View File
@@ -1,4 +1,9 @@
module.exports.ACTIONS_DELETE = 'actions.delete';
module.exports.ACTIONS_NEW = 'actions.new';
module.exports.COMMENTS_NEW = 'comments.new';
module.exports.COMMENTS_EDIT = 'comments.edit';
module.exports = {
ACTIONS_DELETE: 'ACTIONS_DELETE',
ACTIONS_NEW: 'ACTIONS_NEW',
COMMENTS_NEW: 'COMMENTS_NEW',
COMMENTS_EDIT: 'COMMENTS_EDIT',
USERS_SUSPENSION_CHANGE: 'USERS_SUSPENSION_CHANGE',
USERS_BAN_CHANGE: 'USERS_BAN_CHANGE',
USERS_USERNAME_STATUS_CHANGE: 'USERS_USERNAME_STATUS_CHANGE'
};
+12
View File
@@ -0,0 +1,12 @@
const ta = require('timeago.js');
ta.register('es', require('timeago.js/locales/es'));
ta.register('da', require('timeago.js/locales/da'));
ta.register('fr', require('timeago.js/locales/fr'));
ta.register('pt_BR', require('timeago.js/locales/pt_BR'));
const timeago = ta();
module.exports = (time) => {
return timeago.format(new Date(time), 'en');
};
+181 -4
View File
@@ -2,6 +2,15 @@ const uuid = require('uuid');
const bcrypt = require('bcryptjs');
const errors = require('../errors');
const some = require('lodash/some');
const merge = require('lodash/merge');
const events = require('./events');
const timeago = require('./timeago');
const {
USERS_SUSPENSION_CHANGE,
USERS_BAN_CHANGE,
USERS_USERNAME_STATUS_CHANGE,
} = require('./events/constants');
const {
ROOT_URL
@@ -24,6 +33,7 @@ const MailerService = require('./mailer');
const i18n = require('./i18n');
const Wordlist = require('./wordlist');
const DomainList = require('./domain_list');
const SettingsService = require('./settings');
const {escapeRegExp} = require('./regex');
const EMAIL_CONFIRM_JWT_SUBJECT = 'email_confirm';
@@ -39,7 +49,7 @@ const loginRateLimiter = new Limit('loginAttempts', RECAPTCHA_INCORRECT_TRIGGER,
// UsersService is the interface for the application to interact with the
// UserModel through.
module.exports = class UsersService {
class UsersService {
/**
* Returns a user (if found) for the given email address.
@@ -80,8 +90,92 @@ module.exports = class UsersService {
}
}
static async setSuspensionStatus(id, until, assignedBy = null) {
let user = await UserModel.findOneAndUpdate({id}, {
$set: {
'status.suspension.until': until
},
$push: {
'status.suspension.history': {
until,
assigned_by: assignedBy,
created_at: Date.now()
}
}
}, {
new: true
});
if (user === null) {
user = await UserModel.findOne({id});
if (user === null) {
throw errors.ErrNotFound;
}
if (
user.status.suspension.until === until ||
(
user.status.suspension.until.getTime() > until.getTime() - 1000 &&
user.status.suspension.until.getTime() < until.getTime() + 1000
)
) {
return user;
}
throw new Error('suspension status change edit failed for an unknown reason');
}
// Emit that the user username status was changed.
await events.emitAsync(USERS_SUSPENSION_CHANGE, user, until);
return user;
}
static async setBanStatus(id, status, assignedBy = null) {
let user = await UserModel.findOneAndUpdate({
id,
status: {
$ne: status
}
}, {
$set: {
'status.banned.status': status
},
$push: {
'status.banned.history': {
status,
assigned_by: assignedBy,
created_at: Date.now()
}
}
}, {
new: true
});
if (user === null) {
user = await UserModel.findOne({id});
if (user === null) {
throw errors.ErrNotFound;
}
if (user.status.banned.status === status) {
return user;
}
throw new Error('ban status change edit failed for an unknown reason');
}
// Emit that the user ban status was changed.
await events.emitAsync(USERS_BAN_CHANGE, user, status);
return user;
}
static async setUsernameStatus(id, status, assignedBy = null) {
const user = await UserModel.findOneAndUpdate({id}, {
let user = await UserModel.findOneAndUpdate({
id,
status: {
$ne: status
}
}, {
$set: {
'status.username.status': status
},
@@ -96,9 +190,21 @@ module.exports = class UsersService {
new: true
});
if (user === null) {
throw errors.ErrNotFound;
user = await UserModel.findOne({id});
if (user === null) {
throw errors.ErrNotFound;
}
if (user.status.username.status === status) {
return user;
}
throw new Error('username status change edit failed for an unknown reason');
}
// Emit that the user username status was changed.
await events.emitAsync(USERS_USERNAME_STATUS_CHANGE, user, status);
return user;
}
@@ -145,6 +251,9 @@ module.exports = class UsersService {
throw new Error('edit username failed for an unexpected reason');
}
// Emit that the user username status was changed.
await events.emitAsync(USERS_USERNAME_STATUS_CHANGE, user, toStatus);
return user;
} catch (err) {
if (err.code === 11000) {
@@ -297,6 +406,21 @@ module.exports = class UsersService {
});
}
static async sendEmail(user, options) {
const localProfile = user.profiles.find((profile) => profile.provider === 'local');
if (!localProfile) {
throw new Error('user does not have an email');
}
const {id: to} = localProfile;
options = merge(options, {
to,
});
return MailerService.sendSimple(options);
}
static async changePassword(id, password) {
const hashedPassword = await bcrypt.hash(password, SALT_ROUNDS);
@@ -789,7 +913,60 @@ module.exports = class UsersService {
}
});
}
};
}
module.exports = UsersService;
events.on(USERS_BAN_CHANGE, async (user, status) => {
// Check to see if the user was banned now and is currently banned.
if (user.banned && status) {
await UsersService.sendEmail(user, {
template: 'banned',
locals: {
body: 'In accordance with The Coral Projects community guidelines, your account has been banned. You are now longer allowed to comment, flag or engage with our community.'
},
subject: 'Your account has been banned',
});
}
});
events.on(USERS_SUSPENSION_CHANGE, async (user, until) => {
// Check to see if the user was suspended now and is currently suspended.
if (user.suspended && until !== null && until > Date.now()) {
const {organizationName} = await SettingsService.retrieve();
const message = i18n.t(
'suspenduser.email_message_suspend',
user.username,
organizationName,
timeago(until),
);
await UsersService.sendEmail(user, {
template: 'suspension',
locals: {
body: message,
},
subject: 'Your account has been banned',
});
}
});
events.on(USERS_USERNAME_STATUS_CHANGE, async (user, status) => {
if (status === 'REJECTED') {
const message = i18n.t('reject_username.email_message_reject');
await UsersService.sendEmail(user, {
template: 'suspension',
locals: {
body: message
},
subject: 'Username Rejected'
});
}
});
// Extract all the tokenUserNotFound plugins so we can integrate with other
// providers.