diff options
author | Chocobozzz <me@florianbigard.com> | 2020-07-24 15:05:51 +0200 |
---|---|---|
committer | Chocobozzz <chocobozzz@cpy.re> | 2020-07-31 11:35:19 +0200 |
commit | edbc9325462ddf4536775871ebc25e06f46612d1 (patch) | |
tree | 9671dd51303e75d48d4f4f9a1df7a1960e33780d | |
parent | 20516920d2b72c8a18bc24b9740f7176aa962da2 (diff) | |
download | PeerTube-edbc9325462ddf4536775871ebc25e06f46612d1.tar.gz PeerTube-edbc9325462ddf4536775871ebc25e06f46612d1.tar.zst PeerTube-edbc9325462ddf4536775871ebc25e06f46612d1.zip |
Add server API to abuse messages
28 files changed, 1038 insertions, 193 deletions
diff --git a/server/controllers/api/abuse.ts b/server/controllers/api/abuse.ts index 04a0c06e3..50d068157 100644 --- a/server/controllers/api/abuse.ts +++ b/server/controllers/api/abuse.ts | |||
@@ -1,20 +1,24 @@ | |||
1 | import * as express from 'express' | 1 | import * as express from 'express' |
2 | import { createAccountAbuse, createVideoAbuse, createVideoCommentAbuse } from '@server/lib/moderation' | 2 | import { createAccountAbuse, createVideoAbuse, createVideoCommentAbuse } from '@server/lib/moderation' |
3 | import { AbuseModel } from '@server/models/abuse/abuse' | 3 | import { AbuseModel } from '@server/models/abuse/abuse' |
4 | import { AbuseMessageModel } from '@server/models/abuse/abuse-message' | ||
4 | import { getServerActor } from '@server/models/application/application' | 5 | import { getServerActor } from '@server/models/application/application' |
5 | import { AbuseCreate, abusePredefinedReasonsMap, AbuseState, UserRight } from '../../../shared' | 6 | import { AbuseCreate, abusePredefinedReasonsMap, AbuseState, UserRight } from '../../../shared' |
6 | import { getFormattedObjects } from '../../helpers/utils' | 7 | import { getFormattedObjects } from '../../helpers/utils' |
7 | import { sequelizeTypescript } from '../../initializers/database' | 8 | import { sequelizeTypescript } from '../../initializers/database' |
8 | import { | 9 | import { |
9 | abuseGetValidator, | 10 | abuseGetValidator, |
10 | abuseListValidator, | 11 | abuseListForAdminsValidator, |
11 | abuseReportValidator, | 12 | abuseReportValidator, |
12 | abusesSortValidator, | 13 | abusesSortValidator, |
13 | abuseUpdateValidator, | 14 | abuseUpdateValidator, |
15 | addAbuseMessageValidator, | ||
14 | asyncMiddleware, | 16 | asyncMiddleware, |
15 | asyncRetryTransactionMiddleware, | 17 | asyncRetryTransactionMiddleware, |
16 | authenticate, | 18 | authenticate, |
19 | deleteAbuseMessageValidator, | ||
17 | ensureUserHasRight, | 20 | ensureUserHasRight, |
21 | getAbuseValidator, | ||
18 | paginationValidator, | 22 | paginationValidator, |
19 | setDefaultPagination, | 23 | setDefaultPagination, |
20 | setDefaultSort | 24 | setDefaultSort |
@@ -30,8 +34,8 @@ abuseRouter.get('/', | |||
30 | abusesSortValidator, | 34 | abusesSortValidator, |
31 | setDefaultSort, | 35 | setDefaultSort, |
32 | setDefaultPagination, | 36 | setDefaultPagination, |
33 | abuseListValidator, | 37 | abuseListForAdminsValidator, |
34 | asyncMiddleware(listAbuses) | 38 | asyncMiddleware(listAbusesForAdmins) |
35 | ) | 39 | ) |
36 | abuseRouter.put('/:id', | 40 | abuseRouter.put('/:id', |
37 | authenticate, | 41 | authenticate, |
@@ -51,13 +55,33 @@ abuseRouter.delete('/:id', | |||
51 | asyncRetryTransactionMiddleware(deleteAbuse) | 55 | asyncRetryTransactionMiddleware(deleteAbuse) |
52 | ) | 56 | ) |
53 | 57 | ||
58 | abuseRouter.get('/:id/messages', | ||
59 | authenticate, | ||
60 | asyncMiddleware(getAbuseValidator), | ||
61 | asyncRetryTransactionMiddleware(listAbuseMessages) | ||
62 | ) | ||
63 | |||
64 | abuseRouter.post('/:id/messages', | ||
65 | authenticate, | ||
66 | asyncMiddleware(getAbuseValidator), | ||
67 | addAbuseMessageValidator, | ||
68 | asyncRetryTransactionMiddleware(addAbuseMessage) | ||
69 | ) | ||
70 | |||
71 | abuseRouter.delete('/:id/messages/:messageId', | ||
72 | authenticate, | ||
73 | asyncMiddleware(getAbuseValidator), | ||
74 | asyncMiddleware(deleteAbuseMessageValidator), | ||
75 | asyncRetryTransactionMiddleware(deleteAbuseMessage) | ||
76 | ) | ||
77 | |||
54 | // --------------------------------------------------------------------------- | 78 | // --------------------------------------------------------------------------- |
55 | 79 | ||
56 | export { | 80 | export { |
57 | abuseRouter, | 81 | abuseRouter, |
58 | 82 | ||
59 | // FIXME: deprecated in 2.3. Remove these exports | 83 | // FIXME: deprecated in 2.3. Remove these exports |
60 | listAbuses, | 84 | listAbusesForAdmins, |
61 | updateAbuse, | 85 | updateAbuse, |
62 | deleteAbuse, | 86 | deleteAbuse, |
63 | reportAbuse | 87 | reportAbuse |
@@ -65,11 +89,11 @@ export { | |||
65 | 89 | ||
66 | // --------------------------------------------------------------------------- | 90 | // --------------------------------------------------------------------------- |
67 | 91 | ||
68 | async function listAbuses (req: express.Request, res: express.Response) { | 92 | async function listAbusesForAdmins (req: express.Request, res: express.Response) { |
69 | const user = res.locals.oauth.token.user | 93 | const user = res.locals.oauth.token.user |
70 | const serverActor = await getServerActor() | 94 | const serverActor = await getServerActor() |
71 | 95 | ||
72 | const resultList = await AbuseModel.listForApi({ | 96 | const resultList = await AbuseModel.listForAdminApi({ |
73 | start: req.query.start, | 97 | start: req.query.start, |
74 | count: req.query.count, | 98 | count: req.query.count, |
75 | sort: req.query.sort, | 99 | sort: req.query.sort, |
@@ -87,7 +111,10 @@ async function listAbuses (req: express.Request, res: express.Response) { | |||
87 | user | 111 | user |
88 | }) | 112 | }) |
89 | 113 | ||
90 | return res.json(getFormattedObjects(resultList.data, resultList.total)) | 114 | return res.json({ |
115 | total: resultList.total, | ||
116 | data: resultList.data.map(d => d.toFormattedAdminJSON()) | ||
117 | }) | ||
91 | } | 118 | } |
92 | 119 | ||
93 | async function updateAbuse (req: express.Request, res: express.Response) { | 120 | async function updateAbuse (req: express.Request, res: express.Response) { |
@@ -100,6 +127,8 @@ async function updateAbuse (req: express.Request, res: express.Response) { | |||
100 | return abuse.save({ transaction: t }) | 127 | return abuse.save({ transaction: t }) |
101 | }) | 128 | }) |
102 | 129 | ||
130 | // TODO: Notification | ||
131 | |||
103 | // Do not send the delete to other instances, we updated OUR copy of this abuse | 132 | // Do not send the delete to other instances, we updated OUR copy of this abuse |
104 | 133 | ||
105 | return res.type('json').status(204).end() | 134 | return res.type('json').status(204).end() |
@@ -166,3 +195,41 @@ async function reportAbuse (req: express.Request, res: express.Response) { | |||
166 | 195 | ||
167 | return res.json({ abuse: { id } }) | 196 | return res.json({ abuse: { id } }) |
168 | } | 197 | } |
198 | |||
199 | async function listAbuseMessages (req: express.Request, res: express.Response) { | ||
200 | const abuse = res.locals.abuse | ||
201 | |||
202 | const resultList = await AbuseMessageModel.listForApi(abuse.id) | ||
203 | |||
204 | return res.json(getFormattedObjects(resultList.data, resultList.total)) | ||
205 | } | ||
206 | |||
207 | async function addAbuseMessage (req: express.Request, res: express.Response) { | ||
208 | const abuse = res.locals.abuse | ||
209 | const user = res.locals.oauth.token.user | ||
210 | |||
211 | const abuseMessage = await AbuseMessageModel.create({ | ||
212 | message: req.body.message, | ||
213 | byModerator: abuse.reporterAccountId !== user.Account.id, | ||
214 | accountId: user.Account.id, | ||
215 | abuseId: abuse.id | ||
216 | }) | ||
217 | |||
218 | // TODO: Notification | ||
219 | |||
220 | return res.json({ | ||
221 | abuseMessage: { | ||
222 | id: abuseMessage.id | ||
223 | } | ||
224 | }) | ||
225 | } | ||
226 | |||
227 | async function deleteAbuseMessage (req: express.Request, res: express.Response) { | ||
228 | const abuseMessage = res.locals.abuseMessage | ||
229 | |||
230 | await sequelizeTypescript.transaction(t => { | ||
231 | return abuseMessage.destroy({ transaction: t }) | ||
232 | }) | ||
233 | |||
234 | return res.sendStatus(204) | ||
235 | } | ||
diff --git a/server/controllers/api/users/index.ts b/server/controllers/api/users/index.ts index 5939f6125..d339c2a1c 100644 --- a/server/controllers/api/users/index.ts +++ b/server/controllers/api/users/index.ts | |||
@@ -1,10 +1,20 @@ | |||
1 | import * as express from 'express' | 1 | import * as express from 'express' |
2 | import * as RateLimit from 'express-rate-limit' | 2 | import * as RateLimit from 'express-rate-limit' |
3 | import { tokensRouter } from '@server/controllers/api/users/token' | ||
4 | import { Hooks } from '@server/lib/plugins/hooks' | ||
5 | import { MUser, MUserAccountDefault } from '@server/types/models' | ||
3 | import { UserCreate, UserRight, UserRole, UserUpdate } from '../../../../shared' | 6 | import { UserCreate, UserRight, UserRole, UserUpdate } from '../../../../shared' |
7 | import { UserAdminFlag } from '../../../../shared/models/users/user-flag.model' | ||
8 | import { UserRegister } from '../../../../shared/models/users/user-register.model' | ||
9 | import { auditLoggerFactory, getAuditIdFromRes, UserAuditView } from '../../../helpers/audit-logger' | ||
4 | import { logger } from '../../../helpers/logger' | 10 | import { logger } from '../../../helpers/logger' |
5 | import { generateRandomString, getFormattedObjects } from '../../../helpers/utils' | 11 | import { generateRandomString, getFormattedObjects } from '../../../helpers/utils' |
12 | import { CONFIG } from '../../../initializers/config' | ||
6 | import { WEBSERVER } from '../../../initializers/constants' | 13 | import { WEBSERVER } from '../../../initializers/constants' |
14 | import { sequelizeTypescript } from '../../../initializers/database' | ||
7 | import { Emailer } from '../../../lib/emailer' | 15 | import { Emailer } from '../../../lib/emailer' |
16 | import { Notifier } from '../../../lib/notifier' | ||
17 | import { deleteUserToken } from '../../../lib/oauth-model' | ||
8 | import { Redis } from '../../../lib/redis' | 18 | import { Redis } from '../../../lib/redis' |
9 | import { createUserAccountAndChannelAndPlaylist, sendVerifyUserEmail } from '../../../lib/user' | 19 | import { createUserAccountAndChannelAndPlaylist, sendVerifyUserEmail } from '../../../lib/user' |
10 | import { | 20 | import { |
@@ -18,9 +28,9 @@ import { | |||
18 | setDefaultPagination, | 28 | setDefaultPagination, |
19 | setDefaultSort, | 29 | setDefaultSort, |
20 | userAutocompleteValidator, | 30 | userAutocompleteValidator, |
21 | usersListValidator, | ||
22 | usersAddValidator, | 31 | usersAddValidator, |
23 | usersGetValidator, | 32 | usersGetValidator, |
33 | usersListValidator, | ||
24 | usersRegisterValidator, | 34 | usersRegisterValidator, |
25 | usersRemoveValidator, | 35 | usersRemoveValidator, |
26 | usersSortValidator, | 36 | usersSortValidator, |
@@ -35,22 +45,13 @@ import { | |||
35 | usersVerifyEmailValidator | 45 | usersVerifyEmailValidator |
36 | } from '../../../middlewares/validators' | 46 | } from '../../../middlewares/validators' |
37 | import { UserModel } from '../../../models/account/user' | 47 | import { UserModel } from '../../../models/account/user' |
38 | import { auditLoggerFactory, getAuditIdFromRes, UserAuditView } from '../../../helpers/audit-logger' | ||
39 | import { meRouter } from './me' | 48 | import { meRouter } from './me' |
40 | import { deleteUserToken } from '../../../lib/oauth-model' | 49 | import { myAbusesRouter } from './my-abuses' |
41 | import { myBlocklistRouter } from './my-blocklist' | 50 | import { myBlocklistRouter } from './my-blocklist' |
42 | import { myVideoPlaylistsRouter } from './my-video-playlists' | ||
43 | import { myVideosHistoryRouter } from './my-history' | 51 | import { myVideosHistoryRouter } from './my-history' |
44 | import { myNotificationsRouter } from './my-notifications' | 52 | import { myNotificationsRouter } from './my-notifications' |
45 | import { Notifier } from '../../../lib/notifier' | ||
46 | import { mySubscriptionsRouter } from './my-subscriptions' | 53 | import { mySubscriptionsRouter } from './my-subscriptions' |
47 | import { CONFIG } from '../../../initializers/config' | 54 | import { myVideoPlaylistsRouter } from './my-video-playlists' |
48 | import { sequelizeTypescript } from '../../../initializers/database' | ||
49 | import { UserAdminFlag } from '../../../../shared/models/users/user-flag.model' | ||
50 | import { UserRegister } from '../../../../shared/models/users/user-register.model' | ||
51 | import { MUser, MUserAccountDefault } from '@server/types/models' | ||
52 | import { Hooks } from '@server/lib/plugins/hooks' | ||
53 | import { tokensRouter } from '@server/controllers/api/users/token' | ||
54 | 55 | ||
55 | const auditLogger = auditLoggerFactory('users') | 56 | const auditLogger = auditLoggerFactory('users') |
56 | 57 | ||
@@ -72,6 +73,7 @@ usersRouter.use('/', mySubscriptionsRouter) | |||
72 | usersRouter.use('/', myBlocklistRouter) | 73 | usersRouter.use('/', myBlocklistRouter) |
73 | usersRouter.use('/', myVideosHistoryRouter) | 74 | usersRouter.use('/', myVideosHistoryRouter) |
74 | usersRouter.use('/', myVideoPlaylistsRouter) | 75 | usersRouter.use('/', myVideoPlaylistsRouter) |
76 | usersRouter.use('/', myAbusesRouter) | ||
75 | usersRouter.use('/', meRouter) | 77 | usersRouter.use('/', meRouter) |
76 | 78 | ||
77 | usersRouter.get('/autocomplete', | 79 | usersRouter.get('/autocomplete', |
diff --git a/server/controllers/api/users/my-abuses.ts b/server/controllers/api/users/my-abuses.ts new file mode 100644 index 000000000..e43fc483e --- /dev/null +++ b/server/controllers/api/users/my-abuses.ts | |||
@@ -0,0 +1,48 @@ | |||
1 | import * as express from 'express' | ||
2 | import { AbuseModel } from '@server/models/abuse/abuse' | ||
3 | import { | ||
4 | abuseListForUserValidator, | ||
5 | abusesSortValidator, | ||
6 | asyncMiddleware, | ||
7 | authenticate, | ||
8 | paginationValidator, | ||
9 | setDefaultPagination, | ||
10 | setDefaultSort | ||
11 | } from '../../../middlewares' | ||
12 | |||
13 | const myAbusesRouter = express.Router() | ||
14 | |||
15 | myAbusesRouter.get('/me/abuses', | ||
16 | authenticate, | ||
17 | paginationValidator, | ||
18 | abusesSortValidator, | ||
19 | setDefaultSort, | ||
20 | setDefaultPagination, | ||
21 | abuseListForUserValidator, | ||
22 | asyncMiddleware(listMyAbuses) | ||
23 | ) | ||
24 | |||
25 | // --------------------------------------------------------------------------- | ||
26 | |||
27 | export { | ||
28 | myAbusesRouter | ||
29 | } | ||
30 | |||
31 | // --------------------------------------------------------------------------- | ||
32 | |||
33 | async function listMyAbuses (req: express.Request, res: express.Response) { | ||
34 | const resultList = await AbuseModel.listForUserApi({ | ||
35 | start: req.query.start, | ||
36 | count: req.query.count, | ||
37 | sort: req.query.sort, | ||
38 | id: req.query.id, | ||
39 | search: req.query.search, | ||
40 | state: req.query.state, | ||
41 | user: res.locals.oauth.token.User | ||
42 | }) | ||
43 | |||
44 | return res.json({ | ||
45 | total: resultList.total, | ||
46 | data: resultList.data.map(d => d.toFormattedAdminJSON()) | ||
47 | }) | ||
48 | } | ||
diff --git a/server/controllers/api/videos/abuse.ts b/server/controllers/api/videos/abuse.ts index b92a66360..9c4d00849 100644 --- a/server/controllers/api/videos/abuse.ts +++ b/server/controllers/api/videos/abuse.ts | |||
@@ -2,7 +2,6 @@ import * as express from 'express' | |||
2 | import { AbuseModel } from '@server/models/abuse/abuse' | 2 | import { AbuseModel } from '@server/models/abuse/abuse' |
3 | import { getServerActor } from '@server/models/application/application' | 3 | import { getServerActor } from '@server/models/application/application' |
4 | import { AbuseCreate, UserRight, VideoAbuseCreate } from '../../../../shared' | 4 | import { AbuseCreate, UserRight, VideoAbuseCreate } from '../../../../shared' |
5 | import { getFormattedObjects } from '../../../helpers/utils' | ||
6 | import { | 5 | import { |
7 | abusesSortValidator, | 6 | abusesSortValidator, |
8 | asyncMiddleware, | 7 | asyncMiddleware, |
@@ -63,7 +62,7 @@ async function listVideoAbuses (req: express.Request, res: express.Response) { | |||
63 | const user = res.locals.oauth.token.user | 62 | const user = res.locals.oauth.token.user |
64 | const serverActor = await getServerActor() | 63 | const serverActor = await getServerActor() |
65 | 64 | ||
66 | const resultList = await AbuseModel.listForApi({ | 65 | const resultList = await AbuseModel.listForAdminApi({ |
67 | start: req.query.start, | 66 | start: req.query.start, |
68 | count: req.query.count, | 67 | count: req.query.count, |
69 | sort: req.query.sort, | 68 | sort: req.query.sort, |
@@ -81,7 +80,10 @@ async function listVideoAbuses (req: express.Request, res: express.Response) { | |||
81 | user | 80 | user |
82 | }) | 81 | }) |
83 | 82 | ||
84 | return res.json(getFormattedObjects(resultList.data, resultList.total)) | 83 | return res.json({ |
84 | total: resultList.total, | ||
85 | data: resultList.data.map(d => d.toFormattedAdminJSON()) | ||
86 | }) | ||
85 | } | 87 | } |
86 | 88 | ||
87 | async function updateVideoAbuse (req: express.Request, res: express.Response) { | 89 | async function updateVideoAbuse (req: express.Request, res: express.Response) { |
diff --git a/server/helpers/audit-logger.ts b/server/helpers/audit-logger.ts index 954b0b69d..6aae5e821 100644 --- a/server/helpers/audit-logger.ts +++ b/server/helpers/audit-logger.ts | |||
@@ -5,7 +5,7 @@ import { chain } from 'lodash' | |||
5 | import * as path from 'path' | 5 | import * as path from 'path' |
6 | import * as winston from 'winston' | 6 | import * as winston from 'winston' |
7 | import { AUDIT_LOG_FILENAME } from '@server/initializers/constants' | 7 | import { AUDIT_LOG_FILENAME } from '@server/initializers/constants' |
8 | import { Abuse, User, VideoChannel, VideoDetails, VideoImport } from '../../shared' | 8 | import { AdminAbuse, User, VideoChannel, VideoDetails, VideoImport } from '../../shared' |
9 | import { CustomConfig } from '../../shared/models/server/custom-config.model' | 9 | import { CustomConfig } from '../../shared/models/server/custom-config.model' |
10 | import { VideoComment } from '../../shared/models/videos/video-comment.model' | 10 | import { VideoComment } from '../../shared/models/videos/video-comment.model' |
11 | import { CONFIG } from '../initializers/config' | 11 | import { CONFIG } from '../initializers/config' |
@@ -219,7 +219,7 @@ const abuseKeysToKeep = [ | |||
219 | 'createdAt' | 219 | 'createdAt' |
220 | ] | 220 | ] |
221 | class AbuseAuditView extends EntityAuditView { | 221 | class AbuseAuditView extends EntityAuditView { |
222 | constructor (private readonly abuse: Abuse) { | 222 | constructor (private readonly abuse: AdminAbuse) { |
223 | super(abuseKeysToKeep, 'abuse', abuse) | 223 | super(abuseKeysToKeep, 'abuse', abuse) |
224 | } | 224 | } |
225 | } | 225 | } |
diff --git a/server/helpers/custom-validators/abuses.ts b/server/helpers/custom-validators/abuses.ts index 0ca06a252..0ddde4b06 100644 --- a/server/helpers/custom-validators/abuses.ts +++ b/server/helpers/custom-validators/abuses.ts | |||
@@ -4,6 +4,7 @@ import { ABUSE_STATES, CONSTRAINTS_FIELDS } from '../../initializers/constants' | |||
4 | import { exists, isArray } from './misc' | 4 | import { exists, isArray } from './misc' |
5 | 5 | ||
6 | const ABUSES_CONSTRAINTS_FIELDS = CONSTRAINTS_FIELDS.ABUSES | 6 | const ABUSES_CONSTRAINTS_FIELDS = CONSTRAINTS_FIELDS.ABUSES |
7 | const ABUSE_MESSAGES_CONSTRAINTS_FIELDS = CONSTRAINTS_FIELDS.ABUSE_MESSAGES | ||
7 | 8 | ||
8 | function isAbuseReasonValid (value: string) { | 9 | function isAbuseReasonValid (value: string) { |
9 | return exists(value) && validator.isLength(value, ABUSES_CONSTRAINTS_FIELDS.REASON) | 10 | return exists(value) && validator.isLength(value, ABUSES_CONSTRAINTS_FIELDS.REASON) |
@@ -46,13 +47,18 @@ function isAbuseVideoIsValid (value: AbuseVideoIs) { | |||
46 | ) | 47 | ) |
47 | } | 48 | } |
48 | 49 | ||
50 | function isAbuseMessageValid (value: string) { | ||
51 | return exists(value) && validator.isLength(value, ABUSE_MESSAGES_CONSTRAINTS_FIELDS.MESSAGE) | ||
52 | } | ||
53 | |||
49 | // --------------------------------------------------------------------------- | 54 | // --------------------------------------------------------------------------- |
50 | 55 | ||
51 | export { | 56 | export { |
52 | isAbuseReasonValid, | 57 | isAbuseReasonValid, |
53 | isAbuseFilterValid, | 58 | isAbuseFilterValid, |
54 | isAbusePredefinedReasonValid, | 59 | isAbusePredefinedReasonValid, |
55 | areAbusePredefinedReasonsValid as isAbusePredefinedReasonsValid, | 60 | isAbuseMessageValid, |
61 | areAbusePredefinedReasonsValid, | ||
56 | isAbuseTimestampValid, | 62 | isAbuseTimestampValid, |
57 | isAbuseTimestampCoherent, | 63 | isAbuseTimestampCoherent, |
58 | isAbuseModerationCommentValid, | 64 | isAbuseModerationCommentValid, |
diff --git a/server/initializers/constants.ts b/server/initializers/constants.ts index fd5bf5868..a40a22395 100644 --- a/server/initializers/constants.ts +++ b/server/initializers/constants.ts | |||
@@ -206,6 +206,9 @@ const CONSTRAINTS_FIELDS = { | |||
206 | REASON: { min: 2, max: 3000 }, // Length | 206 | REASON: { min: 2, max: 3000 }, // Length |
207 | MODERATION_COMMENT: { min: 2, max: 3000 } // Length | 207 | MODERATION_COMMENT: { min: 2, max: 3000 } // Length |
208 | }, | 208 | }, |
209 | ABUSE_MESSAGES: { | ||
210 | MESSAGE: { min: 2, max: 3000 } // Length | ||
211 | }, | ||
209 | VIDEO_BLACKLIST: { | 212 | VIDEO_BLACKLIST: { |
210 | REASON: { min: 2, max: 300 } // Length | 213 | REASON: { min: 2, max: 300 } // Length |
211 | }, | 214 | }, |
diff --git a/server/initializers/database.ts b/server/initializers/database.ts index 0775f1fad..8ce32f6fa 100644 --- a/server/initializers/database.ts +++ b/server/initializers/database.ts | |||
@@ -1,6 +1,7 @@ | |||
1 | import { QueryTypes, Transaction } from 'sequelize' | 1 | import { QueryTypes, Transaction } from 'sequelize' |
2 | import { Sequelize as SequelizeTypescript } from 'sequelize-typescript' | 2 | import { Sequelize as SequelizeTypescript } from 'sequelize-typescript' |
3 | import { AbuseModel } from '@server/models/abuse/abuse' | 3 | import { AbuseModel } from '@server/models/abuse/abuse' |
4 | import { AbuseMessageModel } from '@server/models/abuse/abuse-message' | ||
4 | import { VideoAbuseModel } from '@server/models/abuse/video-abuse' | 5 | import { VideoAbuseModel } from '@server/models/abuse/video-abuse' |
5 | import { VideoCommentAbuseModel } from '@server/models/abuse/video-comment-abuse' | 6 | import { VideoCommentAbuseModel } from '@server/models/abuse/video-comment-abuse' |
6 | import { isTestInstance } from '../helpers/core-utils' | 7 | import { isTestInstance } from '../helpers/core-utils' |
@@ -87,6 +88,7 @@ async function initDatabaseModels (silent: boolean) { | |||
87 | TagModel, | 88 | TagModel, |
88 | AccountVideoRateModel, | 89 | AccountVideoRateModel, |
89 | UserModel, | 90 | UserModel, |
91 | AbuseMessageModel, | ||
90 | AbuseModel, | 92 | AbuseModel, |
91 | VideoCommentAbuseModel, | 93 | VideoCommentAbuseModel, |
92 | VideoAbuseModel, | 94 | VideoAbuseModel, |
diff --git a/server/lib/emailer.ts b/server/lib/emailer.ts index 48ba7421e..c6ad03328 100644 --- a/server/lib/emailer.ts +++ b/server/lib/emailer.ts | |||
@@ -5,7 +5,7 @@ import { join } from 'path' | |||
5 | import { VideoChannelModel } from '@server/models/video/video-channel' | 5 | import { VideoChannelModel } from '@server/models/video/video-channel' |
6 | import { MVideoBlacklistLightVideo, MVideoBlacklistVideo } from '@server/types/models/video/video-blacklist' | 6 | import { MVideoBlacklistLightVideo, MVideoBlacklistVideo } from '@server/types/models/video/video-blacklist' |
7 | import { MVideoImport, MVideoImportVideo } from '@server/types/models/video/video-import' | 7 | import { MVideoImport, MVideoImportVideo } from '@server/types/models/video/video-import' |
8 | import { Abuse, EmailPayload } from '@shared/models' | 8 | import { UserAbuse, EmailPayload } from '@shared/models' |
9 | import { SendEmailOptions } from '../../shared/models/server/emailer.model' | 9 | import { SendEmailOptions } from '../../shared/models/server/emailer.model' |
10 | import { isTestInstance, root } from '../helpers/core-utils' | 10 | import { isTestInstance, root } from '../helpers/core-utils' |
11 | import { bunyanLogger, logger } from '../helpers/logger' | 11 | import { bunyanLogger, logger } from '../helpers/logger' |
@@ -283,7 +283,7 @@ class Emailer { | |||
283 | } | 283 | } |
284 | 284 | ||
285 | addAbuseModeratorsNotification (to: string[], parameters: { | 285 | addAbuseModeratorsNotification (to: string[], parameters: { |
286 | abuse: Abuse | 286 | abuse: UserAbuse |
287 | abuseInstance: MAbuseFull | 287 | abuseInstance: MAbuseFull |
288 | reporter: string | 288 | reporter: string |
289 | }) { | 289 | }) { |
diff --git a/server/lib/moderation.ts b/server/lib/moderation.ts index 4fc9cd747..b140d5aa9 100644 --- a/server/lib/moderation.ts +++ b/server/lib/moderation.ts | |||
@@ -213,7 +213,7 @@ async function createAbuse (options: { | |||
213 | await sendAbuse(reporterAccount.Actor, abuseInstance, abuseInstance.FlaggedAccount, transaction) | 213 | await sendAbuse(reporterAccount.Actor, abuseInstance, abuseInstance.FlaggedAccount, transaction) |
214 | } | 214 | } |
215 | 215 | ||
216 | const abuseJSON = abuseInstance.toFormattedJSON() | 216 | const abuseJSON = abuseInstance.toFormattedAdminJSON() |
217 | auditLogger.create(reporterAccount.Actor.getIdentifier(), new AbuseAuditView(abuseJSON)) | 217 | auditLogger.create(reporterAccount.Actor.getIdentifier(), new AbuseAuditView(abuseJSON)) |
218 | 218 | ||
219 | Notifier.Instance.notifyOnNewAbuse({ | 219 | Notifier.Instance.notifyOnNewAbuse({ |
diff --git a/server/lib/notifier.ts b/server/lib/notifier.ts index c567e1c20..8f165d2fd 100644 --- a/server/lib/notifier.ts +++ b/server/lib/notifier.ts | |||
@@ -10,7 +10,7 @@ import { | |||
10 | } from '@server/types/models/user' | 10 | } from '@server/types/models/user' |
11 | import { MVideoBlacklistLightVideo, MVideoBlacklistVideo } from '@server/types/models/video/video-blacklist' | 11 | import { MVideoBlacklistLightVideo, MVideoBlacklistVideo } from '@server/types/models/video/video-blacklist' |
12 | import { MVideoImportVideo } from '@server/types/models/video/video-import' | 12 | import { MVideoImportVideo } from '@server/types/models/video/video-import' |
13 | import { Abuse } from '@shared/models' | 13 | import { UserAbuse } from '@shared/models' |
14 | import { UserNotificationSettingValue, UserNotificationType, UserRight } from '../../shared/models/users' | 14 | import { UserNotificationSettingValue, UserNotificationType, UserRight } from '../../shared/models/users' |
15 | import { VideoPrivacy, VideoState } from '../../shared/models/videos' | 15 | import { VideoPrivacy, VideoState } from '../../shared/models/videos' |
16 | import { logger } from '../helpers/logger' | 16 | import { logger } from '../helpers/logger' |
@@ -73,7 +73,7 @@ class Notifier { | |||
73 | .catch(err => logger.error('Cannot notify mentions of comment %s.', comment.url, { err })) | 73 | .catch(err => logger.error('Cannot notify mentions of comment %s.', comment.url, { err })) |
74 | } | 74 | } |
75 | 75 | ||
76 | notifyOnNewAbuse (parameters: { abuse: Abuse, abuseInstance: MAbuseFull, reporter: string }): void { | 76 | notifyOnNewAbuse (parameters: { abuse: UserAbuse, abuseInstance: MAbuseFull, reporter: string }): void { |
77 | this.notifyModeratorsOfNewAbuse(parameters) | 77 | this.notifyModeratorsOfNewAbuse(parameters) |
78 | .catch(err => logger.error('Cannot notify of new abuse %d.', parameters.abuseInstance.id, { err })) | 78 | .catch(err => logger.error('Cannot notify of new abuse %d.', parameters.abuseInstance.id, { err })) |
79 | } | 79 | } |
@@ -350,7 +350,7 @@ class Notifier { | |||
350 | } | 350 | } |
351 | 351 | ||
352 | private async notifyModeratorsOfNewAbuse (parameters: { | 352 | private async notifyModeratorsOfNewAbuse (parameters: { |
353 | abuse: Abuse | 353 | abuse: UserAbuse |
354 | abuseInstance: MAbuseFull | 354 | abuseInstance: MAbuseFull |
355 | reporter: string | 355 | reporter: string |
356 | }) { | 356 | }) { |
diff --git a/server/middlewares/user-right.ts b/server/middlewares/user-right.ts index 4da7b9802..4d836485c 100644 --- a/server/middlewares/user-right.ts +++ b/server/middlewares/user-right.ts | |||
@@ -9,11 +9,7 @@ function ensureUserHasRight (userRight: UserRight) { | |||
9 | const message = `User ${user.username} does not have right ${UserRight[userRight]} to access to ${req.path}.` | 9 | const message = `User ${user.username} does not have right ${UserRight[userRight]} to access to ${req.path}.` |
10 | logger.info(message) | 10 | logger.info(message) |
11 | 11 | ||
12 | return res.status(403) | 12 | return res.status(403).json({ error: message }) |
13 | .json({ | ||
14 | error: message | ||
15 | }) | ||
16 | .end() | ||
17 | } | 13 | } |
18 | 14 | ||
19 | return next() | 15 | return next() |
diff --git a/server/middlewares/validators/abuse.ts b/server/middlewares/validators/abuse.ts index 966d1f7fb..cb0bc658a 100644 --- a/server/middlewares/validators/abuse.ts +++ b/server/middlewares/validators/abuse.ts | |||
@@ -2,8 +2,9 @@ import * as express from 'express' | |||
2 | import { body, param, query } from 'express-validator' | 2 | import { body, param, query } from 'express-validator' |
3 | import { | 3 | import { |
4 | isAbuseFilterValid, | 4 | isAbuseFilterValid, |
5 | isAbuseMessageValid, | ||
5 | isAbuseModerationCommentValid, | 6 | isAbuseModerationCommentValid, |
6 | isAbusePredefinedReasonsValid, | 7 | areAbusePredefinedReasonsValid, |
7 | isAbusePredefinedReasonValid, | 8 | isAbusePredefinedReasonValid, |
8 | isAbuseReasonValid, | 9 | isAbuseReasonValid, |
9 | isAbuseStateValid, | 10 | isAbuseStateValid, |
@@ -15,7 +16,8 @@ import { exists, isIdOrUUIDValid, isIdValid, toIntOrNull } from '@server/helpers | |||
15 | import { doesCommentIdExist } from '@server/helpers/custom-validators/video-comments' | 16 | import { doesCommentIdExist } from '@server/helpers/custom-validators/video-comments' |
16 | import { logger } from '@server/helpers/logger' | 17 | import { logger } from '@server/helpers/logger' |
17 | import { doesAbuseExist, doesAccountIdExist, doesVideoAbuseExist, doesVideoExist } from '@server/helpers/middlewares' | 18 | import { doesAbuseExist, doesAccountIdExist, doesVideoAbuseExist, doesVideoExist } from '@server/helpers/middlewares' |
18 | import { AbuseCreate } from '@shared/models' | 19 | import { AbuseMessageModel } from '@server/models/abuse/abuse-message' |
20 | import { AbuseCreate, UserRight } from '@shared/models' | ||
19 | import { areValidationErrors } from './utils' | 21 | import { areValidationErrors } from './utils' |
20 | 22 | ||
21 | const abuseReportValidator = [ | 23 | const abuseReportValidator = [ |
@@ -53,7 +55,7 @@ const abuseReportValidator = [ | |||
53 | 55 | ||
54 | body('predefinedReasons') | 56 | body('predefinedReasons') |
55 | .optional() | 57 | .optional() |
56 | .custom(isAbusePredefinedReasonsValid) | 58 | .custom(areAbusePredefinedReasonsValid) |
57 | .withMessage('Should have a valid list of predefined reasons'), | 59 | .withMessage('Should have a valid list of predefined reasons'), |
58 | 60 | ||
59 | async (req: express.Request, res: express.Response, next: express.NextFunction) => { | 61 | async (req: express.Request, res: express.Response, next: express.NextFunction) => { |
@@ -111,7 +113,7 @@ const abuseUpdateValidator = [ | |||
111 | } | 113 | } |
112 | ] | 114 | ] |
113 | 115 | ||
114 | const abuseListValidator = [ | 116 | const abuseListForAdminsValidator = [ |
115 | query('id') | 117 | query('id') |
116 | .optional() | 118 | .optional() |
117 | .custom(isIdValid).withMessage('Should have a valid id'), | 119 | .custom(isIdValid).withMessage('Should have a valid id'), |
@@ -146,7 +148,7 @@ const abuseListValidator = [ | |||
146 | .custom(exists).withMessage('Should have a valid video channel search'), | 148 | .custom(exists).withMessage('Should have a valid video channel search'), |
147 | 149 | ||
148 | (req: express.Request, res: express.Response, next: express.NextFunction) => { | 150 | (req: express.Request, res: express.Response, next: express.NextFunction) => { |
149 | logger.debug('Checking abuseListValidator parameters', { parameters: req.body }) | 151 | logger.debug('Checking abuseListForAdminsValidator parameters', { parameters: req.body }) |
150 | 152 | ||
151 | if (areValidationErrors(req, res)) return | 153 | if (areValidationErrors(req, res)) return |
152 | 154 | ||
@@ -154,6 +156,91 @@ const abuseListValidator = [ | |||
154 | } | 156 | } |
155 | ] | 157 | ] |
156 | 158 | ||
159 | const abuseListForUserValidator = [ | ||
160 | query('id') | ||
161 | .optional() | ||
162 | .custom(isIdValid).withMessage('Should have a valid id'), | ||
163 | |||
164 | query('search') | ||
165 | .optional() | ||
166 | .custom(exists).withMessage('Should have a valid search'), | ||
167 | |||
168 | query('state') | ||
169 | .optional() | ||
170 | .custom(isAbuseStateValid).withMessage('Should have a valid abuse state'), | ||
171 | |||
172 | (req: express.Request, res: express.Response, next: express.NextFunction) => { | ||
173 | logger.debug('Checking abuseListForUserValidator parameters', { parameters: req.body }) | ||
174 | |||
175 | if (areValidationErrors(req, res)) return | ||
176 | |||
177 | return next() | ||
178 | } | ||
179 | ] | ||
180 | |||
181 | const getAbuseValidator = [ | ||
182 | param('id').custom(isIdValid).not().isEmpty().withMessage('Should have a valid id'), | ||
183 | |||
184 | async (req: express.Request, res: express.Response, next: express.NextFunction) => { | ||
185 | logger.debug('Checking getAbuseValidator parameters', { parameters: req.body }) | ||
186 | |||
187 | if (areValidationErrors(req, res)) return | ||
188 | if (!await doesAbuseExist(req.params.id, res)) return | ||
189 | |||
190 | const user = res.locals.oauth.token.user | ||
191 | const abuse = res.locals.abuse | ||
192 | |||
193 | if (user.hasRight(UserRight.MANAGE_ABUSES) !== true && abuse.reporterAccountId !== user.Account.id) { | ||
194 | const message = `User ${user.username} does not have right to get abuse ${abuse.id}` | ||
195 | logger.warn(message) | ||
196 | |||
197 | return res.status(403).json({ error: message }) | ||
198 | } | ||
199 | |||
200 | return next() | ||
201 | } | ||
202 | ] | ||
203 | |||
204 | const addAbuseMessageValidator = [ | ||
205 | body('message').custom(isAbuseMessageValid).not().isEmpty().withMessage('Should have a valid abuse message'), | ||
206 | |||
207 | (req: express.Request, res: express.Response, next: express.NextFunction) => { | ||
208 | logger.debug('Checking addAbuseMessageValidator parameters', { parameters: req.body }) | ||
209 | |||
210 | if (areValidationErrors(req, res)) return | ||
211 | |||
212 | return next() | ||
213 | } | ||
214 | ] | ||
215 | |||
216 | const deleteAbuseMessageValidator = [ | ||
217 | param('messageId').custom(isIdValid).not().isEmpty().withMessage('Should have a valid message id'), | ||
218 | |||
219 | async (req: express.Request, res: express.Response, next: express.NextFunction) => { | ||
220 | logger.debug('Checking deleteAbuseMessageValidator parameters', { parameters: req.body }) | ||
221 | |||
222 | if (areValidationErrors(req, res)) return | ||
223 | |||
224 | const user = res.locals.oauth.token.user | ||
225 | const abuse = res.locals.abuse | ||
226 | |||
227 | const messageId = parseInt(req.params.messageId + '', 10) | ||
228 | const abuseMessage = await AbuseMessageModel.loadByIdAndAbuseId(messageId, abuse.id) | ||
229 | |||
230 | if (!abuseMessage) { | ||
231 | return res.status(404).json({ error: 'Abuse message not found' }) | ||
232 | } | ||
233 | |||
234 | if (user.hasRight(UserRight.MANAGE_ABUSES) !== true && abuseMessage.accountId !== user.Account.id) { | ||
235 | return res.status(403).json({ error: 'Cannot delete this abuse message' }) | ||
236 | } | ||
237 | |||
238 | res.locals.abuseMessage = abuseMessage | ||
239 | |||
240 | return next() | ||
241 | } | ||
242 | ] | ||
243 | |||
157 | // FIXME: deprecated in 2.3. Remove these validators | 244 | // FIXME: deprecated in 2.3. Remove these validators |
158 | 245 | ||
159 | const videoAbuseReportValidator = [ | 246 | const videoAbuseReportValidator = [ |
@@ -167,7 +254,7 @@ const videoAbuseReportValidator = [ | |||
167 | .withMessage('Should have a valid reason'), | 254 | .withMessage('Should have a valid reason'), |
168 | body('predefinedReasons') | 255 | body('predefinedReasons') |
169 | .optional() | 256 | .optional() |
170 | .custom(isAbusePredefinedReasonsValid) | 257 | .custom(areAbusePredefinedReasonsValid) |
171 | .withMessage('Should have a valid list of predefined reasons'), | 258 | .withMessage('Should have a valid list of predefined reasons'), |
172 | body('startAt') | 259 | body('startAt') |
173 | .optional() | 260 | .optional() |
@@ -266,10 +353,14 @@ const videoAbuseListValidator = [ | |||
266 | // --------------------------------------------------------------------------- | 353 | // --------------------------------------------------------------------------- |
267 | 354 | ||
268 | export { | 355 | export { |
269 | abuseListValidator, | 356 | abuseListForAdminsValidator, |
270 | abuseReportValidator, | 357 | abuseReportValidator, |
271 | abuseGetValidator, | 358 | abuseGetValidator, |
359 | addAbuseMessageValidator, | ||
272 | abuseUpdateValidator, | 360 | abuseUpdateValidator, |
361 | deleteAbuseMessageValidator, | ||
362 | abuseListForUserValidator, | ||
363 | getAbuseValidator, | ||
273 | videoAbuseReportValidator, | 364 | videoAbuseReportValidator, |
274 | videoAbuseGetValidator, | 365 | videoAbuseGetValidator, |
275 | videoAbuseUpdateValidator, | 366 | videoAbuseUpdateValidator, |
diff --git a/server/models/abuse/abuse-message.ts b/server/models/abuse/abuse-message.ts new file mode 100644 index 000000000..f7721c87d --- /dev/null +++ b/server/models/abuse/abuse-message.ts | |||
@@ -0,0 +1,103 @@ | |||
1 | import { AllowNull, BelongsTo, Column, CreatedAt, DataType, ForeignKey, Is, Model, Table, UpdatedAt } from 'sequelize-typescript' | ||
2 | import { isAbuseMessageValid } from '@server/helpers/custom-validators/abuses' | ||
3 | import { AbuseMessage } from '@shared/models' | ||
4 | import { AccountModel, ScopeNames as AccountScopeNames } from '../account/account' | ||
5 | import { throwIfNotValid, getSort } from '../utils' | ||
6 | import { AbuseModel } from './abuse' | ||
7 | import { MAbuseMessageFormattable, MAbuseMessage } from '@server/types/models' | ||
8 | |||
9 | @Table({ | ||
10 | tableName: 'abuseMessage', | ||
11 | indexes: [ | ||
12 | { | ||
13 | fields: [ 'abuseId' ] | ||
14 | }, | ||
15 | { | ||
16 | fields: [ 'accountId' ] | ||
17 | } | ||
18 | ] | ||
19 | }) | ||
20 | export class AbuseMessageModel extends Model<AbuseMessageModel> { | ||
21 | |||
22 | @AllowNull(false) | ||
23 | @Is('AbuseMessage', value => throwIfNotValid(value, isAbuseMessageValid, 'message')) | ||
24 | @Column(DataType.TEXT) | ||
25 | message: string | ||
26 | |||
27 | @AllowNull(false) | ||
28 | @Column | ||
29 | byModerator: boolean | ||
30 | |||
31 | @CreatedAt | ||
32 | createdAt: Date | ||
33 | |||
34 | @UpdatedAt | ||
35 | updatedAt: Date | ||
36 | |||
37 | @ForeignKey(() => AccountModel) | ||
38 | @Column | ||
39 | accountId: number | ||
40 | |||
41 | @BelongsTo(() => AccountModel, { | ||
42 | foreignKey: { | ||
43 | name: 'accountId', | ||
44 | allowNull: true | ||
45 | }, | ||
46 | onDelete: 'set null' | ||
47 | }) | ||
48 | Account: AccountModel | ||
49 | |||
50 | @ForeignKey(() => AbuseModel) | ||
51 | @Column | ||
52 | abuseId: number | ||
53 | |||
54 | @BelongsTo(() => AbuseModel, { | ||
55 | foreignKey: { | ||
56 | name: 'abuseId', | ||
57 | allowNull: false | ||
58 | }, | ||
59 | onDelete: 'cascade' | ||
60 | }) | ||
61 | Abuse: AbuseModel | ||
62 | |||
63 | static listForApi (abuseId: number) { | ||
64 | const options = { | ||
65 | where: { abuseId }, | ||
66 | |||
67 | order: getSort('createdAt'), | ||
68 | |||
69 | include: [ | ||
70 | { | ||
71 | model: AccountModel.scope(AccountScopeNames.SUMMARY), | ||
72 | required: false | ||
73 | } | ||
74 | ] | ||
75 | } | ||
76 | |||
77 | return AbuseMessageModel.findAndCountAll(options) | ||
78 | .then(({ rows, count }) => ({ data: rows, total: count })) | ||
79 | } | ||
80 | |||
81 | static loadByIdAndAbuseId (messageId: number, abuseId: number): Promise<MAbuseMessage> { | ||
82 | return AbuseMessageModel.findOne({ | ||
83 | where: { | ||
84 | id: messageId, | ||
85 | abuseId | ||
86 | } | ||
87 | }) | ||
88 | } | ||
89 | |||
90 | toFormattedJSON (this: MAbuseMessageFormattable): AbuseMessage { | ||
91 | const account = this.Account | ||
92 | ? this.Account.toFormattedSummaryJSON() | ||
93 | : null | ||
94 | |||
95 | return { | ||
96 | id: this.id, | ||
97 | byModerator: this.byModerator, | ||
98 | message: this.message, | ||
99 | |||
100 | account | ||
101 | } | ||
102 | } | ||
103 | } | ||
diff --git a/server/models/abuse/abuse-query-builder.ts b/server/models/abuse/abuse-query-builder.ts index 5fddcf3c4..9d7cb75aa 100644 --- a/server/models/abuse/abuse-query-builder.ts +++ b/server/models/abuse/abuse-query-builder.ts | |||
@@ -26,8 +26,10 @@ export type BuildAbusesQueryOptions = { | |||
26 | state?: AbuseState | 26 | state?: AbuseState |
27 | 27 | ||
28 | // accountIds | 28 | // accountIds |
29 | serverAccountId: number | 29 | serverAccountId?: number |
30 | userAccountId: number | 30 | userAccountId?: number |
31 | |||
32 | reporterAccountId?: number | ||
31 | } | 33 | } |
32 | 34 | ||
33 | function buildAbuseListQuery (options: BuildAbusesQueryOptions, type: 'count' | 'id') { | 35 | function buildAbuseListQuery (options: BuildAbusesQueryOptions, type: 'count' | 'id') { |
@@ -45,7 +47,14 @@ function buildAbuseListQuery (options: BuildAbusesQueryOptions, type: 'count' | | |||
45 | 'LEFT JOIN "videoComment" ON "commentAbuse"."videoCommentId" = "videoComment"."id"' | 47 | 'LEFT JOIN "videoComment" ON "commentAbuse"."videoCommentId" = "videoComment"."id"' |
46 | ] | 48 | ] |
47 | 49 | ||
48 | whereAnd.push('"abuse"."reporterAccountId" NOT IN (' + buildBlockedAccountSQL([ options.serverAccountId, options.userAccountId ]) + ')') | 50 | if (options.serverAccountId || options.userAccountId) { |
51 | whereAnd.push('"abuse"."reporterAccountId" NOT IN (' + buildBlockedAccountSQL([ options.serverAccountId, options.userAccountId ]) + ')') | ||
52 | } | ||
53 | |||
54 | if (options.reporterAccountId) { | ||
55 | whereAnd.push('"abuse"."reporterAccountId" = :reporterAccountId') | ||
56 | replacements.reporterAccountId = options.reporterAccountId | ||
57 | } | ||
49 | 58 | ||
50 | if (options.search) { | 59 | if (options.search) { |
51 | const searchWhereOr = [ | 60 | const searchWhereOr = [ |
diff --git a/server/models/abuse/abuse.ts b/server/models/abuse/abuse.ts index bd96cf79c..7002502d5 100644 --- a/server/models/abuse/abuse.ts +++ b/server/models/abuse/abuse.ts | |||
@@ -18,7 +18,6 @@ import { | |||
18 | } from 'sequelize-typescript' | 18 | } from 'sequelize-typescript' |
19 | import { isAbuseModerationCommentValid, isAbuseReasonValid, isAbuseStateValid } from '@server/helpers/custom-validators/abuses' | 19 | import { isAbuseModerationCommentValid, isAbuseReasonValid, isAbuseStateValid } from '@server/helpers/custom-validators/abuses' |
20 | import { | 20 | import { |
21 | Abuse, | ||
22 | AbuseFilter, | 21 | AbuseFilter, |
23 | AbuseObject, | 22 | AbuseObject, |
24 | AbusePredefinedReasons, | 23 | AbusePredefinedReasons, |
@@ -26,11 +25,14 @@ import { | |||
26 | AbusePredefinedReasonsString, | 25 | AbusePredefinedReasonsString, |
27 | AbuseState, | 26 | AbuseState, |
28 | AbuseVideoIs, | 27 | AbuseVideoIs, |
29 | VideoAbuse, | 28 | AdminVideoAbuse, |
30 | VideoCommentAbuse | 29 | AdminAbuse, |
30 | AdminVideoCommentAbuse, | ||
31 | UserAbuse, | ||
32 | UserVideoAbuse | ||
31 | } from '@shared/models' | 33 | } from '@shared/models' |
32 | import { ABUSE_STATES, CONSTRAINTS_FIELDS } from '../../initializers/constants' | 34 | import { ABUSE_STATES, CONSTRAINTS_FIELDS } from '../../initializers/constants' |
33 | import { MAbuse, MAbuseAP, MAbuseFormattable, MUserAccountId } from '../../types/models' | 35 | import { MAbuse, MAbuseAdminFormattable, MAbuseAP, MUserAccountId, MAbuseUserFormattable } from '../../types/models' |
34 | import { AccountModel, ScopeNames as AccountScopeNames, SummaryOptions as AccountSummaryOptions } from '../account/account' | 36 | import { AccountModel, ScopeNames as AccountScopeNames, SummaryOptions as AccountSummaryOptions } from '../account/account' |
35 | import { getSort, throwIfNotValid } from '../utils' | 37 | import { getSort, throwIfNotValid } from '../utils' |
36 | import { ThumbnailModel } from '../video/thumbnail' | 38 | import { ThumbnailModel } from '../video/thumbnail' |
@@ -52,6 +54,16 @@ export enum ScopeNames { | |||
52 | attributes: { | 54 | attributes: { |
53 | include: [ | 55 | include: [ |
54 | [ | 56 | [ |
57 | literal( | ||
58 | '(' + | ||
59 | 'SELECT count(*) ' + | ||
60 | 'FROM "abuseMessage" ' + | ||
61 | 'WHERE "abuseId" = "AbuseModel"."id"' + | ||
62 | ')' | ||
63 | ), | ||
64 | 'countMessages' | ||
65 | ], | ||
66 | [ | ||
55 | // we don't care about this count for deleted videos, so there are not included | 67 | // we don't care about this count for deleted videos, so there are not included |
56 | literal( | 68 | literal( |
57 | '(' + | 69 | '(' + |
@@ -285,7 +297,7 @@ export class AbuseModel extends Model<AbuseModel> { | |||
285 | return AbuseModel.findOne(query) | 297 | return AbuseModel.findOne(query) |
286 | } | 298 | } |
287 | 299 | ||
288 | static async listForApi (parameters: { | 300 | static async listForAdminApi (parameters: { |
289 | start: number | 301 | start: number |
290 | count: number | 302 | count: number |
291 | sort: string | 303 | sort: string |
@@ -353,71 +365,98 @@ export class AbuseModel extends Model<AbuseModel> { | |||
353 | return { total, data } | 365 | return { total, data } |
354 | } | 366 | } |
355 | 367 | ||
356 | toFormattedJSON (this: MAbuseFormattable): Abuse { | 368 | static async listForUserApi (parameters: { |
357 | const predefinedReasons = AbuseModel.getPredefinedReasonsStrings(this.predefinedReasons) | 369 | user: MUserAccountId |
358 | 370 | ||
359 | const countReportsForVideo = this.get('countReportsForVideo') as number | 371 | start: number |
360 | const nthReportForVideo = this.get('nthReportForVideo') as number | 372 | count: number |
373 | sort: string | ||
361 | 374 | ||
362 | const countReportsForReporter = this.get('countReportsForReporter') as number | 375 | id?: number |
363 | const countReportsForReportee = this.get('countReportsForReportee') as number | 376 | search?: string |
377 | state?: AbuseState | ||
378 | }) { | ||
379 | const { | ||
380 | start, | ||
381 | count, | ||
382 | sort, | ||
383 | search, | ||
384 | user, | ||
385 | state, | ||
386 | id | ||
387 | } = parameters | ||
364 | 388 | ||
365 | let video: VideoAbuse = null | 389 | const queryOptions: BuildAbusesQueryOptions = { |
366 | let comment: VideoCommentAbuse = null | 390 | start, |
391 | count, | ||
392 | sort, | ||
393 | id, | ||
394 | search, | ||
395 | state, | ||
396 | reporterAccountId: user.Account.id | ||
397 | } | ||
398 | |||
399 | const [ total, data ] = await Promise.all([ | ||
400 | AbuseModel.internalCountForApi(queryOptions), | ||
401 | AbuseModel.internalListForApi(queryOptions) | ||
402 | ]) | ||
403 | |||
404 | return { total, data } | ||
405 | } | ||
367 | 406 | ||
368 | if (this.VideoAbuse) { | 407 | buildBaseVideoCommentAbuse (this: MAbuseUserFormattable) { |
369 | const abuseModel = this.VideoAbuse | 408 | if (!this.VideoCommentAbuse) return null |
370 | const entity = abuseModel.Video || abuseModel.deletedVideo | ||
371 | 409 | ||
372 | video = { | 410 | const abuseModel = this.VideoCommentAbuse |
373 | id: entity.id, | 411 | const entity = abuseModel.VideoComment |
374 | uuid: entity.uuid, | ||
375 | name: entity.name, | ||
376 | nsfw: entity.nsfw, | ||
377 | 412 | ||
378 | startAt: abuseModel.startAt, | 413 | return { |
379 | endAt: abuseModel.endAt, | 414 | id: entity.id, |
415 | threadId: entity.getThreadId(), | ||
380 | 416 | ||
381 | deleted: !abuseModel.Video, | 417 | text: entity.text ?? '', |
382 | blacklisted: abuseModel.Video?.isBlacklisted() || false, | ||
383 | thumbnailPath: abuseModel.Video?.getMiniatureStaticPath(), | ||
384 | 418 | ||
385 | channel: abuseModel.Video?.VideoChannel.toFormattedJSON() || abuseModel.deletedVideo?.channel, | 419 | deleted: entity.isDeleted(), |
386 | 420 | ||
387 | countReports: countReportsForVideo, | 421 | video: { |
388 | nthReport: nthReportForVideo | 422 | id: entity.Video.id, |
423 | name: entity.Video.name, | ||
424 | uuid: entity.Video.uuid | ||
389 | } | 425 | } |
390 | } | 426 | } |
427 | } | ||
391 | 428 | ||
392 | if (this.VideoCommentAbuse) { | 429 | buildBaseVideoAbuse (this: MAbuseUserFormattable): UserVideoAbuse { |
393 | const abuseModel = this.VideoCommentAbuse | 430 | if (!this.VideoAbuse) return null |
394 | const entity = abuseModel.VideoComment | ||
395 | 431 | ||
396 | comment = { | 432 | const abuseModel = this.VideoAbuse |
397 | id: entity.id, | 433 | const entity = abuseModel.Video || abuseModel.deletedVideo |
398 | threadId: entity.getThreadId(), | ||
399 | 434 | ||
400 | text: entity.text ?? '', | 435 | return { |
436 | id: entity.id, | ||
437 | uuid: entity.uuid, | ||
438 | name: entity.name, | ||
439 | nsfw: entity.nsfw, | ||
401 | 440 | ||
402 | deleted: entity.isDeleted(), | 441 | startAt: abuseModel.startAt, |
442 | endAt: abuseModel.endAt, | ||
403 | 443 | ||
404 | video: { | 444 | deleted: !abuseModel.Video, |
405 | id: entity.Video.id, | 445 | blacklisted: abuseModel.Video?.isBlacklisted() || false, |
406 | name: entity.Video.name, | 446 | thumbnailPath: abuseModel.Video?.getMiniatureStaticPath(), |
407 | uuid: entity.Video.uuid | 447 | |
408 | } | 448 | channel: abuseModel.Video?.VideoChannel.toFormattedJSON() || abuseModel.deletedVideo?.channel, |
409 | } | ||
410 | } | 449 | } |
450 | } | ||
451 | |||
452 | buildBaseAbuse (this: MAbuseUserFormattable, countMessages: number): UserAbuse { | ||
453 | const predefinedReasons = AbuseModel.getPredefinedReasonsStrings(this.predefinedReasons) | ||
411 | 454 | ||
412 | return { | 455 | return { |
413 | id: this.id, | 456 | id: this.id, |
414 | reason: this.reason, | 457 | reason: this.reason, |
415 | predefinedReasons, | 458 | predefinedReasons, |
416 | 459 | ||
417 | reporterAccount: this.ReporterAccount | ||
418 | ? this.ReporterAccount.toFormattedJSON() | ||
419 | : null, | ||
420 | |||
421 | flaggedAccount: this.FlaggedAccount | 460 | flaggedAccount: this.FlaggedAccount |
422 | ? this.FlaggedAccount.toFormattedJSON() | 461 | ? this.FlaggedAccount.toFormattedJSON() |
423 | : null, | 462 | : null, |
@@ -429,11 +468,41 @@ export class AbuseModel extends Model<AbuseModel> { | |||
429 | 468 | ||
430 | moderationComment: this.moderationComment, | 469 | moderationComment: this.moderationComment, |
431 | 470 | ||
471 | countMessages, | ||
472 | |||
473 | createdAt: this.createdAt, | ||
474 | updatedAt: this.updatedAt | ||
475 | } | ||
476 | } | ||
477 | |||
478 | toFormattedAdminJSON (this: MAbuseAdminFormattable): AdminAbuse { | ||
479 | const countReportsForVideo = this.get('countReportsForVideo') as number | ||
480 | const nthReportForVideo = this.get('nthReportForVideo') as number | ||
481 | |||
482 | const countReportsForReporter = this.get('countReportsForReporter') as number | ||
483 | const countReportsForReportee = this.get('countReportsForReportee') as number | ||
484 | |||
485 | const countMessages = this.get('countMessages') as number | ||
486 | |||
487 | const baseVideo = this.buildBaseVideoAbuse() | ||
488 | const video: AdminVideoAbuse = baseVideo | ||
489 | ? Object.assign(baseVideo, { | ||
490 | countReports: countReportsForVideo, | ||
491 | nthReport: nthReportForVideo | ||
492 | }) | ||
493 | : null | ||
494 | |||
495 | const comment: AdminVideoCommentAbuse = this.buildBaseVideoCommentAbuse() | ||
496 | |||
497 | const abuse = this.buildBaseAbuse(countMessages || 0) | ||
498 | |||
499 | return Object.assign(abuse, { | ||
432 | video, | 500 | video, |
433 | comment, | 501 | comment, |
434 | 502 | ||
435 | createdAt: this.createdAt, | 503 | reporterAccount: this.ReporterAccount |
436 | updatedAt: this.updatedAt, | 504 | ? this.ReporterAccount.toFormattedJSON() |
505 | : null, | ||
437 | 506 | ||
438 | countReportsForReporter: (countReportsForReporter || 0), | 507 | countReportsForReporter: (countReportsForReporter || 0), |
439 | countReportsForReportee: (countReportsForReportee || 0), | 508 | countReportsForReportee: (countReportsForReportee || 0), |
@@ -443,7 +512,20 @@ export class AbuseModel extends Model<AbuseModel> { | |||
443 | endAt: null, | 512 | endAt: null, |
444 | count: countReportsForVideo || 0, | 513 | count: countReportsForVideo || 0, |
445 | nth: nthReportForVideo || 0 | 514 | nth: nthReportForVideo || 0 |
446 | } | 515 | }) |
516 | } | ||
517 | |||
518 | toFormattedUserJSON (this: MAbuseUserFormattable): UserAbuse { | ||
519 | const countMessages = this.get('countMessages') as number | ||
520 | |||
521 | const video = this.buildBaseVideoAbuse() | ||
522 | const comment: AdminVideoCommentAbuse = this.buildBaseVideoCommentAbuse() | ||
523 | const abuse = this.buildBaseAbuse(countMessages || 0) | ||
524 | |||
525 | return Object.assign(abuse, { | ||
526 | video, | ||
527 | comment | ||
528 | }) | ||
447 | } | 529 | } |
448 | 530 | ||
449 | toActivityPubObject (this: MAbuseAP): AbuseObject { | 531 | toActivityPubObject (this: MAbuseAP): AbuseObject { |
diff --git a/server/tests/api/check-params/abuses.ts b/server/tests/api/check-params/abuses.ts index 8964c0ab2..5e1d66c25 100644 --- a/server/tests/api/check-params/abuses.ts +++ b/server/tests/api/check-params/abuses.ts | |||
@@ -13,7 +13,11 @@ import { | |||
13 | setAccessTokensToServers, | 13 | setAccessTokensToServers, |
14 | updateAbuse, | 14 | updateAbuse, |
15 | uploadVideo, | 15 | uploadVideo, |
16 | userLogin | 16 | userLogin, |
17 | generateUserAccessToken, | ||
18 | addAbuseMessage, | ||
19 | listAbuseMessages, | ||
20 | deleteAbuseMessage | ||
17 | } from '../../../../shared/extra-utils' | 21 | } from '../../../../shared/extra-utils' |
18 | import { | 22 | import { |
19 | checkBadCountPagination, | 23 | checkBadCountPagination, |
@@ -26,7 +30,9 @@ describe('Test abuses API validators', function () { | |||
26 | 30 | ||
27 | let server: ServerInfo | 31 | let server: ServerInfo |
28 | let userAccessToken = '' | 32 | let userAccessToken = '' |
33 | let userAccessToken2 = '' | ||
29 | let abuseId: number | 34 | let abuseId: number |
35 | let messageId: number | ||
30 | 36 | ||
31 | // --------------------------------------------------------------- | 37 | // --------------------------------------------------------------- |
32 | 38 | ||
@@ -42,11 +48,15 @@ describe('Test abuses API validators', function () { | |||
42 | await createUser({ url: server.url, accessToken: server.accessToken, username: username, password: password }) | 48 | await createUser({ url: server.url, accessToken: server.accessToken, username: username, password: password }) |
43 | userAccessToken = await userLogin(server, { username, password }) | 49 | userAccessToken = await userLogin(server, { username, password }) |
44 | 50 | ||
51 | { | ||
52 | userAccessToken2 = await generateUserAccessToken(server, 'user_2') | ||
53 | } | ||
54 | |||
45 | const res = await uploadVideo(server.url, server.accessToken, {}) | 55 | const res = await uploadVideo(server.url, server.accessToken, {}) |
46 | server.video = res.body.video | 56 | server.video = res.body.video |
47 | }) | 57 | }) |
48 | 58 | ||
49 | describe('When listing abuses', function () { | 59 | describe('When listing abuses for admins', function () { |
50 | const path = basePath | 60 | const path = basePath |
51 | 61 | ||
52 | it('Should fail with a bad start pagination', async function () { | 62 | it('Should fail with a bad start pagination', async function () { |
@@ -113,47 +123,89 @@ describe('Test abuses API validators', function () { | |||
113 | }) | 123 | }) |
114 | }) | 124 | }) |
115 | 125 | ||
126 | describe('When listing abuses for users', function () { | ||
127 | const path = '/api/v1/users/me/abuses' | ||
128 | |||
129 | it('Should fail with a bad start pagination', async function () { | ||
130 | await checkBadStartPagination(server.url, path, userAccessToken) | ||
131 | }) | ||
132 | |||
133 | it('Should fail with a bad count pagination', async function () { | ||
134 | await checkBadCountPagination(server.url, path, userAccessToken) | ||
135 | }) | ||
136 | |||
137 | it('Should fail with an incorrect sort', async function () { | ||
138 | await checkBadSortPagination(server.url, path, userAccessToken) | ||
139 | }) | ||
140 | |||
141 | it('Should fail with a non authenticated user', async function () { | ||
142 | await makeGetRequest({ | ||
143 | url: server.url, | ||
144 | path, | ||
145 | statusCodeExpected: 401 | ||
146 | }) | ||
147 | }) | ||
148 | |||
149 | it('Should fail with a bad id filter', async function () { | ||
150 | await makeGetRequest({ url: server.url, path, token: userAccessToken, query: { id: 'toto' } }) | ||
151 | }) | ||
152 | |||
153 | it('Should fail with a bad state filter', async function () { | ||
154 | await makeGetRequest({ url: server.url, path, token: userAccessToken, query: { state: 'toto' } }) | ||
155 | await makeGetRequest({ url: server.url, path, token: userAccessToken, query: { state: 0 } }) | ||
156 | }) | ||
157 | |||
158 | it('Should succeed with the correct params', async function () { | ||
159 | const query = { | ||
160 | id: 13, | ||
161 | state: 2 | ||
162 | } | ||
163 | |||
164 | await makeGetRequest({ url: server.url, path, token: userAccessToken, query, statusCodeExpected: 200 }) | ||
165 | }) | ||
166 | }) | ||
167 | |||
116 | describe('When reporting an abuse', function () { | 168 | describe('When reporting an abuse', function () { |
117 | const path = basePath | 169 | const path = basePath |
118 | 170 | ||
119 | it('Should fail with nothing', async function () { | 171 | it('Should fail with nothing', async function () { |
120 | const fields = {} | 172 | const fields = {} |
121 | await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields }) | 173 | await makePostBodyRequest({ url: server.url, path, token: userAccessToken, fields }) |
122 | }) | 174 | }) |
123 | 175 | ||
124 | it('Should fail with a wrong video', async function () { | 176 | it('Should fail with a wrong video', async function () { |
125 | const fields = { video: { id: 'blabla' }, reason: 'my super reason' } | 177 | const fields = { video: { id: 'blabla' }, reason: 'my super reason' } |
126 | await makePostBodyRequest({ url: server.url, path: path, token: server.accessToken, fields }) | 178 | await makePostBodyRequest({ url: server.url, path: path, token: userAccessToken, fields }) |
127 | }) | 179 | }) |
128 | 180 | ||
129 | it('Should fail with an unknown video', async function () { | 181 | it('Should fail with an unknown video', async function () { |
130 | const fields = { video: { id: 42 }, reason: 'my super reason' } | 182 | const fields = { video: { id: 42 }, reason: 'my super reason' } |
131 | await makePostBodyRequest({ url: server.url, path: path, token: server.accessToken, fields, statusCodeExpected: 404 }) | 183 | await makePostBodyRequest({ url: server.url, path: path, token: userAccessToken, fields, statusCodeExpected: 404 }) |
132 | }) | 184 | }) |
133 | 185 | ||
134 | it('Should fail with a wrong comment', async function () { | 186 | it('Should fail with a wrong comment', async function () { |
135 | const fields = { comment: { id: 'blabla' }, reason: 'my super reason' } | 187 | const fields = { comment: { id: 'blabla' }, reason: 'my super reason' } |
136 | await makePostBodyRequest({ url: server.url, path: path, token: server.accessToken, fields }) | 188 | await makePostBodyRequest({ url: server.url, path: path, token: userAccessToken, fields }) |
137 | }) | 189 | }) |
138 | 190 | ||
139 | it('Should fail with an unknown comment', async function () { | 191 | it('Should fail with an unknown comment', async function () { |
140 | const fields = { comment: { id: 42 }, reason: 'my super reason' } | 192 | const fields = { comment: { id: 42 }, reason: 'my super reason' } |
141 | await makePostBodyRequest({ url: server.url, path: path, token: server.accessToken, fields, statusCodeExpected: 404 }) | 193 | await makePostBodyRequest({ url: server.url, path: path, token: userAccessToken, fields, statusCodeExpected: 404 }) |
142 | }) | 194 | }) |
143 | 195 | ||
144 | it('Should fail with a wrong account', async function () { | 196 | it('Should fail with a wrong account', async function () { |
145 | const fields = { account: { id: 'blabla' }, reason: 'my super reason' } | 197 | const fields = { account: { id: 'blabla' }, reason: 'my super reason' } |
146 | await makePostBodyRequest({ url: server.url, path: path, token: server.accessToken, fields }) | 198 | await makePostBodyRequest({ url: server.url, path: path, token: userAccessToken, fields }) |
147 | }) | 199 | }) |
148 | 200 | ||
149 | it('Should fail with an unknown account', async function () { | 201 | it('Should fail with an unknown account', async function () { |
150 | const fields = { account: { id: 42 }, reason: 'my super reason' } | 202 | const fields = { account: { id: 42 }, reason: 'my super reason' } |
151 | await makePostBodyRequest({ url: server.url, path: path, token: server.accessToken, fields, statusCodeExpected: 404 }) | 203 | await makePostBodyRequest({ url: server.url, path: path, token: userAccessToken, fields, statusCodeExpected: 404 }) |
152 | }) | 204 | }) |
153 | 205 | ||
154 | it('Should fail with not account, comment or video', async function () { | 206 | it('Should fail with not account, comment or video', async function () { |
155 | const fields = { reason: 'my super reason' } | 207 | const fields = { reason: 'my super reason' } |
156 | await makePostBodyRequest({ url: server.url, path: path, token: server.accessToken, fields, statusCodeExpected: 400 }) | 208 | await makePostBodyRequest({ url: server.url, path: path, token: userAccessToken, fields, statusCodeExpected: 400 }) |
157 | }) | 209 | }) |
158 | 210 | ||
159 | it('Should fail with a non authenticated user', async function () { | 211 | it('Should fail with a non authenticated user', async function () { |
@@ -165,38 +217,38 @@ describe('Test abuses API validators', function () { | |||
165 | it('Should fail with a reason too short', async function () { | 217 | it('Should fail with a reason too short', async function () { |
166 | const fields = { video: { id: server.video.id }, reason: 'h' } | 218 | const fields = { video: { id: server.video.id }, reason: 'h' } |
167 | 219 | ||
168 | await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields }) | 220 | await makePostBodyRequest({ url: server.url, path, token: userAccessToken, fields }) |
169 | }) | 221 | }) |
170 | 222 | ||
171 | it('Should fail with a too big reason', async function () { | 223 | it('Should fail with a too big reason', async function () { |
172 | const fields = { video: { id: server.video.id }, reason: 'super'.repeat(605) } | 224 | const fields = { video: { id: server.video.id }, reason: 'super'.repeat(605) } |
173 | 225 | ||
174 | await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields }) | 226 | await makePostBodyRequest({ url: server.url, path, token: userAccessToken, fields }) |
175 | }) | 227 | }) |
176 | 228 | ||
177 | it('Should succeed with the correct parameters (basic)', async function () { | 229 | it('Should succeed with the correct parameters (basic)', async function () { |
178 | const fields: AbuseCreate = { video: { id: server.video.id }, reason: 'my super reason' } | 230 | const fields: AbuseCreate = { video: { id: server.video.id }, reason: 'my super reason' } |
179 | 231 | ||
180 | const res = await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields, statusCodeExpected: 200 }) | 232 | const res = await makePostBodyRequest({ url: server.url, path, token: userAccessToken, fields, statusCodeExpected: 200 }) |
181 | abuseId = res.body.abuse.id | 233 | abuseId = res.body.abuse.id |
182 | }) | 234 | }) |
183 | 235 | ||
184 | it('Should fail with a wrong predefined reason', async function () { | 236 | it('Should fail with a wrong predefined reason', async function () { |
185 | const fields = { video: { id: server.video.id }, reason: 'my super reason', predefinedReasons: [ 'wrongPredefinedReason' ] } | 237 | const fields = { video: { id: server.video.id }, reason: 'my super reason', predefinedReasons: [ 'wrongPredefinedReason' ] } |
186 | 238 | ||
187 | await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields }) | 239 | await makePostBodyRequest({ url: server.url, path, token: userAccessToken, fields }) |
188 | }) | 240 | }) |
189 | 241 | ||
190 | it('Should fail with negative timestamps', async function () { | 242 | it('Should fail with negative timestamps', async function () { |
191 | const fields = { video: { id: server.video.id, startAt: -1 }, reason: 'my super reason' } | 243 | const fields = { video: { id: server.video.id, startAt: -1 }, reason: 'my super reason' } |
192 | 244 | ||
193 | await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields }) | 245 | await makePostBodyRequest({ url: server.url, path, token: userAccessToken, fields }) |
194 | }) | 246 | }) |
195 | 247 | ||
196 | it('Should fail mith misordered startAt/endAt', async function () { | 248 | it('Should fail mith misordered startAt/endAt', async function () { |
197 | const fields = { video: { id: server.video.id, startAt: 5, endAt: 1 }, reason: 'my super reason' } | 249 | const fields = { video: { id: server.video.id, startAt: 5, endAt: 1 }, reason: 'my super reason' } |
198 | 250 | ||
199 | await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields }) | 251 | await makePostBodyRequest({ url: server.url, path, token: userAccessToken, fields }) |
200 | }) | 252 | }) |
201 | 253 | ||
202 | it('Should succeed with the corret parameters (advanced)', async function () { | 254 | it('Should succeed with the corret parameters (advanced)', async function () { |
@@ -210,7 +262,7 @@ describe('Test abuses API validators', function () { | |||
210 | predefinedReasons: [ 'serverRules' ] | 262 | predefinedReasons: [ 'serverRules' ] |
211 | } | 263 | } |
212 | 264 | ||
213 | await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields, statusCodeExpected: 200 }) | 265 | await makePostBodyRequest({ url: server.url, path, token: userAccessToken, fields, statusCodeExpected: 200 }) |
214 | }) | 266 | }) |
215 | }) | 267 | }) |
216 | 268 | ||
@@ -244,6 +296,73 @@ describe('Test abuses API validators', function () { | |||
244 | }) | 296 | }) |
245 | }) | 297 | }) |
246 | 298 | ||
299 | describe('When creating an abuse message', function () { | ||
300 | const message = 'my super message' | ||
301 | |||
302 | it('Should fail with an invalid abuse id', async function () { | ||
303 | await addAbuseMessage(server.url, userAccessToken2, 888, message, 404) | ||
304 | }) | ||
305 | |||
306 | it('Should fail with a non authenticated user', async function () { | ||
307 | await addAbuseMessage(server.url, 'fake_token', abuseId, message, 401) | ||
308 | }) | ||
309 | |||
310 | it('Should fail with an invalid logged in user', async function () { | ||
311 | await addAbuseMessage(server.url, userAccessToken2, abuseId, message, 403) | ||
312 | }) | ||
313 | |||
314 | it('Should fail with an invalid message', async function () { | ||
315 | await addAbuseMessage(server.url, userAccessToken, abuseId, 'a'.repeat(5000), 400) | ||
316 | }) | ||
317 | |||
318 | it('Should suceed with the correct params', async function () { | ||
319 | const res = await addAbuseMessage(server.url, userAccessToken, abuseId, message) | ||
320 | messageId = res.body.abuseMessage.id | ||
321 | }) | ||
322 | }) | ||
323 | |||
324 | describe('When listing abuse message', function () { | ||
325 | |||
326 | it('Should fail with an invalid abuse id', async function () { | ||
327 | await listAbuseMessages(server.url, userAccessToken, 888, 404) | ||
328 | }) | ||
329 | |||
330 | it('Should fail with a non authenticated user', async function () { | ||
331 | await listAbuseMessages(server.url, 'fake_token', abuseId, 401) | ||
332 | }) | ||
333 | |||
334 | it('Should fail with an invalid logged in user', async function () { | ||
335 | await listAbuseMessages(server.url, userAccessToken2, abuseId, 403) | ||
336 | }) | ||
337 | |||
338 | it('Should succeed with the correct params', async function () { | ||
339 | await listAbuseMessages(server.url, userAccessToken, abuseId) | ||
340 | }) | ||
341 | }) | ||
342 | |||
343 | describe('When deleting an abuse message', function () { | ||
344 | |||
345 | it('Should fail with an invalid abuse id', async function () { | ||
346 | await deleteAbuseMessage(server.url, userAccessToken, 888, messageId, 404) | ||
347 | }) | ||
348 | |||
349 | it('Should fail with an invalid message id', async function () { | ||
350 | await deleteAbuseMessage(server.url, userAccessToken, abuseId, 888, 404) | ||
351 | }) | ||
352 | |||
353 | it('Should fail with a non authenticated user', async function () { | ||
354 | await deleteAbuseMessage(server.url, 'fake_token', abuseId, messageId, 401) | ||
355 | }) | ||
356 | |||
357 | it('Should fail with an invalid logged in user', async function () { | ||
358 | await deleteAbuseMessage(server.url, userAccessToken2, abuseId, messageId, 403) | ||
359 | }) | ||
360 | |||
361 | it('Should succeed with the correct params', async function () { | ||
362 | await deleteAbuseMessage(server.url, userAccessToken, abuseId, messageId) | ||
363 | }) | ||
364 | }) | ||
365 | |||
247 | describe('When deleting a video abuse', function () { | 366 | describe('When deleting a video abuse', function () { |
248 | 367 | ||
249 | it('Should fail with a non authenticated user', async function () { | 368 | it('Should fail with a non authenticated user', async function () { |
diff --git a/server/tests/api/moderation/abuses.ts b/server/tests/api/moderation/abuses.ts index f186f7ea0..601125fdf 100644 --- a/server/tests/api/moderation/abuses.ts +++ b/server/tests/api/moderation/abuses.ts | |||
@@ -2,7 +2,7 @@ | |||
2 | 2 | ||
3 | import 'mocha' | 3 | import 'mocha' |
4 | import * as chai from 'chai' | 4 | import * as chai from 'chai' |
5 | import { Abuse, AbuseFilter, AbusePredefinedReasonsString, AbuseState, VideoComment, Account } from '@shared/models' | 5 | import { AbuseFilter, AbusePredefinedReasonsString, AbuseState, Account, AdminAbuse, UserAbuse, VideoComment, AbuseMessage } from '@shared/models' |
6 | import { | 6 | import { |
7 | addVideoCommentThread, | 7 | addVideoCommentThread, |
8 | cleanupTests, | 8 | cleanupTests, |
@@ -10,11 +10,15 @@ import { | |||
10 | deleteAbuse, | 10 | deleteAbuse, |
11 | deleteVideoComment, | 11 | deleteVideoComment, |
12 | flushAndRunMultipleServers, | 12 | flushAndRunMultipleServers, |
13 | getAbusesList, | 13 | generateUserAccessToken, |
14 | getAccount, | ||
15 | getAdminAbusesList, | ||
16 | getUserAbusesList, | ||
14 | getVideoCommentThreads, | 17 | getVideoCommentThreads, |
15 | getVideoIdFromUUID, | 18 | getVideoIdFromUUID, |
16 | getVideosList, | 19 | getVideosList, |
17 | immutableAssign, | 20 | immutableAssign, |
21 | removeUser, | ||
18 | removeVideo, | 22 | removeVideo, |
19 | reportAbuse, | 23 | reportAbuse, |
20 | ServerInfo, | 24 | ServerInfo, |
@@ -23,9 +27,9 @@ import { | |||
23 | uploadVideo, | 27 | uploadVideo, |
24 | uploadVideoAndGetId, | 28 | uploadVideoAndGetId, |
25 | userLogin, | 29 | userLogin, |
26 | getAccount, | 30 | addAbuseMessage, |
27 | removeUser, | 31 | listAbuseMessages, |
28 | generateUserAccessToken | 32 | deleteAbuseMessage |
29 | } from '../../../../shared/extra-utils/index' | 33 | } from '../../../../shared/extra-utils/index' |
30 | import { doubleFollow } from '../../../../shared/extra-utils/server/follows' | 34 | import { doubleFollow } from '../../../../shared/extra-utils/server/follows' |
31 | import { waitJobs } from '../../../../shared/extra-utils/server/jobs' | 35 | import { waitJobs } from '../../../../shared/extra-utils/server/jobs' |
@@ -40,8 +44,8 @@ const expect = chai.expect | |||
40 | 44 | ||
41 | describe('Test abuses', function () { | 45 | describe('Test abuses', function () { |
42 | let servers: ServerInfo[] = [] | 46 | let servers: ServerInfo[] = [] |
43 | let abuseServer1: Abuse | 47 | let abuseServer1: AdminAbuse |
44 | let abuseServer2: Abuse | 48 | let abuseServer2: AdminAbuse |
45 | 49 | ||
46 | before(async function () { | 50 | before(async function () { |
47 | this.timeout(50000) | 51 | this.timeout(50000) |
@@ -87,7 +91,7 @@ describe('Test abuses', function () { | |||
87 | }) | 91 | }) |
88 | 92 | ||
89 | it('Should not have abuses', async function () { | 93 | it('Should not have abuses', async function () { |
90 | const res = await getAbusesList({ url: servers[0].url, token: servers[0].accessToken }) | 94 | const res = await getAdminAbusesList({ url: servers[0].url, token: servers[0].accessToken }) |
91 | 95 | ||
92 | expect(res.body.total).to.equal(0) | 96 | expect(res.body.total).to.equal(0) |
93 | expect(res.body.data).to.be.an('array') | 97 | expect(res.body.data).to.be.an('array') |
@@ -105,13 +109,13 @@ describe('Test abuses', function () { | |||
105 | }) | 109 | }) |
106 | 110 | ||
107 | it('Should have 1 video abuses on server 1 and 0 on server 2', async function () { | 111 | it('Should have 1 video abuses on server 1 and 0 on server 2', async function () { |
108 | const res1 = await getAbusesList({ url: servers[0].url, token: servers[0].accessToken }) | 112 | const res1 = await getAdminAbusesList({ url: servers[0].url, token: servers[0].accessToken }) |
109 | 113 | ||
110 | expect(res1.body.total).to.equal(1) | 114 | expect(res1.body.total).to.equal(1) |
111 | expect(res1.body.data).to.be.an('array') | 115 | expect(res1.body.data).to.be.an('array') |
112 | expect(res1.body.data.length).to.equal(1) | 116 | expect(res1.body.data.length).to.equal(1) |
113 | 117 | ||
114 | const abuse: Abuse = res1.body.data[0] | 118 | const abuse: AdminAbuse = res1.body.data[0] |
115 | expect(abuse.reason).to.equal('my super bad reason') | 119 | expect(abuse.reason).to.equal('my super bad reason') |
116 | 120 | ||
117 | expect(abuse.reporterAccount.name).to.equal('root') | 121 | expect(abuse.reporterAccount.name).to.equal('root') |
@@ -131,7 +135,7 @@ describe('Test abuses', function () { | |||
131 | expect(abuse.countReportsForReporter).to.equal(1) | 135 | expect(abuse.countReportsForReporter).to.equal(1) |
132 | expect(abuse.countReportsForReportee).to.equal(1) | 136 | expect(abuse.countReportsForReportee).to.equal(1) |
133 | 137 | ||
134 | const res2 = await getAbusesList({ url: servers[1].url, token: servers[1].accessToken }) | 138 | const res2 = await getAdminAbusesList({ url: servers[1].url, token: servers[1].accessToken }) |
135 | expect(res2.body.total).to.equal(0) | 139 | expect(res2.body.total).to.equal(0) |
136 | expect(res2.body.data).to.be.an('array') | 140 | expect(res2.body.data).to.be.an('array') |
137 | expect(res2.body.data.length).to.equal(0) | 141 | expect(res2.body.data.length).to.equal(0) |
@@ -141,19 +145,20 @@ describe('Test abuses', function () { | |||
141 | this.timeout(10000) | 145 | this.timeout(10000) |
142 | 146 | ||
143 | const reason = 'my super bad reason 2' | 147 | const reason = 'my super bad reason 2' |
144 | await reportAbuse({ url: servers[0].url, token: servers[0].accessToken, videoId: servers[1].video.id, reason }) | 148 | const videoId = await getVideoIdFromUUID(servers[0].url, servers[1].video.uuid) |
149 | await reportAbuse({ url: servers[0].url, token: servers[0].accessToken, videoId, reason }) | ||
145 | 150 | ||
146 | // We wait requests propagation | 151 | // We wait requests propagation |
147 | await waitJobs(servers) | 152 | await waitJobs(servers) |
148 | }) | 153 | }) |
149 | 154 | ||
150 | it('Should have 2 video abuses on server 1 and 1 on server 2', async function () { | 155 | it('Should have 2 video abuses on server 1 and 1 on server 2', async function () { |
151 | const res1 = await getAbusesList({ url: servers[0].url, token: servers[0].accessToken }) | 156 | const res1 = await getAdminAbusesList({ url: servers[0].url, token: servers[0].accessToken }) |
152 | 157 | ||
153 | expect(res1.body.total).to.equal(2) | 158 | expect(res1.body.total).to.equal(2) |
154 | expect(res1.body.data.length).to.equal(2) | 159 | expect(res1.body.data.length).to.equal(2) |
155 | 160 | ||
156 | const abuse1: Abuse = res1.body.data[0] | 161 | const abuse1: AdminAbuse = res1.body.data[0] |
157 | expect(abuse1.reason).to.equal('my super bad reason') | 162 | expect(abuse1.reason).to.equal('my super bad reason') |
158 | expect(abuse1.reporterAccount.name).to.equal('root') | 163 | expect(abuse1.reporterAccount.name).to.equal('root') |
159 | expect(abuse1.reporterAccount.host).to.equal(servers[0].host) | 164 | expect(abuse1.reporterAccount.host).to.equal(servers[0].host) |
@@ -171,7 +176,7 @@ describe('Test abuses', function () { | |||
171 | expect(abuse1.state.label).to.equal('Pending') | 176 | expect(abuse1.state.label).to.equal('Pending') |
172 | expect(abuse1.moderationComment).to.be.null | 177 | expect(abuse1.moderationComment).to.be.null |
173 | 178 | ||
174 | const abuse2: Abuse = res1.body.data[1] | 179 | const abuse2: AdminAbuse = res1.body.data[1] |
175 | expect(abuse2.reason).to.equal('my super bad reason 2') | 180 | expect(abuse2.reason).to.equal('my super bad reason 2') |
176 | 181 | ||
177 | expect(abuse2.reporterAccount.name).to.equal('root') | 182 | expect(abuse2.reporterAccount.name).to.equal('root') |
@@ -188,7 +193,7 @@ describe('Test abuses', function () { | |||
188 | expect(abuse2.state.label).to.equal('Pending') | 193 | expect(abuse2.state.label).to.equal('Pending') |
189 | expect(abuse2.moderationComment).to.be.null | 194 | expect(abuse2.moderationComment).to.be.null |
190 | 195 | ||
191 | const res2 = await getAbusesList({ url: servers[1].url, token: servers[1].accessToken }) | 196 | const res2 = await getAdminAbusesList({ url: servers[1].url, token: servers[1].accessToken }) |
192 | expect(res2.body.total).to.equal(1) | 197 | expect(res2.body.total).to.equal(1) |
193 | expect(res2.body.data.length).to.equal(1) | 198 | expect(res2.body.data.length).to.equal(1) |
194 | 199 | ||
@@ -213,7 +218,7 @@ describe('Test abuses', function () { | |||
213 | await reportAbuse({ url: servers[1].url, token: servers[1].accessToken, videoId, reason: 'will mute this' }) | 218 | await reportAbuse({ url: servers[1].url, token: servers[1].accessToken, videoId, reason: 'will mute this' }) |
214 | await waitJobs(servers) | 219 | await waitJobs(servers) |
215 | 220 | ||
216 | const res = await getAbusesList({ url: servers[0].url, token: servers[0].accessToken }) | 221 | const res = await getAdminAbusesList({ url: servers[0].url, token: servers[0].accessToken }) |
217 | expect(res.body.total).to.equal(3) | 222 | expect(res.body.total).to.equal(3) |
218 | } | 223 | } |
219 | 224 | ||
@@ -222,7 +227,7 @@ describe('Test abuses', function () { | |||
222 | { | 227 | { |
223 | await addAccountToServerBlocklist(servers[0].url, servers[0].accessToken, accountToBlock) | 228 | await addAccountToServerBlocklist(servers[0].url, servers[0].accessToken, accountToBlock) |
224 | 229 | ||
225 | const res = await getAbusesList({ url: servers[0].url, token: servers[0].accessToken }) | 230 | const res = await getAdminAbusesList({ url: servers[0].url, token: servers[0].accessToken }) |
226 | expect(res.body.total).to.equal(2) | 231 | expect(res.body.total).to.equal(2) |
227 | 232 | ||
228 | const abuse = res.body.data.find(a => a.reason === 'will mute this') | 233 | const abuse = res.body.data.find(a => a.reason === 'will mute this') |
@@ -232,7 +237,7 @@ describe('Test abuses', function () { | |||
232 | { | 237 | { |
233 | await removeAccountFromServerBlocklist(servers[0].url, servers[0].accessToken, accountToBlock) | 238 | await removeAccountFromServerBlocklist(servers[0].url, servers[0].accessToken, accountToBlock) |
234 | 239 | ||
235 | const res = await getAbusesList({ url: servers[0].url, token: servers[0].accessToken }) | 240 | const res = await getAdminAbusesList({ url: servers[0].url, token: servers[0].accessToken }) |
236 | expect(res.body.total).to.equal(3) | 241 | expect(res.body.total).to.equal(3) |
237 | } | 242 | } |
238 | }) | 243 | }) |
@@ -243,7 +248,7 @@ describe('Test abuses', function () { | |||
243 | { | 248 | { |
244 | await addServerToServerBlocklist(servers[0].url, servers[0].accessToken, servers[1].host) | 249 | await addServerToServerBlocklist(servers[0].url, servers[0].accessToken, servers[1].host) |
245 | 250 | ||
246 | const res = await getAbusesList({ url: servers[0].url, token: servers[0].accessToken }) | 251 | const res = await getAdminAbusesList({ url: servers[0].url, token: servers[0].accessToken }) |
247 | expect(res.body.total).to.equal(2) | 252 | expect(res.body.total).to.equal(2) |
248 | 253 | ||
249 | const abuse = res.body.data.find(a => a.reason === 'will mute this') | 254 | const abuse = res.body.data.find(a => a.reason === 'will mute this') |
@@ -253,7 +258,7 @@ describe('Test abuses', function () { | |||
253 | { | 258 | { |
254 | await removeServerFromServerBlocklist(servers[0].url, servers[0].accessToken, serverToBlock) | 259 | await removeServerFromServerBlocklist(servers[0].url, servers[0].accessToken, serverToBlock) |
255 | 260 | ||
256 | const res = await getAbusesList({ url: servers[0].url, token: servers[0].accessToken }) | 261 | const res = await getAdminAbusesList({ url: servers[0].url, token: servers[0].accessToken }) |
257 | expect(res.body.total).to.equal(3) | 262 | expect(res.body.total).to.equal(3) |
258 | } | 263 | } |
259 | }) | 264 | }) |
@@ -265,11 +270,11 @@ describe('Test abuses', function () { | |||
265 | 270 | ||
266 | await waitJobs(servers) | 271 | await waitJobs(servers) |
267 | 272 | ||
268 | const res = await getAbusesList({ url: servers[1].url, token: servers[1].accessToken }) | 273 | const res = await getAdminAbusesList({ url: servers[1].url, token: servers[1].accessToken }) |
269 | expect(res.body.total).to.equal(2, "wrong number of videos returned") | 274 | expect(res.body.total).to.equal(2, "wrong number of videos returned") |
270 | expect(res.body.data).to.have.lengthOf(2, "wrong number of videos returned") | 275 | expect(res.body.data).to.have.lengthOf(2, "wrong number of videos returned") |
271 | 276 | ||
272 | const abuse: Abuse = res.body.data[0] | 277 | const abuse: AdminAbuse = res.body.data[0] |
273 | expect(abuse.id).to.equal(abuseServer2.id, "wrong origin server id for first video") | 278 | expect(abuse.id).to.equal(abuseServer2.id, "wrong origin server id for first video") |
274 | expect(abuse.video.id).to.equal(abuseServer2.video.id, "wrong video id") | 279 | expect(abuse.video.id).to.equal(abuseServer2.video.id, "wrong video id") |
275 | expect(abuse.video.channel).to.exist | 280 | expect(abuse.video.channel).to.exist |
@@ -303,8 +308,8 @@ describe('Test abuses', function () { | |||
303 | await reportAbuse({ url: servers[0].url, token: userAccessToken, videoId: servers[0].video.id, reason: reason4 }) | 308 | await reportAbuse({ url: servers[0].url, token: userAccessToken, videoId: servers[0].video.id, reason: reason4 }) |
304 | 309 | ||
305 | { | 310 | { |
306 | const res2 = await getAbusesList({ url: servers[0].url, token: servers[0].accessToken }) | 311 | const res2 = await getAdminAbusesList({ url: servers[0].url, token: servers[0].accessToken }) |
307 | const abuses = res2.body.data as Abuse[] | 312 | const abuses = res2.body.data as AdminAbuse[] |
308 | 313 | ||
309 | const abuseVideo3 = res2.body.data.find(a => a.video.id === video3.id) | 314 | const abuseVideo3 = res2.body.data.find(a => a.video.id === video3.id) |
310 | expect(abuseVideo3).to.not.be.undefined | 315 | expect(abuseVideo3).to.not.be.undefined |
@@ -333,10 +338,10 @@ describe('Test abuses', function () { | |||
333 | endAt: 5 | 338 | endAt: 5 |
334 | })).body.abuse | 339 | })).body.abuse |
335 | 340 | ||
336 | const res = await getAbusesList({ url: servers[0].url, token: servers[0].accessToken }) | 341 | const res = await getAdminAbusesList({ url: servers[0].url, token: servers[0].accessToken }) |
337 | 342 | ||
338 | { | 343 | { |
339 | const abuse = (res.body.data as Abuse[]).find(a => a.id === createdAbuse.id) | 344 | const abuse = (res.body.data as AdminAbuse[]).find(a => a.id === createdAbuse.id) |
340 | expect(abuse.reason).to.equals(reason5) | 345 | expect(abuse.reason).to.equals(reason5) |
341 | expect(abuse.predefinedReasons).to.deep.equals(predefinedReasons5, "predefined reasons do not match the one reported") | 346 | expect(abuse.predefinedReasons).to.deep.equals(predefinedReasons5, "predefined reasons do not match the one reported") |
342 | expect(abuse.video.startAt).to.equal(1, "starting timestamp doesn't match the one reported") | 347 | expect(abuse.video.startAt).to.equal(1, "starting timestamp doesn't match the one reported") |
@@ -352,14 +357,14 @@ describe('Test abuses', function () { | |||
352 | await waitJobs(servers) | 357 | await waitJobs(servers) |
353 | 358 | ||
354 | { | 359 | { |
355 | const res = await getAbusesList({ url: servers[1].url, token: servers[1].accessToken }) | 360 | const res = await getAdminAbusesList({ url: servers[1].url, token: servers[1].accessToken }) |
356 | expect(res.body.total).to.equal(1) | 361 | expect(res.body.total).to.equal(1) |
357 | expect(res.body.data.length).to.equal(1) | 362 | expect(res.body.data.length).to.equal(1) |
358 | expect(res.body.data[0].id).to.not.equal(abuseServer2.id) | 363 | expect(res.body.data[0].id).to.not.equal(abuseServer2.id) |
359 | } | 364 | } |
360 | 365 | ||
361 | { | 366 | { |
362 | const res = await getAbusesList({ url: servers[0].url, token: servers[0].accessToken }) | 367 | const res = await getAdminAbusesList({ url: servers[0].url, token: servers[0].accessToken }) |
363 | expect(res.body.total).to.equal(6) | 368 | expect(res.body.total).to.equal(6) |
364 | } | 369 | } |
365 | }) | 370 | }) |
@@ -367,7 +372,7 @@ describe('Test abuses', function () { | |||
367 | it('Should list and filter video abuses', async function () { | 372 | it('Should list and filter video abuses', async function () { |
368 | this.timeout(10000) | 373 | this.timeout(10000) |
369 | 374 | ||
370 | async function list (query: Omit<Parameters<typeof getAbusesList>[0], 'url' | 'token'>) { | 375 | async function list (query: Omit<Parameters<typeof getAdminAbusesList>[0], 'url' | 'token'>) { |
371 | const options = { | 376 | const options = { |
372 | url: servers[0].url, | 377 | url: servers[0].url, |
373 | token: servers[0].accessToken | 378 | token: servers[0].accessToken |
@@ -375,9 +380,9 @@ describe('Test abuses', function () { | |||
375 | 380 | ||
376 | Object.assign(options, query) | 381 | Object.assign(options, query) |
377 | 382 | ||
378 | const res = await getAbusesList(options) | 383 | const res = await getAdminAbusesList(options) |
379 | 384 | ||
380 | return res.body.data as Abuse[] | 385 | return res.body.data as AdminAbuse[] |
381 | } | 386 | } |
382 | 387 | ||
383 | expect(await list({ id: 56 })).to.have.lengthOf(0) | 388 | expect(await list({ id: 56 })).to.have.lengthOf(0) |
@@ -446,12 +451,12 @@ describe('Test abuses', function () { | |||
446 | it('Should have 1 comment abuse on server 1 and 0 on server 2', async function () { | 451 | it('Should have 1 comment abuse on server 1 and 0 on server 2', async function () { |
447 | { | 452 | { |
448 | const comment = await getComment(servers[0].url, servers[0].video.id) | 453 | const comment = await getComment(servers[0].url, servers[0].video.id) |
449 | const res = await getAbusesList({ url: servers[0].url, token: servers[0].accessToken, filter: 'comment' }) | 454 | const res = await getAdminAbusesList({ url: servers[0].url, token: servers[0].accessToken, filter: 'comment' }) |
450 | 455 | ||
451 | expect(res.body.total).to.equal(1) | 456 | expect(res.body.total).to.equal(1) |
452 | expect(res.body.data).to.have.lengthOf(1) | 457 | expect(res.body.data).to.have.lengthOf(1) |
453 | 458 | ||
454 | const abuse: Abuse = res.body.data[0] | 459 | const abuse: AdminAbuse = res.body.data[0] |
455 | expect(abuse.reason).to.equal('it is a bad comment') | 460 | expect(abuse.reason).to.equal('it is a bad comment') |
456 | 461 | ||
457 | expect(abuse.reporterAccount.name).to.equal('root') | 462 | expect(abuse.reporterAccount.name).to.equal('root') |
@@ -471,7 +476,7 @@ describe('Test abuses', function () { | |||
471 | } | 476 | } |
472 | 477 | ||
473 | { | 478 | { |
474 | const res = await getAbusesList({ url: servers[1].url, token: servers[1].accessToken, filter: 'comment' }) | 479 | const res = await getAdminAbusesList({ url: servers[1].url, token: servers[1].accessToken, filter: 'comment' }) |
475 | expect(res.body.total).to.equal(0) | 480 | expect(res.body.total).to.equal(0) |
476 | expect(res.body.data.length).to.equal(0) | 481 | expect(res.body.data.length).to.equal(0) |
477 | } | 482 | } |
@@ -491,16 +496,16 @@ describe('Test abuses', function () { | |||
491 | it('Should have 2 comment abuses on server 1 and 1 on server 2', async function () { | 496 | it('Should have 2 comment abuses on server 1 and 1 on server 2', async function () { |
492 | const commentServer2 = await getComment(servers[0].url, servers[1].video.id) | 497 | const commentServer2 = await getComment(servers[0].url, servers[1].video.id) |
493 | 498 | ||
494 | const res1 = await getAbusesList({ url: servers[0].url, token: servers[0].accessToken, filter: 'comment' }) | 499 | const res1 = await getAdminAbusesList({ url: servers[0].url, token: servers[0].accessToken, filter: 'comment' }) |
495 | expect(res1.body.total).to.equal(2) | 500 | expect(res1.body.total).to.equal(2) |
496 | expect(res1.body.data.length).to.equal(2) | 501 | expect(res1.body.data.length).to.equal(2) |
497 | 502 | ||
498 | const abuse: Abuse = res1.body.data[0] | 503 | const abuse: AdminAbuse = res1.body.data[0] |
499 | expect(abuse.reason).to.equal('it is a bad comment') | 504 | expect(abuse.reason).to.equal('it is a bad comment') |
500 | expect(abuse.countReportsForReporter).to.equal(6) | 505 | expect(abuse.countReportsForReporter).to.equal(6) |
501 | expect(abuse.countReportsForReportee).to.equal(5) | 506 | expect(abuse.countReportsForReportee).to.equal(5) |
502 | 507 | ||
503 | const abuse2: Abuse = res1.body.data[1] | 508 | const abuse2: AdminAbuse = res1.body.data[1] |
504 | 509 | ||
505 | expect(abuse2.reason).to.equal('it is a really bad comment') | 510 | expect(abuse2.reason).to.equal('it is a really bad comment') |
506 | 511 | ||
@@ -523,7 +528,7 @@ describe('Test abuses', function () { | |||
523 | expect(abuse2.countReportsForReporter).to.equal(6) | 528 | expect(abuse2.countReportsForReporter).to.equal(6) |
524 | expect(abuse2.countReportsForReportee).to.equal(2) | 529 | expect(abuse2.countReportsForReportee).to.equal(2) |
525 | 530 | ||
526 | const res2 = await getAbusesList({ url: servers[1].url, token: servers[1].accessToken, filter: 'comment' }) | 531 | const res2 = await getAdminAbusesList({ url: servers[1].url, token: servers[1].accessToken, filter: 'comment' }) |
527 | expect(res2.body.total).to.equal(1) | 532 | expect(res2.body.total).to.equal(1) |
528 | expect(res2.body.data.length).to.equal(1) | 533 | expect(res2.body.data.length).to.equal(1) |
529 | 534 | ||
@@ -550,11 +555,11 @@ describe('Test abuses', function () { | |||
550 | 555 | ||
551 | await waitJobs(servers) | 556 | await waitJobs(servers) |
552 | 557 | ||
553 | const res = await getAbusesList({ url: servers[0].url, token: servers[0].accessToken, filter: 'comment' }) | 558 | const res = await getAdminAbusesList({ url: servers[0].url, token: servers[0].accessToken, filter: 'comment' }) |
554 | expect(res.body.total).to.equal(2) | 559 | expect(res.body.total).to.equal(2) |
555 | expect(res.body.data).to.have.lengthOf(2) | 560 | expect(res.body.data).to.have.lengthOf(2) |
556 | 561 | ||
557 | const abuse = (res.body.data as Abuse[]).find(a => a.comment?.id === commentServer2.id) | 562 | const abuse = (res.body.data as AdminAbuse[]).find(a => a.comment?.id === commentServer2.id) |
558 | expect(abuse).to.not.be.undefined | 563 | expect(abuse).to.not.be.undefined |
559 | 564 | ||
560 | expect(abuse.comment.text).to.be.empty | 565 | expect(abuse.comment.text).to.be.empty |
@@ -570,36 +575,46 @@ describe('Test abuses', function () { | |||
570 | await waitJobs(servers) | 575 | await waitJobs(servers) |
571 | 576 | ||
572 | { | 577 | { |
573 | const res = await getAbusesList({ url: servers[1].url, token: servers[1].accessToken, filter: 'comment' }) | 578 | const res = await getAdminAbusesList({ url: servers[1].url, token: servers[1].accessToken, filter: 'comment' }) |
574 | expect(res.body.total).to.equal(0) | 579 | expect(res.body.total).to.equal(0) |
575 | expect(res.body.data.length).to.equal(0) | 580 | expect(res.body.data.length).to.equal(0) |
576 | } | 581 | } |
577 | 582 | ||
578 | { | 583 | { |
579 | const res = await getAbusesList({ url: servers[0].url, token: servers[0].accessToken, filter: 'comment' }) | 584 | const res = await getAdminAbusesList({ url: servers[0].url, token: servers[0].accessToken, filter: 'comment' }) |
580 | expect(res.body.total).to.equal(2) | 585 | expect(res.body.total).to.equal(2) |
581 | } | 586 | } |
582 | }) | 587 | }) |
583 | 588 | ||
584 | it('Should list and filter video abuses', async function () { | 589 | it('Should list and filter video abuses', async function () { |
585 | { | 590 | { |
586 | const res = await getAbusesList({ url: servers[0].url, token: servers[0].accessToken, filter: 'comment', searchReportee: 'foo' }) | 591 | const res = await getAdminAbusesList({ |
592 | url: servers[0].url, | ||
593 | token: servers[0].accessToken, | ||
594 | filter: 'comment', | ||
595 | searchReportee: 'foo' | ||
596 | }) | ||
587 | expect(res.body.total).to.equal(0) | 597 | expect(res.body.total).to.equal(0) |
588 | } | 598 | } |
589 | 599 | ||
590 | { | 600 | { |
591 | const res = await getAbusesList({ url: servers[0].url, token: servers[0].accessToken, filter: 'comment', searchReportee: 'ot' }) | 601 | const res = await getAdminAbusesList({ |
602 | url: servers[0].url, | ||
603 | token: servers[0].accessToken, | ||
604 | filter: 'comment', | ||
605 | searchReportee: 'ot' | ||
606 | }) | ||
592 | expect(res.body.total).to.equal(2) | 607 | expect(res.body.total).to.equal(2) |
593 | } | 608 | } |
594 | 609 | ||
595 | { | 610 | { |
596 | const baseParams = { url: servers[0].url, token: servers[0].accessToken, filter: 'comment' as AbuseFilter, start: 1, count: 1 } | 611 | const baseParams = { url: servers[0].url, token: servers[0].accessToken, filter: 'comment' as AbuseFilter, start: 1, count: 1 } |
597 | 612 | ||
598 | const res1 = await getAbusesList(immutableAssign(baseParams, { sort: 'createdAt' })) | 613 | const res1 = await getAdminAbusesList(immutableAssign(baseParams, { sort: 'createdAt' })) |
599 | expect(res1.body.data).to.have.lengthOf(1) | 614 | expect(res1.body.data).to.have.lengthOf(1) |
600 | expect(res1.body.data[0].comment.text).to.be.empty | 615 | expect(res1.body.data[0].comment.text).to.be.empty |
601 | 616 | ||
602 | const res2 = await getAbusesList(immutableAssign(baseParams, { sort: '-createdAt' })) | 617 | const res2 = await getAdminAbusesList(immutableAssign(baseParams, { sort: '-createdAt' })) |
603 | expect(res2.body.data).to.have.lengthOf(1) | 618 | expect(res2.body.data).to.have.lengthOf(1) |
604 | expect(res2.body.data[0].comment.text).to.equal('comment server 1') | 619 | expect(res2.body.data[0].comment.text).to.equal('comment server 1') |
605 | } | 620 | } |
@@ -638,12 +653,12 @@ describe('Test abuses', function () { | |||
638 | 653 | ||
639 | it('Should have 1 account abuse on server 1 and 0 on server 2', async function () { | 654 | it('Should have 1 account abuse on server 1 and 0 on server 2', async function () { |
640 | { | 655 | { |
641 | const res = await getAbusesList({ url: servers[0].url, token: servers[0].accessToken, filter: 'account' }) | 656 | const res = await getAdminAbusesList({ url: servers[0].url, token: servers[0].accessToken, filter: 'account' }) |
642 | 657 | ||
643 | expect(res.body.total).to.equal(1) | 658 | expect(res.body.total).to.equal(1) |
644 | expect(res.body.data).to.have.lengthOf(1) | 659 | expect(res.body.data).to.have.lengthOf(1) |
645 | 660 | ||
646 | const abuse: Abuse = res.body.data[0] | 661 | const abuse: AdminAbuse = res.body.data[0] |
647 | expect(abuse.reason).to.equal('it is a bad account') | 662 | expect(abuse.reason).to.equal('it is a bad account') |
648 | 663 | ||
649 | expect(abuse.reporterAccount.name).to.equal('root') | 664 | expect(abuse.reporterAccount.name).to.equal('root') |
@@ -657,7 +672,7 @@ describe('Test abuses', function () { | |||
657 | } | 672 | } |
658 | 673 | ||
659 | { | 674 | { |
660 | const res = await getAbusesList({ url: servers[1].url, token: servers[1].accessToken, filter: 'comment' }) | 675 | const res = await getAdminAbusesList({ url: servers[1].url, token: servers[1].accessToken, filter: 'comment' }) |
661 | expect(res.body.total).to.equal(0) | 676 | expect(res.body.total).to.equal(0) |
662 | expect(res.body.data.length).to.equal(0) | 677 | expect(res.body.data.length).to.equal(0) |
663 | } | 678 | } |
@@ -675,14 +690,14 @@ describe('Test abuses', function () { | |||
675 | }) | 690 | }) |
676 | 691 | ||
677 | it('Should have 2 comment abuses on server 1 and 1 on server 2', async function () { | 692 | it('Should have 2 comment abuses on server 1 and 1 on server 2', async function () { |
678 | const res1 = await getAbusesList({ url: servers[0].url, token: servers[0].accessToken, filter: 'account' }) | 693 | const res1 = await getAdminAbusesList({ url: servers[0].url, token: servers[0].accessToken, filter: 'account' }) |
679 | expect(res1.body.total).to.equal(2) | 694 | expect(res1.body.total).to.equal(2) |
680 | expect(res1.body.data.length).to.equal(2) | 695 | expect(res1.body.data.length).to.equal(2) |
681 | 696 | ||
682 | const abuse: Abuse = res1.body.data[0] | 697 | const abuse: AdminAbuse = res1.body.data[0] |
683 | expect(abuse.reason).to.equal('it is a bad account') | 698 | expect(abuse.reason).to.equal('it is a bad account') |
684 | 699 | ||
685 | const abuse2: Abuse = res1.body.data[1] | 700 | const abuse2: AdminAbuse = res1.body.data[1] |
686 | expect(abuse2.reason).to.equal('it is a really bad account') | 701 | expect(abuse2.reason).to.equal('it is a really bad account') |
687 | 702 | ||
688 | expect(abuse2.reporterAccount.name).to.equal('root') | 703 | expect(abuse2.reporterAccount.name).to.equal('root') |
@@ -696,7 +711,7 @@ describe('Test abuses', function () { | |||
696 | 711 | ||
697 | expect(abuse2.moderationComment).to.be.null | 712 | expect(abuse2.moderationComment).to.be.null |
698 | 713 | ||
699 | const res2 = await getAbusesList({ url: servers[1].url, token: servers[1].accessToken, filter: 'account' }) | 714 | const res2 = await getAdminAbusesList({ url: servers[1].url, token: servers[1].accessToken, filter: 'account' }) |
700 | expect(res2.body.total).to.equal(1) | 715 | expect(res2.body.total).to.equal(1) |
701 | expect(res2.body.data.length).to.equal(1) | 716 | expect(res2.body.data.length).to.equal(1) |
702 | 717 | ||
@@ -721,11 +736,11 @@ describe('Test abuses', function () { | |||
721 | 736 | ||
722 | await waitJobs(servers) | 737 | await waitJobs(servers) |
723 | 738 | ||
724 | const res = await getAbusesList({ url: servers[0].url, token: servers[0].accessToken, filter: 'account' }) | 739 | const res = await getAdminAbusesList({ url: servers[0].url, token: servers[0].accessToken, filter: 'account' }) |
725 | expect(res.body.total).to.equal(2) | 740 | expect(res.body.total).to.equal(2) |
726 | expect(res.body.data).to.have.lengthOf(2) | 741 | expect(res.body.data).to.have.lengthOf(2) |
727 | 742 | ||
728 | const abuse = (res.body.data as Abuse[]).find(a => a.reason === 'it is a really bad account') | 743 | const abuse = (res.body.data as AdminAbuse[]).find(a => a.reason === 'it is a really bad account') |
729 | expect(abuse).to.not.be.undefined | 744 | expect(abuse).to.not.be.undefined |
730 | }) | 745 | }) |
731 | 746 | ||
@@ -737,13 +752,13 @@ describe('Test abuses', function () { | |||
737 | await waitJobs(servers) | 752 | await waitJobs(servers) |
738 | 753 | ||
739 | { | 754 | { |
740 | const res = await getAbusesList({ url: servers[1].url, token: servers[1].accessToken, filter: 'account' }) | 755 | const res = await getAdminAbusesList({ url: servers[1].url, token: servers[1].accessToken, filter: 'account' }) |
741 | expect(res.body.total).to.equal(0) | 756 | expect(res.body.total).to.equal(0) |
742 | expect(res.body.data.length).to.equal(0) | 757 | expect(res.body.data.length).to.equal(0) |
743 | } | 758 | } |
744 | 759 | ||
745 | { | 760 | { |
746 | const res = await getAbusesList({ url: servers[0].url, token: servers[0].accessToken, filter: 'account' }) | 761 | const res = await getAdminAbusesList({ url: servers[0].url, token: servers[0].accessToken, filter: 'account' }) |
747 | expect(res.body.total).to.equal(2) | 762 | expect(res.body.total).to.equal(2) |
748 | 763 | ||
749 | abuseServer1 = res.body.data[0] | 764 | abuseServer1 = res.body.data[0] |
@@ -757,7 +772,7 @@ describe('Test abuses', function () { | |||
757 | const body = { state: AbuseState.REJECTED } | 772 | const body = { state: AbuseState.REJECTED } |
758 | await updateAbuse(servers[0].url, servers[0].accessToken, abuseServer1.id, body) | 773 | await updateAbuse(servers[0].url, servers[0].accessToken, abuseServer1.id, body) |
759 | 774 | ||
760 | const res = await getAbusesList({ url: servers[0].url, token: servers[0].accessToken, id: abuseServer1.id }) | 775 | const res = await getAdminAbusesList({ url: servers[0].url, token: servers[0].accessToken, id: abuseServer1.id }) |
761 | expect(res.body.data[0].state.id).to.equal(AbuseState.REJECTED) | 776 | expect(res.body.data[0].state.id).to.equal(AbuseState.REJECTED) |
762 | }) | 777 | }) |
763 | 778 | ||
@@ -765,12 +780,184 @@ describe('Test abuses', function () { | |||
765 | const body = { state: AbuseState.ACCEPTED, moderationComment: 'It is valid' } | 780 | const body = { state: AbuseState.ACCEPTED, moderationComment: 'It is valid' } |
766 | await updateAbuse(servers[0].url, servers[0].accessToken, abuseServer1.id, body) | 781 | await updateAbuse(servers[0].url, servers[0].accessToken, abuseServer1.id, body) |
767 | 782 | ||
768 | const res = await getAbusesList({ url: servers[0].url, token: servers[0].accessToken, id: abuseServer1.id }) | 783 | const res = await getAdminAbusesList({ url: servers[0].url, token: servers[0].accessToken, id: abuseServer1.id }) |
769 | expect(res.body.data[0].state.id).to.equal(AbuseState.ACCEPTED) | 784 | expect(res.body.data[0].state.id).to.equal(AbuseState.ACCEPTED) |
770 | expect(res.body.data[0].moderationComment).to.equal('It is valid') | 785 | expect(res.body.data[0].moderationComment).to.equal('It is valid') |
771 | }) | 786 | }) |
772 | }) | 787 | }) |
773 | 788 | ||
789 | describe('My abuses', async function () { | ||
790 | let abuseId1: number | ||
791 | let userAccessToken: string | ||
792 | |||
793 | before(async function () { | ||
794 | userAccessToken = await generateUserAccessToken(servers[0], 'user_42') | ||
795 | |||
796 | await reportAbuse({ url: servers[0].url, token: userAccessToken, videoId: servers[0].video.id, reason: 'user reason 1' }) | ||
797 | |||
798 | const videoId = await getVideoIdFromUUID(servers[0].url, servers[1].video.uuid) | ||
799 | await reportAbuse({ url: servers[0].url, token: userAccessToken, videoId, reason: 'user reason 2' }) | ||
800 | }) | ||
801 | |||
802 | it('Should correctly list my abuses', async function () { | ||
803 | { | ||
804 | const res = await getUserAbusesList({ url: servers[0].url, token: userAccessToken, start: 0, count: 5, sort: 'createdAt' }) | ||
805 | expect(res.body.total).to.equal(2) | ||
806 | |||
807 | const abuses: UserAbuse[] = res.body.data | ||
808 | expect(abuses[0].reason).to.equal('user reason 1') | ||
809 | expect(abuses[1].reason).to.equal('user reason 2') | ||
810 | |||
811 | abuseId1 = abuses[0].id | ||
812 | } | ||
813 | |||
814 | { | ||
815 | const res = await getUserAbusesList({ url: servers[0].url, token: userAccessToken, start: 1, count: 1, sort: 'createdAt' }) | ||
816 | expect(res.body.total).to.equal(2) | ||
817 | |||
818 | const abuses: UserAbuse[] = res.body.data | ||
819 | expect(abuses[0].reason).to.equal('user reason 2') | ||
820 | } | ||
821 | |||
822 | { | ||
823 | const res = await getUserAbusesList({ url: servers[0].url, token: userAccessToken, start: 1, count: 1, sort: '-createdAt' }) | ||
824 | expect(res.body.total).to.equal(2) | ||
825 | |||
826 | const abuses: UserAbuse[] = res.body.data | ||
827 | expect(abuses[0].reason).to.equal('user reason 1') | ||
828 | } | ||
829 | }) | ||
830 | |||
831 | it('Should correctly filter my abuses by id', async function () { | ||
832 | const res = await getUserAbusesList({ url: servers[0].url, token: userAccessToken, id: abuseId1 }) | ||
833 | |||
834 | expect(res.body.total).to.equal(1) | ||
835 | |||
836 | const abuses: UserAbuse[] = res.body.data | ||
837 | expect(abuses[0].reason).to.equal('user reason 1') | ||
838 | }) | ||
839 | |||
840 | it('Should correctly filter my abuses by search', async function () { | ||
841 | const res = await getUserAbusesList({ | ||
842 | url: servers[0].url, | ||
843 | token: userAccessToken, | ||
844 | search: 'server 2' | ||
845 | }) | ||
846 | |||
847 | expect(res.body.total).to.equal(1) | ||
848 | |||
849 | const abuses: UserAbuse[] = res.body.data | ||
850 | expect(abuses[0].reason).to.equal('user reason 2') | ||
851 | }) | ||
852 | |||
853 | it('Should correctly filter my abuses by state', async function () { | ||
854 | const body = { state: AbuseState.REJECTED } | ||
855 | await updateAbuse(servers[0].url, servers[0].accessToken, abuseId1, body) | ||
856 | |||
857 | const res = await getUserAbusesList({ | ||
858 | url: servers[0].url, | ||
859 | token: userAccessToken, | ||
860 | state: AbuseState.REJECTED | ||
861 | }) | ||
862 | |||
863 | expect(res.body.total).to.equal(1) | ||
864 | |||
865 | const abuses: UserAbuse[] = res.body.data | ||
866 | expect(abuses[0].reason).to.equal('user reason 1') | ||
867 | }) | ||
868 | }) | ||
869 | |||
870 | describe('Abuse messages', async function () { | ||
871 | let abuseId: number | ||
872 | let userAccessToken: string | ||
873 | let abuseMessageUserId: number | ||
874 | let abuseMessageModerationId: number | ||
875 | |||
876 | before(async function () { | ||
877 | userAccessToken = await generateUserAccessToken(servers[0], 'user_43') | ||
878 | |||
879 | const res = await reportAbuse({ | ||
880 | url: servers[0].url, | ||
881 | token: userAccessToken, | ||
882 | videoId: servers[0].video.id, | ||
883 | reason: 'user 43 reason 1' | ||
884 | }) | ||
885 | |||
886 | abuseId = res.body.abuse.id | ||
887 | }) | ||
888 | |||
889 | it('Should create some messages on the abuse', async function () { | ||
890 | await addAbuseMessage(servers[0].url, userAccessToken, abuseId, 'message 1') | ||
891 | await addAbuseMessage(servers[0].url, servers[0].accessToken, abuseId, 'message 2') | ||
892 | await addAbuseMessage(servers[0].url, servers[0].accessToken, abuseId, 'message 3') | ||
893 | await addAbuseMessage(servers[0].url, userAccessToken, abuseId, 'message 4') | ||
894 | }) | ||
895 | |||
896 | it('Should have the correct messages count when listing abuses', async function () { | ||
897 | const results = await Promise.all([ | ||
898 | getAdminAbusesList({ url: servers[0].url, token: servers[0].accessToken, start: 0, count: 50 }), | ||
899 | getUserAbusesList({ url: servers[0].url, token: userAccessToken, start: 0, count: 50 }) | ||
900 | ]) | ||
901 | |||
902 | for (const res of results) { | ||
903 | const abuses: AdminAbuse[] = res.body.data | ||
904 | const abuse = abuses.find(a => a.id === abuseId) | ||
905 | expect(abuse.countMessages).to.equal(4) | ||
906 | } | ||
907 | }) | ||
908 | |||
909 | it('Should correctly list messages of this abuse', async function () { | ||
910 | const results = await Promise.all([ | ||
911 | listAbuseMessages(servers[0].url, servers[0].accessToken, abuseId), | ||
912 | listAbuseMessages(servers[0].url, userAccessToken, abuseId) | ||
913 | ]) | ||
914 | |||
915 | for (const res of results) { | ||
916 | expect(res.body.total).to.equal(4) | ||
917 | |||
918 | const abuseMessages: AbuseMessage[] = res.body.data | ||
919 | |||
920 | expect(abuseMessages[0].message).to.equal('message 1') | ||
921 | expect(abuseMessages[0].byModerator).to.be.false | ||
922 | expect(abuseMessages[0].account.name).to.equal('user_43') | ||
923 | |||
924 | abuseMessageUserId = abuseMessages[0].id | ||
925 | |||
926 | expect(abuseMessages[1].message).to.equal('message 2') | ||
927 | expect(abuseMessages[1].byModerator).to.be.true | ||
928 | expect(abuseMessages[1].account.name).to.equal('root') | ||
929 | |||
930 | expect(abuseMessages[2].message).to.equal('message 3') | ||
931 | expect(abuseMessages[2].byModerator).to.be.true | ||
932 | expect(abuseMessages[2].account.name).to.equal('root') | ||
933 | abuseMessageModerationId = abuseMessages[2].id | ||
934 | |||
935 | expect(abuseMessages[3].message).to.equal('message 4') | ||
936 | expect(abuseMessages[3].byModerator).to.be.false | ||
937 | expect(abuseMessages[3].account.name).to.equal('user_43') | ||
938 | } | ||
939 | }) | ||
940 | |||
941 | it('Should delete messages', async function () { | ||
942 | await deleteAbuseMessage(servers[0].url, servers[0].accessToken, abuseId, abuseMessageModerationId) | ||
943 | await deleteAbuseMessage(servers[0].url, userAccessToken, abuseId, abuseMessageUserId) | ||
944 | |||
945 | const results = await Promise.all([ | ||
946 | listAbuseMessages(servers[0].url, servers[0].accessToken, abuseId), | ||
947 | listAbuseMessages(servers[0].url, userAccessToken, abuseId) | ||
948 | ]) | ||
949 | |||
950 | for (const res of results) { | ||
951 | expect(res.body.total).to.equal(2) | ||
952 | |||
953 | const abuseMessages: AbuseMessage[] = res.body.data | ||
954 | |||
955 | expect(abuseMessages[0].message).to.equal('message 2') | ||
956 | expect(abuseMessages[1].message).to.equal('message 4') | ||
957 | } | ||
958 | }) | ||
959 | }) | ||
960 | |||
774 | after(async function () { | 961 | after(async function () { |
775 | await cleanupTests(servers) | 962 | await cleanupTests(servers) |
776 | }) | 963 | }) |
diff --git a/server/tests/api/users/users.ts b/server/tests/api/users/users.ts index ea74bde6a..edb0b4bb3 100644 --- a/server/tests/api/users/users.ts +++ b/server/tests/api/users/users.ts | |||
@@ -11,8 +11,8 @@ import { | |||
11 | createUser, | 11 | createUser, |
12 | deleteMe, | 12 | deleteMe, |
13 | flushAndRunServer, | 13 | flushAndRunServer, |
14 | getAbusesList, | ||
15 | getAccountRatings, | 14 | getAccountRatings, |
15 | getAdminAbusesList, | ||
16 | getBlacklistedVideosList, | 16 | getBlacklistedVideosList, |
17 | getCustomConfig, | 17 | getCustomConfig, |
18 | getMyUserInformation, | 18 | getMyUserInformation, |
@@ -928,7 +928,7 @@ describe('Test users', function () { | |||
928 | const reason = 'my super bad reason' | 928 | const reason = 'my super bad reason' |
929 | await reportAbuse({ url: server.url, token: user17AccessToken, videoId, reason }) | 929 | await reportAbuse({ url: server.url, token: user17AccessToken, videoId, reason }) |
930 | 930 | ||
931 | const res1 = await getAbusesList({ url: server.url, token: server.accessToken }) | 931 | const res1 = await getAdminAbusesList({ url: server.url, token: server.accessToken }) |
932 | const abuseId = res1.body.data[0].id | 932 | const abuseId = res1.body.data[0].id |
933 | 933 | ||
934 | const res2 = await getUserInformation(server.url, server.accessToken, user17Id, true) | 934 | const res2 = await getUserInformation(server.url, server.accessToken, user17Id, true) |
diff --git a/server/tests/api/videos/video-abuse.ts b/server/tests/api/videos/video-abuse.ts index baeb543e0..0b6a0e8ae 100644 --- a/server/tests/api/videos/video-abuse.ts +++ b/server/tests/api/videos/video-abuse.ts | |||
@@ -2,7 +2,7 @@ | |||
2 | 2 | ||
3 | import 'mocha' | 3 | import 'mocha' |
4 | import * as chai from 'chai' | 4 | import * as chai from 'chai' |
5 | import { Abuse, AbusePredefinedReasonsString, AbuseState } from '@shared/models' | 5 | import { AbusePredefinedReasonsString, AbuseState, AdminAbuse } from '@shared/models' |
6 | import { | 6 | import { |
7 | cleanupTests, | 7 | cleanupTests, |
8 | createUser, | 8 | createUser, |
@@ -33,7 +33,7 @@ const expect = chai.expect | |||
33 | 33 | ||
34 | describe('Test video abuses', function () { | 34 | describe('Test video abuses', function () { |
35 | let servers: ServerInfo[] = [] | 35 | let servers: ServerInfo[] = [] |
36 | let abuseServer2: Abuse | 36 | let abuseServer2: AdminAbuse |
37 | 37 | ||
38 | before(async function () { | 38 | before(async function () { |
39 | this.timeout(50000) | 39 | this.timeout(50000) |
@@ -97,7 +97,7 @@ describe('Test video abuses', function () { | |||
97 | expect(res1.body.data).to.be.an('array') | 97 | expect(res1.body.data).to.be.an('array') |
98 | expect(res1.body.data.length).to.equal(1) | 98 | expect(res1.body.data.length).to.equal(1) |
99 | 99 | ||
100 | const abuse: Abuse = res1.body.data[0] | 100 | const abuse: AdminAbuse = res1.body.data[0] |
101 | expect(abuse.reason).to.equal('my super bad reason') | 101 | expect(abuse.reason).to.equal('my super bad reason') |
102 | expect(abuse.reporterAccount.name).to.equal('root') | 102 | expect(abuse.reporterAccount.name).to.equal('root') |
103 | expect(abuse.reporterAccount.host).to.equal('localhost:' + servers[0].port) | 103 | expect(abuse.reporterAccount.host).to.equal('localhost:' + servers[0].port) |
@@ -130,7 +130,7 @@ describe('Test video abuses', function () { | |||
130 | expect(res1.body.data).to.be.an('array') | 130 | expect(res1.body.data).to.be.an('array') |
131 | expect(res1.body.data.length).to.equal(2) | 131 | expect(res1.body.data.length).to.equal(2) |
132 | 132 | ||
133 | const abuse1: Abuse = res1.body.data[0] | 133 | const abuse1: AdminAbuse = res1.body.data[0] |
134 | expect(abuse1.reason).to.equal('my super bad reason') | 134 | expect(abuse1.reason).to.equal('my super bad reason') |
135 | expect(abuse1.reporterAccount.name).to.equal('root') | 135 | expect(abuse1.reporterAccount.name).to.equal('root') |
136 | expect(abuse1.reporterAccount.host).to.equal('localhost:' + servers[0].port) | 136 | expect(abuse1.reporterAccount.host).to.equal('localhost:' + servers[0].port) |
@@ -141,7 +141,7 @@ describe('Test video abuses', function () { | |||
141 | expect(abuse1.video.countReports).to.equal(1) | 141 | expect(abuse1.video.countReports).to.equal(1) |
142 | expect(abuse1.video.nthReport).to.equal(1) | 142 | expect(abuse1.video.nthReport).to.equal(1) |
143 | 143 | ||
144 | const abuse2: Abuse = res1.body.data[1] | 144 | const abuse2: AdminAbuse = res1.body.data[1] |
145 | expect(abuse2.reason).to.equal('my super bad reason 2') | 145 | expect(abuse2.reason).to.equal('my super bad reason 2') |
146 | expect(abuse2.reporterAccount.name).to.equal('root') | 146 | expect(abuse2.reporterAccount.name).to.equal('root') |
147 | expect(abuse2.reporterAccount.host).to.equal('localhost:' + servers[0].port) | 147 | expect(abuse2.reporterAccount.host).to.equal('localhost:' + servers[0].port) |
@@ -245,7 +245,7 @@ describe('Test video abuses', function () { | |||
245 | expect(res.body.data.length).to.equal(2, "wrong number of videos returned") | 245 | expect(res.body.data.length).to.equal(2, "wrong number of videos returned") |
246 | expect(res.body.data[0].id).to.equal(abuseServer2.id, "wrong origin server id for first video") | 246 | expect(res.body.data[0].id).to.equal(abuseServer2.id, "wrong origin server id for first video") |
247 | 247 | ||
248 | const abuse: Abuse = res.body.data[0] | 248 | const abuse: AdminAbuse = res.body.data[0] |
249 | expect(abuse.video.id).to.equal(abuseServer2.video.id, "wrong video id") | 249 | expect(abuse.video.id).to.equal(abuseServer2.video.id, "wrong video id") |
250 | expect(abuse.video.channel).to.exist | 250 | expect(abuse.video.channel).to.exist |
251 | expect(abuse.video.deleted).to.be.true | 251 | expect(abuse.video.deleted).to.be.true |
@@ -279,7 +279,7 @@ describe('Test video abuses', function () { | |||
279 | const res2 = await getVideoAbusesList({ url: servers[0].url, token: servers[0].accessToken }) | 279 | const res2 = await getVideoAbusesList({ url: servers[0].url, token: servers[0].accessToken }) |
280 | 280 | ||
281 | { | 281 | { |
282 | for (const abuse of res2.body.data as Abuse[]) { | 282 | for (const abuse of res2.body.data as AdminAbuse[]) { |
283 | if (abuse.video.id === video3.id) { | 283 | if (abuse.video.id === video3.id) { |
284 | expect(abuse.video.countReports).to.equal(1, "wrong reports count for video 3") | 284 | expect(abuse.video.countReports).to.equal(1, "wrong reports count for video 3") |
285 | expect(abuse.video.nthReport).to.equal(1, "wrong report position in report list for video 3") | 285 | expect(abuse.video.nthReport).to.equal(1, "wrong report position in report list for video 3") |
@@ -311,7 +311,7 @@ describe('Test video abuses', function () { | |||
311 | const res = await getVideoAbusesList({ url: servers[0].url, token: servers[0].accessToken }) | 311 | const res = await getVideoAbusesList({ url: servers[0].url, token: servers[0].accessToken }) |
312 | 312 | ||
313 | { | 313 | { |
314 | const abuse = (res.body.data as Abuse[]).find(a => a.id === createdAbuse.id) | 314 | const abuse = (res.body.data as AdminAbuse[]).find(a => a.id === createdAbuse.id) |
315 | expect(abuse.reason).to.equals(reason5) | 315 | expect(abuse.reason).to.equals(reason5) |
316 | expect(abuse.predefinedReasons).to.deep.equals(predefinedReasons5, "predefined reasons do not match the one reported") | 316 | expect(abuse.predefinedReasons).to.deep.equals(predefinedReasons5, "predefined reasons do not match the one reported") |
317 | expect(abuse.video.startAt).to.equal(1, "starting timestamp doesn't match the one reported") | 317 | expect(abuse.video.startAt).to.equal(1, "starting timestamp doesn't match the one reported") |
@@ -350,7 +350,7 @@ describe('Test video abuses', function () { | |||
350 | 350 | ||
351 | const res = await getVideoAbusesList(options) | 351 | const res = await getVideoAbusesList(options) |
352 | 352 | ||
353 | return res.body.data as Abuse[] | 353 | return res.body.data as AdminAbuse[] |
354 | } | 354 | } |
355 | 355 | ||
356 | expect(await list({ id: 56 })).to.have.lengthOf(0) | 356 | expect(await list({ id: 56 })).to.have.lengthOf(0) |
diff --git a/server/types/models/moderation/abuse-message.ts b/server/types/models/moderation/abuse-message.ts new file mode 100644 index 000000000..565eca706 --- /dev/null +++ b/server/types/models/moderation/abuse-message.ts | |||
@@ -0,0 +1,20 @@ | |||
1 | import { AbuseMessageModel } from '@server/models/abuse/abuse-message' | ||
2 | import { PickWith } from '@shared/core-utils' | ||
3 | import { AbuseModel } from '../../../models/abuse/abuse' | ||
4 | import { MAccountFormattable } from '../account' | ||
5 | |||
6 | type Use<K extends keyof AbuseMessageModel, M> = PickWith<AbuseMessageModel, K, M> | ||
7 | |||
8 | // ############################################################################ | ||
9 | |||
10 | export type MAbuseMessage = Omit<AbuseMessageModel, 'Account' | 'Abuse' | 'toFormattedJSON'> | ||
11 | |||
12 | export type MAbuseMessageId = Pick<AbuseModel, 'id'> | ||
13 | |||
14 | // ############################################################################ | ||
15 | |||
16 | // Format for API | ||
17 | |||
18 | export type MAbuseMessageFormattable = | ||
19 | MAbuseMessage & | ||
20 | Use<'Account', MAccountFormattable> | ||
diff --git a/server/types/models/moderation/abuse.ts b/server/types/models/moderation/abuse.ts index a0bf4b08f..39ef50771 100644 --- a/server/types/models/moderation/abuse.ts +++ b/server/types/models/moderation/abuse.ts | |||
@@ -95,9 +95,15 @@ export type MAbuseFull = | |||
95 | 95 | ||
96 | // Format for API or AP object | 96 | // Format for API or AP object |
97 | 97 | ||
98 | export type MAbuseFormattable = | 98 | export type MAbuseAdminFormattable = |
99 | MAbuse & | 99 | MAbuse & |
100 | Use<'ReporterAccount', MAccountFormattable> & | 100 | Use<'ReporterAccount', MAccountFormattable> & |
101 | Use<'FlaggedAccount', MAccountFormattable> & | 101 | Use<'FlaggedAccount', MAccountFormattable> & |
102 | Use<'VideoAbuse', MVideoAbuseFormattable> & | 102 | Use<'VideoAbuse', MVideoAbuseFormattable> & |
103 | Use<'VideoCommentAbuse', MCommentAbuseFormattable> | 103 | Use<'VideoCommentAbuse', MCommentAbuseFormattable> |
104 | |||
105 | export type MAbuseUserFormattable = | ||
106 | MAbuse & | ||
107 | Use<'FlaggedAccount', MAccountFormattable> & | ||
108 | Use<'VideoAbuse', MVideoAbuseFormattable> & | ||
109 | Use<'VideoCommentAbuse', MCommentAbuseFormattable> | ||
diff --git a/server/types/models/moderation/index.ts b/server/types/models/moderation/index.ts index 8bea1708f..1ed91b249 100644 --- a/server/types/models/moderation/index.ts +++ b/server/types/models/moderation/index.ts | |||
@@ -1 +1,2 @@ | |||
1 | export * from './abuse' | 1 | export * from './abuse' |
2 | export * from './abuse-message' | ||
diff --git a/server/typings/express/index.d.ts b/server/typings/express/index.d.ts index 7595e6d86..452c6e1a0 100644 --- a/server/typings/express/index.d.ts +++ b/server/typings/express/index.d.ts | |||
@@ -1,6 +1,7 @@ | |||
1 | import { RegisterServerAuthExternalOptions } from '@server/types' | 1 | import { RegisterServerAuthExternalOptions } from '@server/types' |
2 | import { | 2 | import { |
3 | MAbuse, | 3 | MAbuse, |
4 | MAbuseMessage, | ||
4 | MAccountBlocklist, | 5 | MAccountBlocklist, |
5 | MActorUrl, | 6 | MActorUrl, |
6 | MStreamingPlaylist, | 7 | MStreamingPlaylist, |
@@ -78,6 +79,7 @@ declare module 'express' { | |||
78 | videoCaption?: MVideoCaptionVideo | 79 | videoCaption?: MVideoCaptionVideo |
79 | 80 | ||
80 | abuse?: MAbuse | 81 | abuse?: MAbuse |
82 | abuseMessage?: MAbuseMessage | ||
81 | 83 | ||
82 | videoStreamingPlaylist?: MStreamingPlaylist | 84 | videoStreamingPlaylist?: MStreamingPlaylist |
83 | 85 | ||
diff --git a/shared/extra-utils/moderation/abuses.ts b/shared/extra-utils/moderation/abuses.ts index 62af9556e..7db75cebb 100644 --- a/shared/extra-utils/moderation/abuses.ts +++ b/shared/extra-utils/moderation/abuses.ts | |||
@@ -54,7 +54,7 @@ function reportAbuse (options: { | |||
54 | }) | 54 | }) |
55 | } | 55 | } |
56 | 56 | ||
57 | function getAbusesList (options: { | 57 | function getAdminAbusesList (options: { |
58 | url: string | 58 | url: string |
59 | token: string | 59 | token: string |
60 | 60 | ||
@@ -117,6 +117,48 @@ function getAbusesList (options: { | |||
117 | }) | 117 | }) |
118 | } | 118 | } |
119 | 119 | ||
120 | function getUserAbusesList (options: { | ||
121 | url: string | ||
122 | token: string | ||
123 | |||
124 | start?: number | ||
125 | count?: number | ||
126 | sort?: string | ||
127 | |||
128 | id?: number | ||
129 | search?: string | ||
130 | state?: AbuseState | ||
131 | }) { | ||
132 | const { | ||
133 | url, | ||
134 | token, | ||
135 | start, | ||
136 | count, | ||
137 | sort, | ||
138 | id, | ||
139 | search, | ||
140 | state | ||
141 | } = options | ||
142 | const path = '/api/v1/users/me/abuses' | ||
143 | |||
144 | const query = { | ||
145 | id, | ||
146 | search, | ||
147 | state, | ||
148 | start, | ||
149 | count, | ||
150 | sort: sort || 'createdAt' | ||
151 | } | ||
152 | |||
153 | return makeGetRequest({ | ||
154 | url, | ||
155 | path, | ||
156 | token, | ||
157 | query, | ||
158 | statusCodeExpected: 200 | ||
159 | }) | ||
160 | } | ||
161 | |||
120 | function updateAbuse ( | 162 | function updateAbuse ( |
121 | url: string, | 163 | url: string, |
122 | token: string, | 164 | token: string, |
@@ -146,11 +188,49 @@ function deleteAbuse (url: string, token: string, abuseId: number, statusCodeExp | |||
146 | }) | 188 | }) |
147 | } | 189 | } |
148 | 190 | ||
191 | function listAbuseMessages (url: string, token: string, abuseId: number, statusCodeExpected = 200) { | ||
192 | const path = '/api/v1/abuses/' + abuseId + '/messages' | ||
193 | |||
194 | return makeGetRequest({ | ||
195 | url, | ||
196 | token, | ||
197 | path, | ||
198 | statusCodeExpected | ||
199 | }) | ||
200 | } | ||
201 | |||
202 | function deleteAbuseMessage (url: string, token: string, abuseId: number, messageId: number, statusCodeExpected = 204) { | ||
203 | const path = '/api/v1/abuses/' + abuseId + '/messages/' + messageId | ||
204 | |||
205 | return makeDeleteRequest({ | ||
206 | url, | ||
207 | token, | ||
208 | path, | ||
209 | statusCodeExpected | ||
210 | }) | ||
211 | } | ||
212 | |||
213 | function addAbuseMessage (url: string, token: string, abuseId: number, message: string, statusCodeExpected = 200) { | ||
214 | const path = '/api/v1/abuses/' + abuseId + '/messages' | ||
215 | |||
216 | return makePostBodyRequest({ | ||
217 | url, | ||
218 | token, | ||
219 | path, | ||
220 | fields: { message }, | ||
221 | statusCodeExpected | ||
222 | }) | ||
223 | } | ||
224 | |||
149 | // --------------------------------------------------------------------------- | 225 | // --------------------------------------------------------------------------- |
150 | 226 | ||
151 | export { | 227 | export { |
152 | reportAbuse, | 228 | reportAbuse, |
153 | getAbusesList, | 229 | getAdminAbusesList, |
154 | updateAbuse, | 230 | updateAbuse, |
155 | deleteAbuse | 231 | deleteAbuse, |
232 | getUserAbusesList, | ||
233 | listAbuseMessages, | ||
234 | deleteAbuseMessage, | ||
235 | addAbuseMessage | ||
156 | } | 236 | } |
diff --git a/shared/models/moderation/abuse/abuse-message.model.ts b/shared/models/moderation/abuse/abuse-message.model.ts new file mode 100644 index 000000000..02072d5ce --- /dev/null +++ b/shared/models/moderation/abuse/abuse-message.model.ts | |||
@@ -0,0 +1,9 @@ | |||
1 | import { AccountSummary } from '@shared/models' | ||
2 | |||
3 | export interface AbuseMessage { | ||
4 | id: number | ||
5 | message: string | ||
6 | byModerator: boolean | ||
7 | |||
8 | account: AccountSummary | ||
9 | } | ||
diff --git a/shared/models/moderation/abuse/abuse.model.ts b/shared/models/moderation/abuse/abuse.model.ts index 0a0c6bd35..7f126ba4a 100644 --- a/shared/models/moderation/abuse/abuse.model.ts +++ b/shared/models/moderation/abuse/abuse.model.ts | |||
@@ -4,7 +4,7 @@ import { AbusePredefinedReasonsString } from './abuse-reason.model' | |||
4 | import { VideoConstant } from '../../videos/video-constant.model' | 4 | import { VideoConstant } from '../../videos/video-constant.model' |
5 | import { VideoChannel } from '../../videos/channel/video-channel.model' | 5 | import { VideoChannel } from '../../videos/channel/video-channel.model' |
6 | 6 | ||
7 | export interface VideoAbuse { | 7 | export interface AdminVideoAbuse { |
8 | id: number | 8 | id: number |
9 | name: string | 9 | name: string |
10 | uuid: string | 10 | uuid: string |
@@ -23,7 +23,7 @@ export interface VideoAbuse { | |||
23 | nthReport: number | 23 | nthReport: number |
24 | } | 24 | } |
25 | 25 | ||
26 | export interface VideoCommentAbuse { | 26 | export interface AdminVideoCommentAbuse { |
27 | id: number | 27 | id: number |
28 | threadId: number | 28 | threadId: number |
29 | 29 | ||
@@ -38,7 +38,7 @@ export interface VideoCommentAbuse { | |||
38 | deleted: boolean | 38 | deleted: boolean |
39 | } | 39 | } |
40 | 40 | ||
41 | export interface Abuse { | 41 | export interface AdminAbuse { |
42 | id: number | 42 | id: number |
43 | 43 | ||
44 | reason: string | 44 | reason: string |
@@ -50,8 +50,8 @@ export interface Abuse { | |||
50 | state: VideoConstant<AbuseState> | 50 | state: VideoConstant<AbuseState> |
51 | moderationComment?: string | 51 | moderationComment?: string |
52 | 52 | ||
53 | video?: VideoAbuse | 53 | video?: AdminVideoAbuse |
54 | comment?: VideoCommentAbuse | 54 | comment?: AdminVideoCommentAbuse |
55 | 55 | ||
56 | createdAt: Date | 56 | createdAt: Date |
57 | updatedAt: Date | 57 | updatedAt: Date |
@@ -59,6 +59,8 @@ export interface Abuse { | |||
59 | countReportsForReporter?: number | 59 | countReportsForReporter?: number |
60 | countReportsForReportee?: number | 60 | countReportsForReportee?: number |
61 | 61 | ||
62 | countMessages: number | ||
63 | |||
62 | // FIXME: deprecated in 2.3, remove the following properties | 64 | // FIXME: deprecated in 2.3, remove the following properties |
63 | 65 | ||
64 | // @deprecated | 66 | // @deprecated |
@@ -71,3 +73,10 @@ export interface Abuse { | |||
71 | // @deprecated | 73 | // @deprecated |
72 | nth?: number | 74 | nth?: number |
73 | } | 75 | } |
76 | |||
77 | export type UserVideoAbuse = Omit<AdminVideoAbuse, 'countReports' | 'nthReport'> | ||
78 | |||
79 | export type UserVideoCommentAbuse = AdminVideoCommentAbuse | ||
80 | |||
81 | export type UserAbuse = Omit<AdminAbuse, 'reporterAccount' | 'countReportsForReportee' | 'countReportsForReporter' | 'startAt' | 'endAt' | ||
82 | | 'count' | 'nth'> | ||
diff --git a/shared/models/moderation/abuse/index.ts b/shared/models/moderation/abuse/index.ts index 55046426a..b518517a6 100644 --- a/shared/models/moderation/abuse/index.ts +++ b/shared/models/moderation/abuse/index.ts | |||
@@ -1,5 +1,6 @@ | |||
1 | export * from './abuse-create.model' | 1 | export * from './abuse-create.model' |
2 | export * from './abuse-filter.type' | 2 | export * from './abuse-filter.type' |
3 | export * from './abuse-message.model' | ||
3 | export * from './abuse-reason.model' | 4 | export * from './abuse-reason.model' |
4 | export * from './abuse-state.model' | 5 | export * from './abuse-state.model' |
5 | export * from './abuse-update.model' | 6 | export * from './abuse-update.model' |