]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/commitdiff
Implement abuses check params
authorChocobozzz <me@florianbigard.com>
Tue, 7 Jul 2020 08:57:04 +0000 (10:57 +0200)
committerChocobozzz <chocobozzz@cpy.re>
Fri, 10 Jul 2020 12:02:41 +0000 (14:02 +0200)
22 files changed:
server/controllers/api/abuse.ts
server/helpers/custom-validators/abuses.ts
server/helpers/custom-validators/video-comments.ts
server/helpers/middlewares/abuses.ts
server/helpers/middlewares/accounts.ts
server/middlewares/validators/abuse.ts
server/middlewares/validators/videos/video-comments.ts
server/models/abuse/abuse.ts
server/models/abuse/video-comment-abuse.ts
server/models/video/video-comment.ts
server/models/video/video.ts
server/tests/api/check-params/abuses.ts [new file with mode: 0644]
server/tests/api/check-params/index.ts
server/tests/api/check-params/video-abuses.ts
server/types/models/moderation/abuse.ts
server/typings/express/index.d.ts
shared/extra-utils/moderation/abuses.ts
shared/models/moderation/abuse/abuse-create.model.ts
shared/models/moderation/abuse/abuse-filter.ts [deleted file]
shared/models/moderation/abuse/abuse-filter.type.ts [new file with mode: 0644]
shared/models/moderation/abuse/abuse.model.ts
shared/models/moderation/abuse/index.ts

index ee046cb3aa80fe1c4e32afc61c358884daf088fe..38808021db33cff1b0f8ecc7cf9cd2713d8cffbb 100644 (file)
@@ -23,7 +23,7 @@ import { AccountModel } from '../../models/account/account'
 
 const abuseRouter = express.Router()
 
