mirror of
https://github.com/wassname/talk.git
synced 2026-09-12 13:01:11 +08:00
initial pass at status support
This commit is contained in:
@@ -1,5 +1,4 @@
|
||||
const ActionsService = require('../../services/actions');
|
||||
const UsersService = require('../../services/users');
|
||||
const errors = require('../../errors');
|
||||
const {CREATE_ACTION, DELETE_ACTION} = require('../../perms/constants');
|
||||
|
||||
@@ -31,15 +30,6 @@ const createAction = async ({user = {}, pubsub, loaders: {Comments}}, {item_id,
|
||||
metadata
|
||||
});
|
||||
|
||||
if (item_type === 'USERS' && action_type === 'FLAG') {
|
||||
|
||||
// Set the user as pending if it was a user flag and user has no Admin, Staff or Moderation roles
|
||||
let user = await UsersService.findById(item_id);
|
||||
if(!user.isStaff()){
|
||||
await UsersService.setStatus(item_id, 'PENDING');
|
||||
}
|
||||
}
|
||||
|
||||
if (comment) {
|
||||
pubsub.publish('commentFlagged', comment);
|
||||
}
|
||||
|
||||
+88
-30
@@ -1,29 +1,82 @@
|
||||
const errors = require('../../errors');
|
||||
const UserModel = require('../../models/user');
|
||||
const UsersService = require('../../services/users');
|
||||
const {SET_USER_STATUS, SUSPEND_USER, REJECT_USERNAME} = require('../../perms/constants');
|
||||
const {
|
||||
SET_USER_USERNAME_STATUS,
|
||||
SET_USER_BAN_STATUS,
|
||||
SET_USER_SUSPENSION_STATUS,
|
||||
} = require('../../perms/constants');
|
||||
|
||||
const setUserStatus = async ({pubsub}, {id, status}) => {
|
||||
const result = await UsersService.setStatus(id, status);
|
||||
if (result && result.status === 'BANNED') {
|
||||
pubsub.publish('userBanned', result);
|
||||
const setUserUsernameStatus = async (ctx, id, status) => {
|
||||
const user = await UserModel.findOneAndUpdate({id}, {
|
||||
$set: {
|
||||
'status.username.status': status
|
||||
},
|
||||
$push: {
|
||||
'status.username.history': {
|
||||
status,
|
||||
assigned_by: ctx.user.id,
|
||||
created_at: Date.now()
|
||||
}
|
||||
}
|
||||
}, {
|
||||
new: true
|
||||
});
|
||||
if (user === null) {
|
||||
throw errors.ErrNotFound;
|
||||
}
|
||||
|
||||
if (status === 'REJECTED') {
|
||||
ctx.pubsub.publish('usernameRejected', user);
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
const suspendUser = async ({pubsub}, {id, message, until}) => {
|
||||
const result = await UsersService.suspendUser(id, message, until);
|
||||
if (result) {
|
||||
pubsub.publish('userSuspended', result);
|
||||
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;
|
||||
}
|
||||
|
||||
if (user.banned) {
|
||||
ctx.pubsub.publish('userBanned', user);
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
const rejectUsername = async ({pubsub}, {id, message}) => {
|
||||
const result = await UsersService.rejectUsername(id, message);
|
||||
if (result) {
|
||||
pubsub.publish('usernameRejected', result);
|
||||
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;
|
||||
}
|
||||
|
||||
if (user.suspended) {
|
||||
ctx.pubsub.publish('userSuspended', user);
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
const ignoreUser = ({user}, userToIgnore) => {
|
||||
@@ -34,27 +87,32 @@ const stopIgnoringUser = ({user}, userToStopIgnoring) => {
|
||||
return UsersService.stopIgnoringUsers(user.id, [userToStopIgnoring.id]);
|
||||
};
|
||||
|
||||
module.exports = (context) => {
|
||||
module.exports = (ctx) => {
|
||||
let mutators = {
|
||||
User: {
|
||||
setUserStatus: () => Promise.reject(errors.ErrNotAuthorized),
|
||||
suspendUser: () => Promise.reject(errors.ErrNotAuthorized),
|
||||
rejectUsername: () => Promise.reject(errors.ErrNotAuthorized),
|
||||
ignoreUser: (action) => ignoreUser(context, action),
|
||||
stopIgnoringUser: (action) => stopIgnoringUser(context, action),
|
||||
ignoreUser: () => Promise.reject(errors.ErrNotAuthorized),
|
||||
stopIgnoringUser: () => Promise.reject(errors.ErrNotAuthorized),
|
||||
setUserUsernameStatus: () => Promise.reject(errors.ErrNotAuthorized),
|
||||
setUserBanStatus: () => Promise.reject(errors.ErrNotAuthorized),
|
||||
setUserSuspensionStatus: () => Promise.reject(errors.ErrNotAuthorized),
|
||||
}
|
||||
};
|
||||
|
||||
if (context.user && context.user.can(SET_USER_STATUS)) {
|
||||
mutators.User.setUserStatus = (action) => setUserStatus(context, action);
|
||||
}
|
||||
if (ctx.user) {
|
||||
mutators.User.ignoreUser = (action) => ignoreUser(ctx, action);
|
||||
mutators.User.stopIgnoringUser = (action) => stopIgnoringUser(ctx, action);
|
||||
|
||||
if (context.user && context.user.can(SUSPEND_USER)) {
|
||||
mutators.User.suspendUser = (action) => suspendUser(context, action);
|
||||
}
|
||||
if (ctx.user.can(SET_USER_USERNAME_STATUS)) {
|
||||
mutators.User.setUserUsernameStatus = (id, status) => setUserUsernameStatus(ctx, id, status);
|
||||
}
|
||||
|
||||
if (context.user && context.user.can(REJECT_USERNAME)) {
|
||||
mutators.User.rejectUsername = (action) => rejectUsername(context, action);
|
||||
if (ctx.user.can(SET_USER_BAN_STATUS)) {
|
||||
mutators.User.setUserBanStatus = (id, status) => setUserBanStatus(ctx, id, status);
|
||||
}
|
||||
|
||||
if (ctx.user.can(SET_USER_SUSPENSION_STATUS)) {
|
||||
mutators.User.setUserSuspensionStatus = (id, until) => setUserSuspensionStatus(ctx, id, until);
|
||||
}
|
||||
}
|
||||
|
||||
return mutators;
|
||||
|
||||
@@ -19,14 +19,17 @@ const RootMutation = {
|
||||
deleteAction: async (_, {id}, {mutators: {Action}}) => {
|
||||
await Action.delete({id});
|
||||
},
|
||||
setUserStatus: async (_, {id, status}, {mutators: {User}}) => {
|
||||
await User.setUserStatus({id, status});
|
||||
approveUsername: async (_, {id}, {mutators: {User}}) => {
|
||||
await User.setUserUsernameStatus(id, 'APPROVED');
|
||||
},
|
||||
suspendUser: async (_, {input: {id, message, until}}, {mutators: {User}}) => {
|
||||
await User.suspendUser({id, message, until});
|
||||
rejectUsername: async (_, {id}, {mutators: {User}}) => {
|
||||
await User.setUserUsernameStatus(id, 'REJECTED');
|
||||
},
|
||||
rejectUsername: async (_, {input: {id, message}}, {mutators: {User}}) => {
|
||||
await User.rejectUsername({id, message});
|
||||
setUserSuspensionStatus: async (_, {input: {id, until}}, {mutators: {User}}) => {
|
||||
await User.setUserSuspensionStatus(id, until);
|
||||
},
|
||||
setUserBanStatus: async (_, {input: {id, status}}, {mutators: {User}}) => {
|
||||
await User.setUserBanStatus(id, status);
|
||||
},
|
||||
ignoreUser: async (_, {id}, {mutators: {User}}) => {
|
||||
await User.ignoreUser({id});
|
||||
|
||||
@@ -6,7 +6,6 @@ const {
|
||||
SEARCH_OTHERS_COMMENTS,
|
||||
UPDATE_USER_ROLES,
|
||||
SEARCH_COMMENT_METRICS,
|
||||
VIEW_SUSPENSION_INFO,
|
||||
LIST_OWN_TOKENS
|
||||
} = require('../../perms/constants');
|
||||
|
||||
@@ -84,12 +83,12 @@ const User = {
|
||||
}
|
||||
},
|
||||
|
||||
suspension({id, suspension}, _, {user}) {
|
||||
if (user.id !== id && !user.can(VIEW_SUSPENSION_INFO)) {
|
||||
return null;
|
||||
}
|
||||
return suspension;
|
||||
}
|
||||
// suspension({id, suspension}, _, {user}) {
|
||||
// if (user.id !== id && !user.can(VIEW_SUSPENSION_INFO)) {
|
||||
// return null;
|
||||
// }
|
||||
// return suspension;
|
||||
// }
|
||||
};
|
||||
|
||||
// Decorate the User type resolver with a tags field.
|
||||
|
||||
@@ -27,13 +27,13 @@ const setupFunctions = plugins.get('server', 'setupFunctions').reduce((acc, {plu
|
||||
commentAdded: {
|
||||
filter: (comment, context) => {
|
||||
|
||||
// Only priviledged users can subscribe to all assets.
|
||||
// Only privileged users can subscribe to all assets.
|
||||
if (!args.asset_id && (!context.user || !context.user.can(SUBSCRIBE_ALL_COMMENT_ADDED))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If user scubsscribes for statuses other than NONE and/or ACCEPTED statuses, it needs
|
||||
// special priviledges.
|
||||
// If user subscribes for statuses other than NONE and/or ACCEPTED statuses, it needs
|
||||
// special privileges.
|
||||
if (
|
||||
(!args.statuses || args.statuses.some((status) => !['NONE', 'ACCEPTED'].includes(status))) &&
|
||||
(!context.user || !context.user.can(SUBSCRIBE_ALL_COMMENT_ADDED))
|
||||
|
||||
+139
-55
@@ -64,8 +64,99 @@ type UserProfile {
|
||||
provider: String!
|
||||
}
|
||||
|
||||
type SuspensionInfo {
|
||||
# USER_STATUS_USERNAME is the different states that a username can be in.
|
||||
enum USER_STATUS_USERNAME {
|
||||
# UNSET is used when the username can be changed, and does not necessarily
|
||||
# require moderator action to become active. This can be used when the user
|
||||
# signs up with a social login and has the option of setting their own
|
||||
# username.
|
||||
UNSET
|
||||
|
||||
# SET is used when the username has been set for the first time, but cannot
|
||||
# change without the username being rejected by a moderator and that moderator
|
||||
# agreeing that the username should be allowed to change.
|
||||
SET
|
||||
|
||||
# APPROVED is used when the username was changed, and subsequently approved by
|
||||
# said moderator.
|
||||
APPROVED
|
||||
|
||||
# REJECTED is used when the username was changed, and subsequently rejected by
|
||||
# said moderator.
|
||||
REJECTED
|
||||
|
||||
# CHANGED is used after a user has changed their username after it was
|
||||
# rejected.
|
||||
CHANGED
|
||||
}
|
||||
|
||||
# UserStatusInput describes the queryable components of the UserStatus.
|
||||
input UserStatusInput {
|
||||
# username will restrict the returned users to only those with the given
|
||||
# username status's. If not provided, no filtering will be performed.
|
||||
username: [USER_STATUS_USERNAME!]
|
||||
|
||||
# banned will restrict the returned users to only those that are, or are not
|
||||
# banned. If not provided, no filtering will be performed.
|
||||
banned: Boolean
|
||||
|
||||
# suspended will restrict the returned users to only those that are, or are not
|
||||
# suspended. If not provided, no filtering will be performed.
|
||||
suspended: Boolean
|
||||
}
|
||||
|
||||
type UsernameStatusHistory {
|
||||
status: USER_STATUS_USERNAME!
|
||||
assigned_by: User
|
||||
created_at: Date!
|
||||
}
|
||||
|
||||
type UsernameStatus {
|
||||
status: USER_STATUS_USERNAME!
|
||||
history: [UsernameStatusHistory!]
|
||||
}
|
||||
|
||||
type BannedStatusHistory {
|
||||
status: Boolean!
|
||||
assigned_by: User
|
||||
created_at: Date!
|
||||
}
|
||||
|
||||
type BannedStatus {
|
||||
status: Boolean!
|
||||
history: [BannedStatusHistory!]
|
||||
}
|
||||
|
||||
type SuspensionStatusHistory {
|
||||
until: Date
|
||||
assigned_by: User
|
||||
created_at: Date!
|
||||
}
|
||||
|
||||
type SuspensionStatus {
|
||||
until: Date
|
||||
history: [SuspensionStatusHistory!]
|
||||
}
|
||||
|
||||
type UserStatus {
|
||||
# username is the status of the username.
|
||||
username: UsernameStatus!
|
||||
|
||||
# banned is the bool that determines if the user is banned or not.
|
||||
banned: BannedStatus!
|
||||
|
||||
# suspension is the date that the user is suspended until.
|
||||
suspension: SuspensionStatus!
|
||||
}
|
||||
|
||||
input UserStateInput {
|
||||
status: UserStatusInput
|
||||
}
|
||||
|
||||
# UserState describes the different permission based details for a user.
|
||||
type UserState {
|
||||
# status describes the statuses of different aspects of the user's details.
|
||||
status: UserStatus
|
||||
}
|
||||
|
||||
# Any person who can author comments, create actions, and view comments on a
|
||||
@@ -114,11 +205,7 @@ type User {
|
||||
reliable: Reliability
|
||||
|
||||
# returns user status
|
||||
status: USER_STATUS
|
||||
|
||||
# returns suspension info. Only available to Admins and Moderators
|
||||
# or on own logged in User.
|
||||
suspension: SuspensionInfo
|
||||
state: UserState
|
||||
}
|
||||
|
||||
# UserConnection represents a paginable subset of a user list.
|
||||
@@ -143,8 +230,7 @@ input UsersQuery {
|
||||
# Users returned will only be ones which have at least one action of this.
|
||||
action_type: ACTION_TYPE
|
||||
|
||||
# Current status of a user..
|
||||
statuses: [USER_STATUS!]
|
||||
state: UserStateInput
|
||||
|
||||
# Limit the number of results to be returned.
|
||||
limit: Int = 10
|
||||
@@ -261,7 +347,7 @@ enum ACTION_TYPE {
|
||||
# CommentsQuery allows the ability to query comments by a specific methods.
|
||||
input CommentsQuery {
|
||||
|
||||
# Author of the commente
|
||||
# Author of the comments.
|
||||
author_id: ID
|
||||
|
||||
# Current status of a comment.
|
||||
@@ -351,8 +437,8 @@ input UserCountQuery {
|
||||
# type.
|
||||
action_type: ACTION_TYPE
|
||||
|
||||
# Current status of a user.
|
||||
statuses: [USER_STATUS]
|
||||
# state queries for a specific subset of users with the given state query.
|
||||
state: UserStateInput
|
||||
}
|
||||
|
||||
type EditInfo {
|
||||
@@ -810,14 +896,6 @@ enum SORT_COMMENTS_BY {
|
||||
REPLIES
|
||||
}
|
||||
|
||||
# All queries that can be executed.
|
||||
enum USER_STATUS {
|
||||
ACTIVE
|
||||
BANNED
|
||||
PENDING
|
||||
APPROVED
|
||||
}
|
||||
|
||||
# Metrics for the assets.
|
||||
enum ASSET_METRICS_SORT {
|
||||
|
||||
@@ -995,26 +1073,13 @@ input CreateDontAgreeInput {
|
||||
}
|
||||
|
||||
# Input for suspendUser mutation.
|
||||
input SuspendUserInput {
|
||||
input SetUserSuspensionStatusInput {
|
||||
|
||||
# id of target user.
|
||||
id: ID!
|
||||
|
||||
# message to be sent to the user.
|
||||
message: String!
|
||||
|
||||
# target user will be suspended until this date.
|
||||
until: Date!
|
||||
}
|
||||
|
||||
# Input for rejectUsername mutation.
|
||||
input RejectUsernameInput {
|
||||
|
||||
# id of target user.
|
||||
id: ID!
|
||||
|
||||
# message to be sent to the user.
|
||||
message: String!
|
||||
until: Date
|
||||
}
|
||||
|
||||
# Configurable settings that can be overridden for the Asset. You must specify
|
||||
@@ -1027,7 +1092,7 @@ input AssetSettingsInput {
|
||||
# moderation is the moderation mode for the asset.
|
||||
moderation: MODERATION_MODE
|
||||
|
||||
# questionBoxEnable will enable the Question Boxs' content to be visable above
|
||||
# questionBoxEnable will enable the Question Boxs' content to be visible above
|
||||
# the comment box.
|
||||
questionBoxEnable: Boolean
|
||||
|
||||
@@ -1075,17 +1140,9 @@ type DeleteActionResponse implements Response {
|
||||
errors: [UserError!]
|
||||
}
|
||||
|
||||
# SetUserStatusResponse is the response returned with possibly some errors
|
||||
# relating to the delete action attempt.
|
||||
type SetUserStatusResponse implements Response {
|
||||
|
||||
# An array of errors relating to the mutation that occurred.
|
||||
errors: [UserError!]
|
||||
}
|
||||
|
||||
# SuspendUserResponse is the response returned with possibly some errors
|
||||
# relating to the suspend action attempt.
|
||||
type SuspendUserResponse implements Response {
|
||||
# SetUserSuspensionStatusResponse is the response returned with possibly some
|
||||
# errors relating to the suspend action attempt.
|
||||
type SetUserSuspensionStatusResponse implements Response {
|
||||
|
||||
# An array of errors relating to the mutation that occurred.
|
||||
errors: [UserError!]
|
||||
@@ -1218,7 +1275,7 @@ input UpdateSettingsInput {
|
||||
# comment is posted that it can still be edited by the author.
|
||||
editCommentWindowLength: Int
|
||||
|
||||
# wordlist allows chaninging the available wordlists.
|
||||
# wordlist allows changing the available wordlists.
|
||||
wordlist: UpdateWordlistInput
|
||||
|
||||
# domains allows changing the available lists of domains.
|
||||
@@ -1281,6 +1338,29 @@ type RevokeTokenResponse implements Response {
|
||||
errors: [UserError!]
|
||||
}
|
||||
|
||||
# SetUserBanStatusInput contains the input to change the ban status of a given
|
||||
# user.
|
||||
input SetUserBanStatusInput {
|
||||
|
||||
# id is the user to set the ban status on.
|
||||
id: ID!
|
||||
|
||||
# status is the ban status to set on the target user.
|
||||
status: Boolean!
|
||||
}
|
||||
|
||||
type SetUserBanStatusResponse implements Response {
|
||||
|
||||
# An array of errors relating to the mutation that occurred.
|
||||
errors: [UserError!]
|
||||
}
|
||||
|
||||
type SetUsernameStatusResponse implements Response {
|
||||
|
||||
# An array of errors relating to the mutation that occurred.
|
||||
errors: [UserError!]
|
||||
}
|
||||
|
||||
# All mutations for the application are defined on this object.
|
||||
type RootMutation {
|
||||
|
||||
@@ -1299,17 +1379,21 @@ type RootMutation {
|
||||
# Edit a comment
|
||||
editComment(id: ID!, asset_id: ID!, edit: EditCommentInput): EditCommentResponse!
|
||||
|
||||
# Sets User status. Requires the `ADMIN` role.
|
||||
# Sets the suspension status on a given user. Requires the `MODERATOR` role.
|
||||
# Mutation is restricted.
|
||||
setUserStatus(id: ID!, status: USER_STATUS!): SetUserStatusResponse
|
||||
setUserSuspensionStatus(input: SetUserSuspensionStatusInput!): SetUserSuspensionStatusResponse
|
||||
|
||||
# Suspends a user. Requires the `ADMIN` role.
|
||||
# Sets the ban status on a given user. Requires the `MODERATOR` role.
|
||||
# Mutation is restricted.
|
||||
suspendUser(input: SuspendUserInput!): SuspendUserResponse
|
||||
setUserBanStatus(input: SetUserBanStatusInput!): SetUserBanStatusResponse
|
||||
|
||||
# Reject a username. Requires the `ADMIN` role.
|
||||
# Mutation is restricted.
|
||||
rejectUsername(input: RejectUsernameInput!): RejectUsernameResponse
|
||||
# Sets the username status on a given user to `APPROVED`. Requires the
|
||||
# `MODERATOR` role. Mutation is restricted.
|
||||
approveUsername(id: ID!): SetUsernameStatusResponse
|
||||
|
||||
# Sets the username status on a given user to `REJECTED`. Requires the
|
||||
# `MODERATOR` role. Mutation is restricted.
|
||||
rejectUsername(id: ID!): SetUsernameStatusResponse
|
||||
|
||||
# Sets Comment status. Requires the `ADMIN` role.
|
||||
# Mutation is restricted.
|
||||
@@ -1327,7 +1411,7 @@ type RootMutation {
|
||||
|
||||
# Updates the status of an asset allowing you to close/reopen an asset for
|
||||
# commenting.
|
||||
# Mutation is restricted.
|
||||
# Mutation is restricted.
|
||||
updateAssetStatus(id: ID!, input: UpdateAssetStatusInput!): UpdateAssetStatusResponse
|
||||
|
||||
# updateSettings will update the global settings.
|
||||
|
||||
Reference in New Issue
Block a user