mirror of
https://github.com/wassname/talk.git
synced 2026-06-28 22:04:50 +08:00
270 lines
7.3 KiB
JavaScript
270 lines
7.3 KiB
JavaScript
const {
|
|
ErrNotFound,
|
|
ErrNotAuthorized,
|
|
ErrPasswordIncorrect,
|
|
} = require('../../errors');
|
|
const Users = require('../../services/users');
|
|
const migrationHelpers = require('../../services/migration/helpers');
|
|
const {
|
|
CHANGE_USERNAME,
|
|
SET_USERNAME,
|
|
SET_USER_USERNAME_STATUS,
|
|
SET_USER_BAN_STATUS,
|
|
SET_USER_SUSPENSION_STATUS,
|
|
UPDATE_USER_ROLES,
|
|
DELETE_OTHER_USER,
|
|
CHANGE_PASSWORD,
|
|
} = require('../../perms/constants');
|
|
|
|
const setUserUsernameStatus = async (ctx, id, status) => {
|
|
const user = await Users.setUsernameStatus(id, status, ctx.user.id);
|
|
if (status === 'REJECTED') {
|
|
ctx.pubsub.publish('usernameRejected', user);
|
|
} else if (status === 'APPROVED') {
|
|
ctx.pubsub.publish('usernameApproved', user);
|
|
}
|
|
};
|
|
|
|
const setUserBanStatus = async (ctx, id, status = false, message = null) => {
|
|
const user = await Users.setBanStatus(id, status, ctx.user.id, message);
|
|
if (user.banned) {
|
|
ctx.pubsub.publish('userBanned', user);
|
|
}
|
|
};
|
|
|
|
const setUserSuspensionStatus = async (
|
|
ctx,
|
|
id,
|
|
until = null,
|
|
message = null
|
|
) => {
|
|
const user = await Users.setSuspensionStatus(id, until, ctx.user.id, message);
|
|
if (user.suspended) {
|
|
ctx.pubsub.publish('userSuspended', user);
|
|
}
|
|
};
|
|
|
|
const ignoreUser = ({ user }, userToIgnore) => {
|
|
return Users.ignoreUsers(user.id, [userToIgnore.id]);
|
|
};
|
|
|
|
const stopIgnoringUser = ({ user }, userToStopIgnoring) => {
|
|
return Users.stopIgnoringUsers(user.id, [userToStopIgnoring.id]);
|
|
};
|
|
|
|
const changeUsername = async (ctx, id, username) => {
|
|
const user = await Users.changeUsername(id, username, ctx.user.id);
|
|
const previousUsername = ctx.user.username;
|
|
ctx.pubsub.publish('usernameChanged', { previousUsername, user });
|
|
return user;
|
|
};
|
|
|
|
const setUsername = async (ctx, id, username) => {
|
|
return Users.setUsername(id, username, ctx.user.id);
|
|
};
|
|
|
|
const setRole = (ctx, id, role) => {
|
|
return Users.setRole(id, role);
|
|
};
|
|
|
|
/**
|
|
* transforms a specific action to a removal action on the target model.
|
|
*/
|
|
const actionDecrTransformer = ({ item_id, action_type, group_id }) => {
|
|
const update = {
|
|
$inc: {
|
|
[`action_counts.${action_type.toLowerCase()}`]: -1,
|
|
},
|
|
};
|
|
|
|
if (group_id) {
|
|
// If the action had a groupID, also decrement that key.
|
|
update.$inc[
|
|
`action_counts.${action_type.toLowerCase()}_${group_id.toLowerCase()}`
|
|
] = -1;
|
|
}
|
|
|
|
return {
|
|
query: { id: item_id },
|
|
update,
|
|
};
|
|
};
|
|
|
|
// delUser will delete a given user with the specified id.
|
|
const delUser = async (ctx, id) => {
|
|
const { connectors: { models: { User, Action, Comment } } } = ctx;
|
|
|
|
// Find the user we're removing.
|
|
const user = await User.findOne({ id });
|
|
if (!user) {
|
|
throw new ErrNotFound();
|
|
}
|
|
|
|
// Get the query transformer we'll use to help batch process the user
|
|
// deletion.
|
|
const { transformSingleWithCursor } = migrationHelpers({
|
|
queryBatchSize: 10000,
|
|
updateBatchSize: 10000,
|
|
});
|
|
|
|
// Remove all actions against this users comments.
|
|
await transformSingleWithCursor(
|
|
Action.collection.find({ user_id: user.id, item_type: 'COMMENTS' }),
|
|
actionDecrTransformer,
|
|
Comment
|
|
);
|
|
|
|
// Remove all actions against users.
|
|
await transformSingleWithCursor(
|
|
Action.collection.find({ user_id: user.id, item_type: 'USERS' }),
|
|
actionDecrTransformer,
|
|
User
|
|
);
|
|
|
|
// Remove all the user's actions.
|
|
await Action.where({ user_id: user.id })
|
|
.setOptions({ multi: true })
|
|
.remove();
|
|
|
|
// Remove the user from all other user's ignore lists.
|
|
await User.update(
|
|
{ ignoresUsers: user.id },
|
|
{
|
|
$pull: { ignoresUsers: user.id },
|
|
},
|
|
{ multi: true }
|
|
);
|
|
|
|
// For each comment that the user has authored, purge the comment data from it
|
|
// and unset their id from those comments.
|
|
await transformSingleWithCursor(
|
|
Comment.collection.find({ author_id: user.id }),
|
|
({
|
|
id,
|
|
asset_id,
|
|
status,
|
|
parent_id,
|
|
reply_count,
|
|
created_at,
|
|
updated_at,
|
|
}) => ({
|
|
query: { id },
|
|
replace: {
|
|
id,
|
|
body: null,
|
|
body_history: [],
|
|
asset_id,
|
|
author_id: null,
|
|
status_history: [],
|
|
status,
|
|
parent_id,
|
|
reply_count,
|
|
action_counts: {},
|
|
tags: [],
|
|
metadata: {},
|
|
deleted_at: new Date(),
|
|
created_at,
|
|
updated_at,
|
|
},
|
|
}),
|
|
Comment
|
|
);
|
|
|
|
// Remove the user.
|
|
await user.remove();
|
|
};
|
|
|
|
const changeUserPassword = async (ctx, oldPassword, newPassword) => {
|
|
const {
|
|
user,
|
|
loaders: { Settings },
|
|
connectors: { services: { I18n } },
|
|
} = ctx;
|
|
|
|
// Verify the old password.
|
|
const validPassword = await user.verifyPassword(oldPassword);
|
|
if (!validPassword) {
|
|
throw new ErrPasswordIncorrect();
|
|
}
|
|
|
|
// Change the users password now.
|
|
await Users.changePassword(user.id, newPassword);
|
|
|
|
// Get some context for the email to be sent.
|
|
const { organizationName, organizationContactEmail } = await Settings.select(
|
|
'organizationName',
|
|
'organizationContactEmail'
|
|
);
|
|
|
|
// Send the password change email.
|
|
await Users.sendEmail(user, {
|
|
template: 'plain',
|
|
locals: {
|
|
body: I18n.t('email.password_change.body', organizationContactEmail),
|
|
},
|
|
subject: I18n.t('email.password_change.subject', organizationName),
|
|
});
|
|
};
|
|
|
|
module.exports = ctx => {
|
|
let mutators = {
|
|
User: {
|
|
changeUsername: () => Promise.reject(new ErrNotAuthorized()),
|
|
ignoreUser: () => Promise.reject(new ErrNotAuthorized()),
|
|
setRole: () => Promise.reject(new ErrNotAuthorized()),
|
|
setUserBanStatus: () => Promise.reject(new ErrNotAuthorized()),
|
|
setUserSuspensionStatus: () => Promise.reject(new ErrNotAuthorized()),
|
|
setUserUsernameStatus: () => Promise.reject(new ErrNotAuthorized()),
|
|
setUsername: () => Promise.reject(new ErrNotAuthorized()),
|
|
stopIgnoringUser: () => Promise.reject(new ErrNotAuthorized()),
|
|
del: () => Promise.reject(new ErrNotAuthorized()),
|
|
changePassword: () => Promise.reject(new ErrNotAuthorized()),
|
|
},
|
|
};
|
|
|
|
if (ctx.user) {
|
|
mutators.User.ignoreUser = action => ignoreUser(ctx, action);
|
|
mutators.User.stopIgnoringUser = action => stopIgnoringUser(ctx, action);
|
|
|
|
if (ctx.user.can(UPDATE_USER_ROLES)) {
|
|
mutators.User.setRole = (id, role) => setRole(ctx, id, role);
|
|
}
|
|
|
|
if (ctx.user.can(CHANGE_USERNAME)) {
|
|
mutators.User.changeUsername = (id, username) =>
|
|
changeUsername(ctx, id, username);
|
|
}
|
|
|
|
if (ctx.user.can(SET_USERNAME)) {
|
|
mutators.User.setUsername = (id, username) =>
|
|
setUsername(ctx, id, username);
|
|
}
|
|
|
|
if (ctx.user.can(SET_USER_USERNAME_STATUS)) {
|
|
mutators.User.setUserUsernameStatus = (id, status) =>
|
|
setUserUsernameStatus(ctx, id, status);
|
|
}
|
|
|
|
if (ctx.user.can(SET_USER_BAN_STATUS)) {
|
|
mutators.User.setUserBanStatus = (id, status, message) =>
|
|
setUserBanStatus(ctx, id, status, message);
|
|
}
|
|
|
|
if (ctx.user.can(SET_USER_SUSPENSION_STATUS)) {
|
|
mutators.User.setUserSuspensionStatus = (id, until, message) =>
|
|
setUserSuspensionStatus(ctx, id, until, message);
|
|
}
|
|
|
|
if (ctx.user.can(DELETE_OTHER_USER)) {
|
|
mutators.User.del = id => delUser(ctx, id);
|
|
}
|
|
|
|
if (ctx.user.can(CHANGE_PASSWORD)) {
|
|
mutators.User.changePassword = ({ oldPassword, newPassword }) =>
|
|
changeUserPassword(ctx, oldPassword, newPassword);
|
|
}
|
|
}
|
|
|
|
return mutators;
|
|
};
|