-abuseRouter.get('/abuse',
+abuseRouter.get('/',
   authenticate,
   ensureUserHasRight(UserRight.MANAGE_ABUSES),
   paginationValidator,
@@ -33,18 +33,18 @@ abuseRouter.get('/abuse',
   abuseListValidator,
   asyncMiddleware(listAbuses)
 )
-abuseRouter.put('/:videoId/abuse/:id',
+abuseRouter.put('/:id',
   authenticate,
   ensureUserHasRight(UserRight.MANAGE_ABUSES),
   asyncMiddleware(abuseUpdateValidator),
   asyncRetryTransactionMiddleware(updateAbuse)
 )
-abuseRouter.post('/:videoId/abuse',
+abuseRouter.post('/',
   authenticate,
   asyncMiddleware(abuseReportValidator),
   asyncRetryTransactionMiddleware(reportAbuse)
 )
-abuseRouter.delete('/:videoId/abuse/:id',
+abuseRouter.delete('/:id',
   authenticate,
   ensureUserHasRight(UserRight.MANAGE_ABUSES),
   asyncMiddleware(abuseGetValidator),
@@ -74,7 +74,7 @@ async function listAbuses (req: express.Request, res: express.Response) {
     count: req.query.count,
     sort: req.query.sort,
     id: req.query.id,
-    filter: 'video',
+    filter: req.query.filter,
     predefinedReason: req.query.predefinedReason,
     search: req.query.search,
     state: req.query.state,
index a6a895c659edb130d67dbc584d5e6a43e3f93c23..c21468caafedd4099144e1206f23cace76ddb9c7 100644 (file)
@@ -1,6 +1,6 @@
 import validator from 'validator'
-import { abusePredefinedReasonsMap, AbusePredefinedReasonsString, AbuseVideoIs } from '@shared/models'
-import { CONSTRAINTS_FIELDS, ABUSE_STATES } from '../../initializers/constants'
+import { AbuseFilter, abusePredefinedReasonsMap, AbusePredefinedReasonsString, AbuseVideoIs, AbuseCreate } from '@shared/models'
+import { ABUSE_STATES, CONSTRAINTS_FIELDS } from '../../initializers/constants'
 import { exists, isArray } from './misc'
 
 const VIDEO_ABUSES_CONSTRAINTS_FIELDS = CONSTRAINTS_FIELDS.ABUSES
@@ -13,7 +13,11 @@ function isAbusePredefinedReasonValid (value: AbusePredefinedReasonsString) {
   return exists(value) && value in abusePredefinedReasonsMap
 }
 
-function isAbusePredefinedReasonsValid (value: AbusePredefinedReasonsString[]) {
+function isAbuseFilterValid (value: AbuseFilter) {
+  return value === 'video' || value === 'comment' || value === 'account'
+}
+
+function areAbusePredefinedReasonsValid (value: AbusePredefinedReasonsString[]) {
   return exists(value) && isArray(value) && value.every(v => v in abusePredefinedReasonsMap)
 }
 
@@ -22,7 +26,9 @@ function isAbuseTimestampValid (value: number) {
 }
 
 function isAbuseTimestampCoherent (endAt: number, { req }) {
-  return exists(req.body.startAt) && endAt > req.body.startAt
+  const startAt = (req.body as AbuseCreate).video.startAt
+
+  return exists(startAt) && endAt > startAt
 }
 
 function isAbuseModerationCommentValid (value: string) {
@@ -44,8 +50,9 @@ function isAbuseVideoIsValid (value: AbuseVideoIs) {
 
 export {
   isAbuseReasonValid,
+  isAbuseFilterValid,
   isAbusePredefinedReasonValid,
-  isAbusePredefinedReasonsValid,
+  areAbusePredefinedReasonsValid as isAbusePredefinedReasonsValid,
   isAbuseTimestampValid,
   isAbuseTimestampCoherent,
   isAbuseModerationCommentValid,
index 846f28b17ad744ab6ed011e504041f458a7b43f2..a01680cbee10b7b3ad2fefddebb829a4c32434cc 100644 (file)
@@ -1,6 +1,8 @@
-import 'multer'
+import * as express from 'express'
 import validator from 'validator'
+import { VideoCommentModel } from '@server/models/video/video-comment'
 import { CONSTRAINTS_FIELDS } from '../../initializers/constants'
+import { MVideoId } from '@server/types/models'
 
 const VIDEO_COMMENTS_CONSTRAINTS_FIELDS = CONSTRAINTS_FIELDS.VIDEO_COMMENTS
 
@@ -8,8 +10,83 @@ function isValidVideoCommentText (value: string) {
   return value === null || validator.isLength(value, VIDEO_COMMENTS_CONSTRAINTS_FIELDS.TEXT)
 }
 
+async function doesVideoCommentThreadExist (idArg: number | string, video: MVideoId, res: express.Response) {
+  const id = parseInt(idArg + '', 10)
+  const videoComment = await VideoCommentModel.loadById(id)
+
+  if (!videoComment) {
+    res.status(404)
+      .json({ error: 'Video comment thread not found' })
+      .end()
+
+    return false
+  }
+
+  if (videoComment.videoId !== video.id) {
+    res.status(400)
+      .json({ error: 'Video comment is not associated to this video.' })
+      .end()
+
+    return false
+  }
+
+  if (videoComment.inReplyToCommentId !== null) {
+    res.status(400)
+      .json({ error: 'Video comment is not a thread.' })
+      .end()
+
+    return false
+  }
+
+  res.locals.videoCommentThread = videoComment
+  return true
+}
+
+async function doesVideoCommentExist (idArg: number | string, video: MVideoId, res: express.Response) {
+  const id = parseInt(idArg + '', 10)
+  const videoComment = await VideoCommentModel.loadByIdAndPopulateVideoAndAccountAndReply(id)
+
+  if (!videoComment) {
+    res.status(404)
+      .json({ error: 'Video comment thread not found' })
+      .end()
+
+    return false
+  }
+
+  if (videoComment.videoId !== video.id) {
+    res.status(400)
+      .json({ error: 'Video comment is not associated to this video.' })
+      .end()
+
+    return false
+  }
+
+  res.locals.videoCommentFull = videoComment
+  return true
+}
+
+async function doesCommentIdExist (idArg: number | string, res: express.Response) {
+  const id = parseInt(idArg + '', 10)
+  const videoComment = await VideoCommentModel.loadById(id)
+
+  if (!videoComment) {
+    res.status(404)
+      .json({ error: 'Video comment thread not found' })
+
+    return false
+  }
+
+  res.locals.videoComment = videoComment
+
+  return true
+}
+
 // ---------------------------------------------------------------------------
 
 export {
-  isValidVideoCommentText
+  isValidVideoCommentText,
+  doesVideoCommentThreadExist,
+  doesVideoCommentExist,
+  doesCommentIdExist
 }
index 3906f67604893ad76119ef972e82d472730dd1e4..b102273a2794b2c5f03c2f9628da6a8c3d19594a 100644 (file)
@@ -17,7 +17,6 @@ async function doesVideoAbuseExist (abuseIdArg: number | string, videoUUID: stri
   if (abuse === null) {
     res.status(404)
        .json({ error: 'Video abuse not found' })
-       .end()
 
     return false
   }
@@ -26,8 +25,18 @@ async function doesVideoAbuseExist (abuseIdArg: number | string, videoUUID: stri
   return true
 }
 
-async function doesAbuseExist (abuseIdArg: number | string, videoUUID: string, res: Response) {
+async function doesAbuseExist (abuseId: number | string, res: Response) {
+  const abuse = await AbuseModel.loadById(parseInt(abuseId + '', 10))
 
+  if (!abuse) {
+    res.status(404)
+       .json({ error: 'Video abuse not found' })
+
+    return false
+  }
+
+  res.locals.abuse = abuse
+  return true
 }
 
 // ---------------------------------------------------------------------------
index bddea7eaa3f12326be09ba401da79fbdc45b2330..29b4ed1a6f56c737210f85817f5c6b97575df069 100644 (file)
@@ -3,8 +3,8 @@ import { AccountModel } from '../../models/account/account'
 import * as Bluebird from 'bluebird'
 import { MAccountDefault } from '../../types/models'
 
-function doesAccountIdExist (id: number, res: Response, sendNotFound = true) {
-  const promise = AccountModel.load(id)
+function doesAccountIdExist (id: number | string, res: Response, sendNotFound = true) {
+  const promise = AccountModel.load(parseInt(id + '', 10))
 
   return doesAccountExist(promise, res, sendNotFound)
 }
index f098e2ff9ad57d4ad3a13601dba3723c68cd73a7..048dbead0e70a0aa5d1bdc9d86b354afa67c97e3 100644 (file)
@@ -1,6 +1,7 @@
 import * as express from 'express'
 import { body, param, query } from 'express-validator'
 import {
+  isAbuseFilterValid,
   isAbuseModerationCommentValid,
   isAbusePredefinedReasonsValid,
   isAbusePredefinedReasonValid,
@@ -11,29 +12,28 @@ import {
   isAbuseVideoIsValid
 } from '@server/helpers/custom-validators/abuses'
 import { exists, isIdOrUUIDValid, isIdValid, toIntOrNull } from '@server/helpers/custom-validators/misc'
+import { doesCommentIdExist } from '@server/helpers/custom-validators/video-comments'
 import { logger } from '@server/helpers/logger'
-import { doesAbuseExist, doesVideoAbuseExist, doesVideoExist } from '@server/helpers/middlewares'
+import { doesAbuseExist, doesAccountIdExist, doesVideoAbuseExist, doesVideoExist } from '@server/helpers/middlewares'
+import { AbuseCreate } from '@shared/models'
 import { areValidationErrors } from './utils'
 
 const abuseReportValidator = [
-  param('videoId')
+  body('account.id')
+    .optional()
+    .custom(isIdValid)
+    .withMessage('Should have a valid accountId'),
+
+  body('video.id')
+    .optional()
     .custom(isIdOrUUIDValid)
-    .not()
-    .isEmpty()
     .withMessage('Should have a valid videoId'),
-  body('reason')
-    .custom(isAbuseReasonValid)
-    .withMessage('Should have a valid reason'),
-  body('predefinedReasons')
-    .optional()
-    .custom(isAbusePredefinedReasonsValid)
-    .withMessage('Should have a valid list of predefined reasons'),
-  body('startAt')
+  body('video.startAt')
     .optional()
     .customSanitizer(toIntOrNull)
     .custom(isAbuseTimestampValid)
     .withMessage('Should have valid starting time value'),
-  body('endAt')
+  body('video.endAt')
     .optional()
     .customSanitizer(toIntOrNull)
     .custom(isAbuseTimestampValid)
@@ -42,47 +42,70 @@ const abuseReportValidator = [
     .custom(isAbuseTimestampCoherent)
     .withMessage('Should have a startAt timestamp beginning before endAt'),
 
+  body('comment.id')
+    .optional()
+    .custom(isIdValid)
+    .withMessage('Should have a valid commentId'),
+
+  body('reason')
+    .custom(isAbuseReasonValid)
+    .withMessage('Should have a valid reason'),
+
+  body('predefinedReasons')
+    .optional()
+    .custom(isAbusePredefinedReasonsValid)
+    .withMessage('Should have a valid list of predefined reasons'),
+
   async (req: express.Request, res: express.Response, next: express.NextFunction) => {
     logger.debug('Checking abuseReport parameters', { parameters: req.body })
 
     if (areValidationErrors(req, res)) return
-    if (!await doesVideoExist(req.params.videoId, res)) return
 
-    // TODO: check comment or video (exlusive)
+    const body: AbuseCreate = req.body
+
+    if (body.video?.id && !await doesVideoExist(body.video.id, res)) return
+    if (body.account?.id && !await doesAccountIdExist(body.account.id, res)) return
+    if (body.comment?.id && !await doesCommentIdExist(body.comment.id, res)) return
+
+    if (!body.video?.id && !body.account?.id && !body.comment?.id) {
+      res.status(400)
+        .json({ error: 'video id or account id or comment id is required.' })
+
+      return
+    }
 
     return next()
   }
 ]
 
 const abuseGetValidator = [
-  param('videoId').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid videoId'),
   param('id').custom(isIdValid).not().isEmpty().withMessage('Should have a valid id'),
 
   async (req: express.Request, res: express.Response, next: express.NextFunction) => {
     logger.debug('Checking abuseGetValidator parameters', { parameters: req.body })
 
     if (areValidationErrors(req, res)) return
-    // if (!await doesAbuseExist(req.params.id, req.params.videoId, res)) return
+    if (!await doesAbuseExist(req.params.id, res)) return
 
     return next()
   }
 ]
 
 const abuseUpdateValidator = [
-  param('videoId').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid videoId'),
   param('id').custom(isIdValid).not().isEmpty().withMessage('Should have a valid id'),
+
   body('state')
     .optional()
-    .custom(isAbuseStateValid).withMessage('Should have a valid video abuse state'),
+    .custom(isAbuseStateValid).withMessage('Should have a valid abuse state'),
   body('moderationComment')
     .optional()
-    .custom(isAbuseModerationCommentValid).withMessage('Should have a valid video moderation comment'),
+    .custom(isAbuseModerationCommentValid).withMessage('Should have a valid moderation comment'),
 
   async (req: express.Request, res: express.Response, next: express.NextFunction) => {
     logger.debug('Checking abuseUpdateValidator parameters', { parameters: req.body })
 
     if (areValidationErrors(req, res)) return
-    // if (!await doesAbuseExist(req.params.id, req.params.videoId, res)) return
+    if (!await doesAbuseExist(req.params.id, res)) return
 
     return next()
   }
@@ -92,6 +115,10 @@ const abuseListValidator = [
   query('id')
     .optional()
     .custom(isIdValid).withMessage('Should have a valid id'),
+  query('filter')
+    .optional()
+    .custom(isAbuseFilterValid)
+    .withMessage('Should have a valid filter'),
   query('predefinedReason')
     .optional()
     .custom(isAbusePredefinedReasonValid)
@@ -151,10 +178,7 @@ const videoAbuseReportValidator = [
     .optional()
     .customSanitizer(toIntOrNull)
     .custom(isAbuseTimestampValid)
-    .withMessage('Should have valid ending time value')
-    .bail()
-    .custom(isAbuseTimestampCoherent)
-    .withMessage('Should have a startAt timestamp beginning before endAt'),
+    .withMessage('Should have valid ending time value'),
 
   async (req: express.Request, res: express.Response, next: express.NextFunction) => {
     logger.debug('Checking videoAbuseReport parameters', { parameters: req.body })
index ef019fcf915f126f622c4c3df08d7dec00fe21c5..77f5c6ff3c5dd5bf41c51951308dc8e8fefda410 100644 (file)
@@ -3,13 +3,16 @@ import { body, param } from 'express-validator'
 import { MUserAccountUrl } from '@server/types/models'
 import { UserRight } from '../../../../shared'
 import { isIdOrUUIDValid, isIdValid } from '../../../helpers/custom-validators/misc'
-import { isValidVideoCommentText } from '../../../helpers/custom-validators/video-comments'
+import {
+  doesVideoCommentExist,
+  doesVideoCommentThreadExist,
+  isValidVideoCommentText
+} from '../../../helpers/custom-validators/video-comments'
 import { logger } from '../../../helpers/logger'
 import { doesVideoExist } from '../../../helpers/middlewares'
 import { AcceptResult, isLocalVideoCommentReplyAccepted, isLocalVideoThreadAccepted } from '../../../lib/moderation'
 import { Hooks } from '../../../lib/plugins/hooks'
-import { VideoCommentModel } from '../../../models/video/video-comment'
-import { MCommentOwnerVideoReply, MVideo, MVideoFullLight, MVideoId } from '../../../types/models/video'
+import { MCommentOwnerVideoReply, MVideo, MVideoFullLight } from '../../../types/models/video'
 import { areValidationErrors } from '../utils'
 
 const listVideoCommentThreadsValidator = [
@@ -120,67 +123,10 @@ export {
 
 // ---------------------------------------------------------------------------
 
-async function doesVideoCommentThreadExist (idArg: number | string, video: MVideoId, res: express.Response) {
-  const id = parseInt(idArg + '', 10)
-  const videoComment = await VideoCommentModel.loadById(id)
-
-  if (!videoComment) {
-    res.status(404)
-      .json({ error: 'Video comment thread not found' })
-      .end()
-
-    return false
-  }
-
-  if (videoComment.videoId !== video.id) {
-    res.status(400)
-      .json({ error: 'Video comment is not associated to this video.' })
-      .end()
-
-    return false
-  }
-
-  if (videoComment.inReplyToCommentId !== null) {
-    res.status(400)
-      .json({ error: 'Video comment is not a thread.' })
-      .end()
-
-    return false
-  }
-
-  res.locals.videoCommentThread = videoComment
-  return true
-}
-
-async function doesVideoCommentExist (idArg: number | string, video: MVideoId, res: express.Response) {
-  const id = parseInt(idArg + '', 10)
-  const videoComment = await VideoCommentModel.loadByIdAndPopulateVideoAndAccountAndReply(id)
-
-  if (!videoComment) {
-    res.status(404)
-      .json({ error: 'Video comment thread not found' })
-      .end()
-
-    return false
-  }
-
-  if (videoComment.videoId !== video.id) {
-    res.status(400)
-      .json({ error: 'Video comment is not associated to this video.' })
-      .end()
-
-    return false
-  }
-
-  res.locals.videoCommentFull = videoComment
-  return true
-}
-
 function isVideoCommentsEnabled (video: MVideo, res: express.Response) {
   if (video.commentsEnabled !== true) {
     res.status(409)
       .json({ error: 'Video comments are disabled for this video.' })
-      .end()
 
     return false
   }
@@ -192,7 +138,7 @@ function checkUserCanDeleteVideoComment (user: MUserAccountUrl, videoComment: MC
   if (videoComment.isDeleted()) {
     res.status(409)
       .json({ error: 'This comment is already deleted' })
-      .end()
+
     return false
   }
 
@@ -240,7 +186,7 @@ async function isVideoCommentAccepted (req: express.Request, res: express.Respon
   if (!acceptedResult || acceptedResult.accepted !== true) {
     logger.info('Refused local comment.', { acceptedResult, acceptParameters })
     res.status(403)
-              .json({ error: acceptedResult.errorMessage || 'Refused local comment' })
+       .json({ error: acceptedResult.errorMessage || 'Refused local comment' })
 
     return false
   }
index 4f99f9c9b836e7a6955fa0f2229995f4fc8096b6..087c77bd3ef3c17cc501d527d29d088ff6c43766 100644 (file)
@@ -19,16 +19,17 @@ import {
 import { isAbuseModerationCommentValid, isAbuseReasonValid, isAbuseStateValid } from '@server/helpers/custom-validators/abuses'
 import {
   Abuse,
+  AbuseFilter,
   AbuseObject,
   AbusePredefinedReasons,
   abusePredefinedReasonsMap,
   AbusePredefinedReasonsString,
   AbuseState,
   AbuseVideoIs,
-  VideoAbuse
+  VideoAbuse,
+  VideoCommentAbuse
 } from '@shared/models'
-import { AbuseFilter } from '@shared/models/moderation/abuse/abuse-filter'
-import { CONSTRAINTS_FIELDS, ABUSE_STATES } from '../../initializers/constants'
+import { ABUSE_STATES, CONSTRAINTS_FIELDS } from '../../initializers/constants'
 import { MAbuse, MAbuseAP, MAbuseFormattable, MUserAccountId } from '../../types/models'
 import { AccountModel, ScopeNames as AccountScopeNames } from '../account/account'
 import { buildBlockedAccountSQL, getSort, searchAttribute, throwIfNotValid } from '../utils'
@@ -38,6 +39,7 @@ import { VideoBlacklistModel } from '../video/video-blacklist'
 import { ScopeNames as VideoChannelScopeNames, SummaryOptions, VideoChannelModel } from '../video/video-channel'
 import { VideoAbuseModel } from './video-abuse'
 import { VideoCommentAbuseModel } from './video-comment-abuse'
+import { VideoCommentModel } from '../video/video-comment'
 
 export enum ScopeNames {
   FOR_API = 'FOR_API'
@@ -66,19 +68,18 @@ export enum ScopeNames {
     serverAccountId: number
     userAccountId: number
   }) => {
-    const onlyBlacklisted = options.videoIs === 'blacklisted'
-    const videoRequired = !!(onlyBlacklisted || options.searchVideo || options.searchVideoChannel)
+    const whereAnd: WhereOptions[] = []
 
-    const where = {
+    whereAnd.push({
       reporterAccountId: {
         [Op.notIn]: literal('(' + buildBlockedAccountSQL([ options.serverAccountId, options.userAccountId ]) + ')')
       }
-    }
+    })
 
     if (options.search) {
       const escapedSearch = AbuseModel.sequelize.escape('%' + options.search + '%')
 
-      Object.assign(where, {
+      whereAnd.push({
         [Op.or]: [
           {
             [Op.and]: [
@@ -110,11 +111,11 @@ export enum ScopeNames {
       })
     }
 
-    if (options.id) Object.assign(where, { id: options.id })
-    if (options.state) Object.assign(where, { state: options.state })
+    if (options.id) whereAnd.push({ id: options.id })
+    if (options.state) whereAnd.push({ state: options.state })
 
     if (options.videoIs === 'deleted') {
-      Object.assign(where, {
+      whereAnd.push({
         '$VideoAbuse.deletedVideo$': {
           [Op.not]: null
         }
@@ -122,13 +123,23 @@ export enum ScopeNames {
     }
 
     if (options.predefinedReasonId) {
-      Object.assign(where, {
+      whereAnd.push({
         predefinedReasons: {
           [Op.contains]: [ options.predefinedReasonId ]
         }
       })
     }
 
+    if (options.filter === 'account') {
+      whereAnd.push({
+        videoId: null,
+        commentId: null
+      })
+    }
+
+    const onlyBlacklisted = options.videoIs === 'blacklisted'
+    const videoRequired = !!(onlyBlacklisted || options.searchVideo || options.searchVideoChannel)
+
     return {
       attributes: {
         include: [
@@ -222,6 +233,23 @@ export enum ScopeNames {
           required: true,
           where: searchAttribute(options.searchReportee, 'name')
         },
+        {
+          model: VideoCommentAbuseModel.unscoped(),
+          required: options.filter === 'comment',
+          include: [
+            {
+              model: VideoCommentModel.unscoped(),
+              required: false,
+              include: [
+                {
+                  model: VideoModel.unscoped(),
+                  attributes: [ 'name', 'id', 'uuid' ],
+                  required: true
+                }
+              ]
+            }
+          ]
+        },
         {
           model: VideoAbuseModel,
           required: options.filter === 'video' || !!options.videoIs || videoRequired,
@@ -241,8 +269,7 @@ export enum ScopeNames {
                   include: [
                     {
                       model: AccountModel.scope(AccountScopeNames.SUMMARY),
-                      required: true,
-                      where: searchAttribute(options.searchReportee, 'name')
+                      required: true
                     }
                   ]
                 },
@@ -256,7 +283,9 @@ export enum ScopeNames {
           ]
         }
       ],
-      where
+      where: {
+        [Op.and]: whereAnd
+      }
     }
   }
 }))
@@ -348,6 +377,7 @@ export class AbuseModel extends Model<AbuseModel> {
   })
   VideoAbuse: VideoAbuseModel
 
+  // FIXME: deprecated in 2.3. Remove these validators
   static loadByIdAndVideoId (id: number, videoId?: number, uuid?: string): Bluebird<MAbuse> {
     const videoWhere: WhereOptions = {}
 
@@ -369,6 +399,16 @@ export class AbuseModel extends Model<AbuseModel> {
     return AbuseModel.findOne(query)
   }
 
+  static loadById (id: number): Bluebird<MAbuse> {
+    const query = {
+      where: {
+        id
+      }
+    }
+
+    return AbuseModel.findOne(query)
+  }
+
   static listForApi (parameters: {
     start: number
     count: number
@@ -454,6 +494,7 @@ export class AbuseModel extends Model<AbuseModel> {
     const countReportsForReporteeDeletedVideo = this.get('countReportsForReportee__deletedVideo') as number
 
     let video: VideoAbuse
+    let comment: VideoCommentAbuse
 
     if (this.VideoAbuse) {
       const abuseModel = this.VideoAbuse
@@ -475,6 +516,24 @@ export class AbuseModel extends Model<AbuseModel> {
       }
     }
 
+    if (this.VideoCommentAbuse) {
+      const abuseModel = this.VideoCommentAbuse
+      const entity = abuseModel.VideoComment || abuseModel.deletedComment
+
+      comment = {
+        id: entity.id,
+        text: entity.text,
+
+        deleted: !abuseModel.VideoComment,
+
+        video: {
+          id: entity.Video.id,
+          name: entity.Video.name,
+          uuid: entity.Video.uuid
+        }
+      }
+    }
+
     return {
       id: this.id,
       reason: this.reason,
@@ -490,7 +549,7 @@ export class AbuseModel extends Model<AbuseModel> {
       moderationComment: this.moderationComment,
 
       video,
-      comment: null,
+      comment,
 
       createdAt: this.createdAt,
       updatedAt: this.updatedAt,
index b4cc2762e155a798adda92549751592c2dc8b911..de9f4d5fd722a2d4b8a27c71046dd64a00d20212 100644 (file)
@@ -25,7 +25,7 @@ export class VideoCommentAbuseModel extends Model<VideoCommentAbuseModel> {
   @AllowNull(true)
   @Default(null)
   @Column(DataType.JSONB)
-  deletedComment: VideoComment
+  deletedComment: VideoComment & { Video: { name: string, id: number, uuid: string }}
 
   @ForeignKey(() => AbuseModel)
   @Column
index 90625d987a9f6436ecf125f3360ee9a8fe544a1b..fb6078ed889411496331fda3be8118b0fb547b05 100644 (file)
@@ -1,7 +1,22 @@
 import * as Bluebird from 'bluebird'
 import { uniq } from 'lodash'
 import { FindOptions, Op, Order, ScopeOptions, Sequelize, Transaction } from 'sequelize'
-import { AllowNull, BelongsTo, Column, CreatedAt, DataType, ForeignKey, Is, Model, Scopes, Table, UpdatedAt } from 'sequelize-typescript'
+import {
+  AllowNull,
+  BeforeDestroy,
+  BelongsTo,
+  Column,
+  CreatedAt,
+  DataType,
+  ForeignKey,
+  HasMany,
+  Is,
+  Model,
+  Scopes,
+  Table,
+  UpdatedAt
+} from 'sequelize-typescript'
+import { logger } from '@server/helpers/logger'
 import { getServerActor } from '@server/models/application/application'
 import { MAccount, MAccountId, MUserAccountId } from '@server/types/models'
 import { VideoPrivacy } from '@shared/models'
@@ -24,6 +39,7 @@ import {
   MCommentOwnerVideoReply,
   MVideoImmutable
 } from '../../types/models/video'
+import { VideoCommentAbuseModel } from '../abuse/video-comment-abuse'
 import { AccountModel } from '../account/account'
 import { ActorModel, unusedActorAttributesForAPI } from '../activitypub/actor'
 import { buildBlockedAccountSQL, buildLocalAccountIdsIn, getCommentSort, throwIfNotValid } from '../utils'
@@ -224,6 +240,53 @@ export class VideoCommentModel extends Model<VideoCommentModel> {
   })
   Account: AccountModel
 
+  @HasMany(() => VideoCommentAbuseModel, {
+    foreignKey: {
+      name: 'commentId',
+      allowNull: true
+    },
+    onDelete: 'set null'
+  })
+  CommentAbuses: VideoCommentAbuseModel[]
+
+  @BeforeDestroy
+  static async saveEssentialDataToAbuses (instance: VideoCommentModel, options) {
+    const tasks: Promise<any>[] = []
+
+    if (!Array.isArray(instance.CommentAbuses)) {
+      instance.CommentAbuses = await instance.$get('CommentAbuses')
+
+      if (instance.CommentAbuses.length === 0) return undefined
+    }
+
+    if (!instance.Video) {
+      instance.Video = await instance.$get('Video')
+    }
+
+    logger.info('Saving video comment %s for abuse.', instance.url)
+
+    const details = Object.assign(instance.toFormattedJSON(), {
+      Video: {
+        id: instance.Video.id,
+        name: instance.Video.name,
+        uuid: instance.Video.uuid
+      }
+    })
+
+    for (const abuse of instance.CommentAbuses) {
+      abuse.deletedComment = details
+
+      tasks.push(abuse.save({ transaction: options.transaction }))
+    }
+
+    Promise.all(tasks)
+           .catch(err => {
+             logger.error('Some errors when saving details of comment %s in its abuses before destroy hook.', instance.url, { err })
+           })
+
+    return undefined
+  }
+
   static loadById (id: number, t?: Transaction): Bluebird<MComment> {
     const query: FindOptions = {
       where: {
index 272bba0e1c6ee08aa187a457765d47521c966cdf..43609587cf6f77a233e4c5af22d1cd18eccfe9a0 100644 (file)
@@ -803,14 +803,14 @@ export class VideoModel extends Model<VideoModel> {
   static async saveEssentialDataToAbuses (instance: VideoModel, options) {
     const tasks: Promise<any>[] = []
 
-    logger.info('Saving video abuses details of video %s.', instance.url)
-
     if (!Array.isArray(instance.VideoAbuses)) {
       instance.VideoAbuses = await instance.$get('VideoAbuses')
 
       if (instance.VideoAbuses.length === 0) return undefined
     }
 
+    logger.info('Saving video abuses details of video %s.', instance.url)
+
     const details = instance.toFormattedDetailsJSON()
 
     for (const abuse of instance.VideoAbuses) {
diff --git a/server/tests/api/check-params/abuses.ts b/server/tests/api/check-params/abuses.ts
new file mode 100644 (file)
index 0000000..ba7c083
--- /dev/null
@@ -0,0 +1,271 @@
+/* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
+
+import 'mocha'
+import { AbuseCreate, AbuseState } from '@shared/models'
+import {
+  cleanupTests,
+  createUser,
+  deleteAbuse,
+  flushAndRunServer,
+  makeGetRequest,
+  makePostBodyRequest,
+  ServerInfo,
+  setAccessTokensToServers,
+  updateAbuse,
+  uploadVideo,
+  userLogin
+} from '../../../../shared/extra-utils'
+import {
+  checkBadCountPagination,
+  checkBadSortPagination,
+  checkBadStartPagination
+} from '../../../../shared/extra-utils/requests/check-api-params'
+
+// FIXME: deprecated in 2.3. Remove this controller
+
+describe('Test video abuses API validators', function () {
+  const basePath = '/api/v1/abuses/'
+
+  let server: ServerInfo
+  let userAccessToken = ''
+  let abuseId: number
+
+  // ---------------------------------------------------------------
+
+  before(async function () {
+    this.timeout(30000)
+
+    server = await flushAndRunServer(1)
+
+    await setAccessTokensToServers([ server ])
+
+    const username = 'user1'
+    const password = 'my super password'
+    await createUser({ url: server.url, accessToken: server.accessToken, username: username, password: password })
+    userAccessToken = await userLogin(server, { username, password })
+
+    const res = await uploadVideo(server.url, server.accessToken, {})
+    server.video = res.body.video
+  })
+
+  describe('When listing abuses', function () {
+    const path = basePath
+
+    it('Should fail with a bad start pagination', async function () {
+      await checkBadStartPagination(server.url, path, server.accessToken)
+    })
+
+    it('Should fail with a bad count pagination', async function () {
+      await checkBadCountPagination(server.url, path, server.accessToken)
+    })
+
+    it('Should fail with an incorrect sort', async function () {
+      await checkBadSortPagination(server.url, path, server.accessToken)
+    })
+
+    it('Should fail with a non authenticated user', async function () {
+      await makeGetRequest({
+        url: server.url,
+        path,
+        statusCodeExpected: 401
+      })
+    })
+
+    it('Should fail with a non admin user', async function () {
+      await makeGetRequest({
+        url: server.url,
+        path,
+        token: userAccessToken,
+        statusCodeExpected: 403
+      })
+    })
+
+    it('Should fail with a bad id filter', async function () {
+      await makeGetRequest({ url: server.url, path, token: server.accessToken, query: { id: 'toto' } })
+    })
+
+    it('Should fail with a bad filter', async function () {
+      await makeGetRequest({ url: server.url, path, token: server.accessToken, query: { filter: 'toto' } })
+      await makeGetRequest({ url: server.url, path, token: server.accessToken, query: { filter: 'videos' } })
+    })
+
+    it('Should fail with bad predefined reason', async function () {
+      await makeGetRequest({ url: server.url, path, token: server.accessToken, query: { predefinedReason: 'violentOrRepulsives' } })
+    })
+
+    it('Should fail with a bad state filter', async function () {
+      await makeGetRequest({ url: server.url, path, token: server.accessToken, query: { state: 'toto' } })
+      await makeGetRequest({ url: server.url, path, token: server.accessToken, query: { state: 0 } })
+    })
+
+    it('Should fail with a bad videoIs filter', async function () {
+      await makeGetRequest({ url: server.url, path, token: server.accessToken, query: { videoIs: 'toto' } })
+    })
+
+    it('Should succeed with the correct params', async function () {
+      const query = {
+        id: 13,
+        predefinedReason: 'violentOrRepulsive',
+        filter: 'comment',
+        state: 2,
+        videoIs: 'deleted'
+      }
+
+      await makeGetRequest({ url: server.url, path, token: server.accessToken, query, statusCodeExpected: 200 })
+    })
+  })
+
+  describe('When reporting an abuse', function () {
+    const path = basePath
+
+    it('Should fail with nothing', async function () {
+      const fields = {}
+      await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields })
+    })
+
+    it('Should fail with a wrong video', async function () {
+      const fields = { video: { id: 'blabla' }, reason: 'my super reason' }
+      await makePostBodyRequest({ url: server.url, path: path, token: server.accessToken, fields })
+    })
+
+    it('Should fail with an unknown video', async function () {
+      const fields = { video: { id: 42 }, reason: 'my super reason' }
+      await makePostBodyRequest({ url: server.url, path: path, token: server.accessToken, fields, statusCodeExpected: 404 })
+    })
+
+    it('Should fail with a wrong comment', async function () {
+      const fields = { comment: { id: 'blabla' }, reason: 'my super reason' }
+      await makePostBodyRequest({ url: server.url, path: path, token: server.accessToken, fields })
+    })
+
+    it('Should fail with an unknown comment', async function () {
+      const fields = { comment: { id: 42 }, reason: 'my super reason' }
+      await makePostBodyRequest({ url: server.url, path: path, token: server.accessToken, fields, statusCodeExpected: 404 })
+    })
+
+    it('Should fail with a wrong account', async function () {
+      const fields = { account: { id: 'blabla' }, reason: 'my super reason' }
+      await makePostBodyRequest({ url: server.url, path: path, token: server.accessToken, fields })
+    })
+
+    it('Should fail with an unknown account', async function () {
+      const fields = { account: { id: 42 }, reason: 'my super reason' }
+      await makePostBodyRequest({ url: server.url, path: path, token: server.accessToken, fields, statusCodeExpected: 404 })
+    })
+
+    it('Should fail with not account, comment or video', async function () {
+      const fields = { reason: 'my super reason' }
+      await makePostBodyRequest({ url: server.url, path: path, token: server.accessToken, fields, statusCodeExpected: 400 })
+    })
+
+    it('Should fail with a non authenticated user', async function () {
+      const fields = { video: { id: server.video.id }, reason: 'my super reason' }
+
+      await makePostBodyRequest({ url: server.url, path, token: 'hello', fields, statusCodeExpected: 401 })
+    })
+
+    it('Should fail with a reason too short', async function () {
+      const fields = { video: { id: server.video.id }, reason: 'h' }
+
+      await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields })
+    })
+
+    it('Should fail with a too big reason', async function () {
+      const fields = { video: { id: server.video.id }, reason: 'super'.repeat(605) }
+
+      await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields })
+    })
+
+    it('Should succeed with the correct parameters (basic)', async function () {
+      const fields: AbuseCreate = { video: { id: server.video.id }, reason: 'my super reason' }
+
+      const res = await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields, statusCodeExpected: 200 })
+      abuseId = res.body.abuse.id
+    })
+
+    it('Should fail with a wrong predefined reason', async function () {
+      const fields = { video: { id: server.video.id }, reason: 'my super reason', predefinedReasons: [ 'wrongPredefinedReason' ] }
+
+      await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields })
+    })
+
+    it('Should fail with negative timestamps', async function () {
+      const fields = { video: { id: server.video.id, startAt: -1 }, reason: 'my super reason' }
+
+      await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields })
+    })
+
+    it('Should fail mith misordered startAt/endAt', async function () {
+      const fields = { video: { id: server.video.id, startAt: 5, endAt: 1 }, reason: 'my super reason' }
+
+      await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields })
+    })
+
+    it('Should succeed with the corret parameters (advanced)', async function () {
+      const fields: AbuseCreate = {
+        video: {
+          id: server.video.id,
+          startAt: 1,
+          endAt: 5
+        },
+        reason: 'my super reason',
+        predefinedReasons: [ 'serverRules' ]
+      }
+
+      await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields, statusCodeExpected: 200 })
+    })
+  })
+
+  describe('When updating an abuse', function () {
+
+    it('Should fail with a non authenticated user', async function () {
+      await updateAbuse(server.url, 'blabla', abuseId, {}, 401)
+    })
+
+    it('Should fail with a non admin user', async function () {
+      await updateAbuse(server.url, userAccessToken, abuseId, {}, 403)
+    })
+
+    it('Should fail with a bad abuse id', async function () {
+      await updateAbuse(server.url, server.accessToken, 45, {}, 404)
+    })
+
+    it('Should fail with a bad state', async function () {
+      const body = { state: 5 }
+      await updateAbuse(server.url, server.accessToken, abuseId, body, 400)
+    })
+
+    it('Should fail with a bad moderation comment', async function () {
+      const body = { moderationComment: 'b'.repeat(3001) }
+      await updateAbuse(server.url, server.accessToken, abuseId, body, 400)
+    })
+
+    it('Should succeed with the correct params', async function () {
+      const body = { state: AbuseState.ACCEPTED }
+      await updateAbuse(server.url, server.accessToken, abuseId, body)
+    })
+  })
+
+  describe('When deleting a video abuse', function () {
+
+    it('Should fail with a non authenticated user', async function () {
+      await deleteAbuse(server.url, 'blabla', abuseId, 401)
+    })
+
+    it('Should fail with a non admin user', async function () {
+      await deleteAbuse(server.url, userAccessToken, abuseId, 403)
+    })
+
+    it('Should fail with a bad abuse id', async function () {
+      await deleteAbuse(server.url, server.accessToken, 45, 404)
+    })
+
+    it('Should succeed with the correct params', async function () {
+      await deleteAbuse(server.url, server.accessToken, abuseId)
+    })
+  })
+
+  after(async function () {
+    await cleanupTests([ server ])
+  })
+})
index 93ffd98b199c90d5093e035fc230fb38459e7a8b..0ee1f27aaa574362e8bc440c557bca44496c2a26 100644 (file)
@@ -1,3 +1,4 @@
+import './abuses'
 import './accounts'
 import './blocklist'
 import './bulk'
index f122baef4417c3d953be37e44ae40287344aef86..3b361ca79eb08f2216f78d3992b72b14f7c1d7ce 100644 (file)
@@ -152,12 +152,6 @@ describe('Test video abuses API validators', function () {
       await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields })
     })
 
-    it('Should fail mith misordered startAt/endAt', async function () {
-      const fields = { reason: 'my super reason', startAt: 5, endAt: 1 }
-
-      await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields })
-    })
-
     it('Should succeed with the corret parameters (advanced)', async function () {
       const fields: VideoAbuseCreate = { reason: 'my super reason', predefinedReasons: [ 'serverRules' ], startAt: 1, endAt: 5 }
 
index abbc93d6fba1c1f50bdd9626ba60e3d5ae6feac0..8e12be87470dc6cfba0b42f51a823150bcb1c9e1 100644 (file)
@@ -3,7 +3,7 @@ import { VideoCommentAbuseModel } from '@server/models/abuse/video-comment-abuse
 import { PickWith } from '@shared/core-utils'
 import { AbuseModel } from '../../../models/abuse/abuse'
 import { MAccountDefault, MAccountFormattable, MAccountLight, MAccountUrl } from '../account'
-import { MCommentOwner, MCommentUrl, MVideoUrl, MCommentOwnerVideo } from '../video'
+import { MCommentOwner, MCommentUrl, MVideoUrl, MCommentOwnerVideo, MComment, MCommentVideo } from '../video'
 import { MVideo, MVideoAccountLightBlacklistAllFiles } from '../video/video'
 
 type Use<K extends keyof AbuseModel, M> = PickWith<AbuseModel, K, M>
@@ -51,6 +51,10 @@ export type MCommentAbuseUrl =
   MCommentAbuse &
   UseCommentAbuse<'VideoComment', MCommentUrl>
 
+export type MCommentAbuseFormattable =
+  MCommentAbuse &
+  UseCommentAbuse<'VideoComment', MComment & PickWith<MCommentVideo, 'Video', Pick<MVideo, 'id' | 'uuid' | 'name'>>>
+
 // ############################################################################
 
 export type MAbuseId = Pick<AbuseModel, 'id'>
@@ -94,4 +98,5 @@ export type MAbuseFull =
 export type MAbuseFormattable =
   MAbuse &
   Use<'ReporterAccount', MAccountFormattable> &
-  Use<'VideoAbuse', MVideoAbuseFormattable>
+  Use<'VideoAbuse', MVideoAbuseFormattable> &
+  Use<'VideoCommentAbuse', MCommentAbuseFormattable>
index 7595e6d86c22ceaafd3bef731f3fd74e1dcc7788..b1afffcd4c4bb90a792a1c83f06ea28c86cd3081 100644 (file)
@@ -91,6 +91,7 @@ declare module 'express' {
 
       accountVideoRate?: MAccountVideoRateAccountVideo
 
+      videoComment?: MComment
       videoCommentFull?: MCommentOwnerVideoReply
       videoCommentThread?: MComment
 
index 48a51e2b8580910d5e0d47b33a2cfb5448be3155..1af703f923198f34292c972344d35aca778faf49 100644 (file)
@@ -1,25 +1,57 @@
-import * as request from 'supertest'
-import { AbusePredefinedReasonsString, AbuseState, AbuseUpdate, AbuseVideoIs } from '@shared/models'
-import { makeDeleteRequest, makeGetRequest, makePutBodyRequest } from '../requests/requests'
 
-function reportAbuse (
-  url: string,
-  token: string,
-  videoId: number | string,
-  reason: string,
-  predefinedReasons?: AbusePredefinedReasonsString[],
-  startAt?: number,
-  endAt?: number,
-  specialStatus = 200
-) {
-  const path = '/api/v1/videos/' + videoId + '/abuse'
-
-  return request(url)
-          .post(path)
-          .set('Accept', 'application/json')
-          .set('Authorization', 'Bearer ' + token)
-          .send({ reason, predefinedReasons, startAt, endAt })
-          .expect(specialStatus)
+import { AbuseFilter, AbusePredefinedReasonsString, AbuseState, AbuseUpdate, AbuseVideoIs } from '@shared/models'
+import { makeDeleteRequest, makeGetRequest, makePostBodyRequest, makePutBodyRequest } from '../requests/requests'
+
+function reportAbuse (options: {
+  url: string
+  token: string
+
+  reason: string
+
+  accountId?: number
+  videoId?: number
+  commentId?: number
+
+  predefinedReasons?: AbusePredefinedReasonsString[]
+
+  startAt?: number
+  endAt?: number
+
+  statusCodeExpected?: number
+}) {
+  const path = '/api/v1/abuses'
+
+  const video = options.videoId ? {
+    id: options.videoId,
+    startAt: options.startAt,
+    endAt: options.endAt
+  } : undefined
+
+  const comment = options.commentId ? {
+    id: options.commentId
+  } : undefined
+
+  const account = options.accountId ? {
+    id: options.accountId
+  } : undefined
+
+  const body = {
+    account,
+    video,
+    comment,
+
+    reason: options.reason,
+    predefinedReasons: options.predefinedReasons
+  }
+
+  return makePostBodyRequest({
+    url: options.url,
+    path,
+    token: options.token,
+
+    fields: body,
+    statusCodeExpected: options.statusCodeExpected || 200
+  })
 }
 
 function getAbusesList (options: {
@@ -28,6 +60,7 @@ function getAbusesList (options: {
   id?: number
   predefinedReason?: AbusePredefinedReasonsString
   search?: string
+  filter?: AbuseFilter,
   state?: AbuseState
   videoIs?: AbuseVideoIs
   searchReporter?: string
@@ -41,6 +74,7 @@ function getAbusesList (options: {
     id,
     predefinedReason,
     search,
+    filter,
     state,
     videoIs,
     searchReporter,
@@ -48,7 +82,7 @@ function getAbusesList (options: {
     searchVideo,
     searchVideoChannel
   } = options
-  const path = '/api/v1/videos/abuse'
+  const path = '/api/v1/abuses'
 
   const query = {
     sort: 'createdAt',
@@ -56,6 +90,7 @@ function getAbusesList (options: {
     predefinedReason,
     search,
     state,
+    filter,
     videoIs,
     searchReporter,
     searchReportee,
@@ -75,12 +110,11 @@ function getAbusesList (options: {
 function updateAbuse (
   url: string,
   token: string,
-  videoId: string | number,
-  videoAbuseId: number,
+  abuseId: number,
   body: AbuseUpdate,
   statusCodeExpected = 204
 ) {
-  const path = '/api/v1/videos/' + videoId + '/abuse/' + videoAbuseId
+  const path = '/api/v1/abuses/' + abuseId
 
   return makePutBodyRequest({
     url,
@@ -91,8 +125,8 @@ function updateAbuse (
   })
 }
 
-function deleteAbuse (url: string, token: string, videoId: string | number, videoAbuseId: number, statusCodeExpected = 204) {
-  const path = '/api/v1/videos/' + videoId + '/abuse/' + videoAbuseId
+function deleteAbuse (url: string, token: string, abuseId: number, statusCodeExpected = 204) {
+  const path = '/api/v1/abuses/' + abuseId
 
   return makeDeleteRequest({
     url,
index c0d04e46d5d8da9b73d15bbc193cd280bcd11dcb..b0358dbb9af1bc867770bcd891596b0c97d57332 100644 (file)
@@ -1,11 +1,14 @@
 import { AbusePredefinedReasonsString } from './abuse-reason.model'
 
 export interface AbuseCreate {
-  accountId: number
-
   reason: string
+
   predefinedReasons?: AbusePredefinedReasonsString[]
 
+  account?: {
+    id: number
+  }
+
   video?: {
     id: number
     startAt?: number
diff --git a/shared/models/moderation/abuse/abuse-filter.ts b/shared/models/moderation/abuse/abuse-filter.ts
deleted file mode 100644 (file)
index 03303bb..0000000
+++ /dev/null
@@ -1 +0,0 @@
-export type AbuseFilter = 'video' | 'comment'
diff --git a/shared/models/moderation/abuse/abuse-filter.type.ts b/shared/models/moderation/abuse/abuse-filter.type.ts
new file mode 100644 (file)
index 0000000..7dafc6d
--- /dev/null
@@ -0,0 +1 @@
+export type AbuseFilter = 'video' | 'comment' | 'account'
index 9ff150c4aaca8ae2a0d79d9350f383f1b3cfa31c..a120803e67966786241ba2ac1fef06edbd684dc7 100644 (file)
@@ -9,6 +9,7 @@ export interface VideoAbuse {
   name: string
   uuid: string
   nsfw: boolean
+
   deleted: boolean
   blacklisted: boolean
 
@@ -21,8 +22,15 @@ export interface VideoAbuse {
 
 export interface VideoCommentAbuse {
   id: number
-  account?: Account
+
+  video: {
+    id: number
+    name: string
+    uuid: string
+  }
+
   text: string
+
   deleted: boolean
 }
 
index 32a6b4e6c77bba2d709f04d833ebda029ad9f033..55046426ab3ffaa3547f2b4863a5bedb4aef20d9 100644 (file)
@@ -1,4 +1,5 @@
 export * from './abuse-create.model'
+export * from './abuse-filter.type'
 export * from './abuse-reason.model'
 export * from './abuse-state.model'
 export * from './abuse-update.model'