Implement abuses check params
This commit is contained in:
parent
d95d155988
commit
57f6896f67
|
@ -23,7 +23,7 @@ import { AccountModel } from '../../models/account/account'
|
||||||
|
|
||||||
const abuseRouter = express.Router()
|
const abuseRouter = express.Router()
|
||||||
|
|
||||||
abuseRouter.get('/abuse',
|
abuseRouter.get('/',
|
||||||
authenticate,
|
authenticate,
|
||||||
ensureUserHasRight(UserRight.MANAGE_ABUSES),
|
ensureUserHasRight(UserRight.MANAGE_ABUSES),
|
||||||
paginationValidator,
|
paginationValidator,
|
||||||
|
@ -33,18 +33,18 @@ abuseRouter.get('/abuse',
|
||||||
abuseListValidator,
|
abuseListValidator,
|
||||||
asyncMiddleware(listAbuses)
|
asyncMiddleware(listAbuses)
|
||||||
)
|
)
|
||||||
abuseRouter.put('/:videoId/abuse/:id',
|
abuseRouter.put('/:id',
|
||||||
authenticate,
|
authenticate,
|
||||||
ensureUserHasRight(UserRight.MANAGE_ABUSES),
|
ensureUserHasRight(UserRight.MANAGE_ABUSES),
|
||||||
asyncMiddleware(abuseUpdateValidator),
|
asyncMiddleware(abuseUpdateValidator),
|
||||||
asyncRetryTransactionMiddleware(updateAbuse)
|
asyncRetryTransactionMiddleware(updateAbuse)
|
||||||
)
|
)
|
||||||
abuseRouter.post('/:videoId/abuse',
|
abuseRouter.post('/',
|
||||||
authenticate,
|
authenticate,
|
||||||
asyncMiddleware(abuseReportValidator),
|
asyncMiddleware(abuseReportValidator),
|
||||||
asyncRetryTransactionMiddleware(reportAbuse)
|
asyncRetryTransactionMiddleware(reportAbuse)
|
||||||
)
|
)
|
||||||
abuseRouter.delete('/:videoId/abuse/:id',
|
abuseRouter.delete('/:id',
|
||||||
authenticate,
|
authenticate,
|
||||||
ensureUserHasRight(UserRight.MANAGE_ABUSES),
|
ensureUserHasRight(UserRight.MANAGE_ABUSES),
|
||||||
asyncMiddleware(abuseGetValidator),
|
asyncMiddleware(abuseGetValidator),
|
||||||
|
@ -74,7 +74,7 @@ async function listAbuses (req: express.Request, res: express.Response) {
|
||||||
count: req.query.count,
|
count: req.query.count,
|
||||||
sort: req.query.sort,
|
sort: req.query.sort,
|
||||||
id: req.query.id,
|
id: req.query.id,
|
||||||
filter: 'video',
|
filter: req.query.filter,
|
||||||
predefinedReason: req.query.predefinedReason,
|
predefinedReason: req.query.predefinedReason,
|
||||||
search: req.query.search,
|
search: req.query.search,
|
||||||
state: req.query.state,
|
state: req.query.state,
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import validator from 'validator'
|
import validator from 'validator'
|
||||||
import { abusePredefinedReasonsMap, AbusePredefinedReasonsString, AbuseVideoIs } from '@shared/models'
|
import { AbuseFilter, abusePredefinedReasonsMap, AbusePredefinedReasonsString, AbuseVideoIs, AbuseCreate } from '@shared/models'
|
||||||
import { CONSTRAINTS_FIELDS, ABUSE_STATES } from '../../initializers/constants'
|
import { ABUSE_STATES, CONSTRAINTS_FIELDS } from '../../initializers/constants'
|
||||||
import { exists, isArray } from './misc'
|
import { exists, isArray } from './misc'
|
||||||
|
|
||||||
const VIDEO_ABUSES_CONSTRAINTS_FIELDS = CONSTRAINTS_FIELDS.ABUSES
|
const VIDEO_ABUSES_CONSTRAINTS_FIELDS = CONSTRAINTS_FIELDS.ABUSES
|
||||||
|
@ -13,7 +13,11 @@ function isAbusePredefinedReasonValid (value: AbusePredefinedReasonsString) {
|
||||||
return exists(value) && value in abusePredefinedReasonsMap
|
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)
|
return exists(value) && isArray(value) && value.every(v => v in abusePredefinedReasonsMap)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,7 +26,9 @@ function isAbuseTimestampValid (value: number) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function isAbuseTimestampCoherent (endAt: number, { req }) {
|
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) {
|
function isAbuseModerationCommentValid (value: string) {
|
||||||
|
@ -44,8 +50,9 @@ function isAbuseVideoIsValid (value: AbuseVideoIs) {
|
||||||
|
|
||||||
export {
|
export {
|
||||||
isAbuseReasonValid,
|
isAbuseReasonValid,
|
||||||
|
isAbuseFilterValid,
|
||||||
isAbusePredefinedReasonValid,
|
isAbusePredefinedReasonValid,
|
||||||
isAbusePredefinedReasonsValid,
|
areAbusePredefinedReasonsValid as isAbusePredefinedReasonsValid,
|
||||||
isAbuseTimestampValid,
|
isAbuseTimestampValid,
|
||||||
isAbuseTimestampCoherent,
|
isAbuseTimestampCoherent,
|
||||||
isAbuseModerationCommentValid,
|
isAbuseModerationCommentValid,
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
import 'multer'
|
import * as express from 'express'
|
||||||
import validator from 'validator'
|
import validator from 'validator'
|
||||||
|
import { VideoCommentModel } from '@server/models/video/video-comment'
|
||||||
import { CONSTRAINTS_FIELDS } from '../../initializers/constants'
|
import { CONSTRAINTS_FIELDS } from '../../initializers/constants'
|
||||||
|
import { MVideoId } from '@server/types/models'
|
||||||
|
|
||||||
const VIDEO_COMMENTS_CONSTRAINTS_FIELDS = CONSTRAINTS_FIELDS.VIDEO_COMMENTS
|
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)
|
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 {
|
export {
|
||||||
isValidVideoCommentText
|
isValidVideoCommentText,
|
||||||
|
doesVideoCommentThreadExist,
|
||||||
|
doesVideoCommentExist,
|
||||||
|
doesCommentIdExist
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,6 @@ async function doesVideoAbuseExist (abuseIdArg: number | string, videoUUID: stri
|
||||||
if (abuse === null) {
|
if (abuse === null) {
|
||||||
res.status(404)
|
res.status(404)
|
||||||
.json({ error: 'Video abuse not found' })
|
.json({ error: 'Video abuse not found' })
|
||||||
.end()
|
|
||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
@ -26,8 +25,18 @@ async function doesVideoAbuseExist (abuseIdArg: number | string, videoUUID: stri
|
||||||
return true
|
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
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
|
@ -3,8 +3,8 @@ import { AccountModel } from '../../models/account/account'
|
||||||
import * as Bluebird from 'bluebird'
|
import * as Bluebird from 'bluebird'
|
||||||
import { MAccountDefault } from '../../types/models'
|
import { MAccountDefault } from '../../types/models'
|
||||||
|
|
||||||
function doesAccountIdExist (id: number, res: Response, sendNotFound = true) {
|
function doesAccountIdExist (id: number | string, res: Response, sendNotFound = true) {
|
||||||
const promise = AccountModel.load(id)
|
const promise = AccountModel.load(parseInt(id + '', 10))
|
||||||
|
|
||||||
return doesAccountExist(promise, res, sendNotFound)
|
return doesAccountExist(promise, res, sendNotFound)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import * as express from 'express'
|
import * as express from 'express'
|
||||||
import { body, param, query } from 'express-validator'
|
import { body, param, query } from 'express-validator'
|
||||||
import {
|
import {
|
||||||
|
isAbuseFilterValid,
|
||||||
isAbuseModerationCommentValid,
|
isAbuseModerationCommentValid,
|
||||||
isAbusePredefinedReasonsValid,
|
isAbusePredefinedReasonsValid,
|
||||||
isAbusePredefinedReasonValid,
|
isAbusePredefinedReasonValid,
|
||||||
|
@ -11,29 +12,28 @@ import {
|
||||||
isAbuseVideoIsValid
|
isAbuseVideoIsValid
|
||||||
} from '@server/helpers/custom-validators/abuses'
|
} from '@server/helpers/custom-validators/abuses'
|
||||||
import { exists, isIdOrUUIDValid, isIdValid, toIntOrNull } from '@server/helpers/custom-validators/misc'
|
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 { 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'
|
import { areValidationErrors } from './utils'
|
||||||
|
|
||||||
const abuseReportValidator = [
|
const abuseReportValidator = [
|
||||||
param('videoId')
|
body('account.id')
|
||||||
.custom(isIdOrUUIDValid)
|
|
||||||
.not()
|
|
||||||
.isEmpty()
|
|
||||||
.withMessage('Should have a valid videoId'),
|
|
||||||
body('reason')
|
|
||||||
.custom(isAbuseReasonValid)
|
|
||||||
.withMessage('Should have a valid reason'),
|
|
||||||
body('predefinedReasons')
|
|
||||||
.optional()
|
.optional()
|
||||||
.custom(isAbusePredefinedReasonsValid)
|
.custom(isIdValid)
|
||||||
.withMessage('Should have a valid list of predefined reasons'),
|
.withMessage('Should have a valid accountId'),
|
||||||
body('startAt')
|
|
||||||
|
body('video.id')
|
||||||
|
.optional()
|
||||||
|
.custom(isIdOrUUIDValid)
|
||||||
|
.withMessage('Should have a valid videoId'),
|
||||||
|
body('video.startAt')
|
||||||
.optional()
|
.optional()
|
||||||
.customSanitizer(toIntOrNull)
|
.customSanitizer(toIntOrNull)
|
||||||
.custom(isAbuseTimestampValid)
|
.custom(isAbuseTimestampValid)
|
||||||
.withMessage('Should have valid starting time value'),
|
.withMessage('Should have valid starting time value'),
|
||||||
body('endAt')
|
body('video.endAt')
|
||||||
.optional()
|
.optional()
|
||||||
.customSanitizer(toIntOrNull)
|
.customSanitizer(toIntOrNull)
|
||||||
.custom(isAbuseTimestampValid)
|
.custom(isAbuseTimestampValid)
|
||||||
|
@ -42,47 +42,70 @@ const abuseReportValidator = [
|
||||||
.custom(isAbuseTimestampCoherent)
|
.custom(isAbuseTimestampCoherent)
|
||||||
.withMessage('Should have a startAt timestamp beginning before endAt'),
|
.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) => {
|
async (req: express.Request, res: express.Response, next: express.NextFunction) => {
|
||||||
logger.debug('Checking abuseReport parameters', { parameters: req.body })
|
logger.debug('Checking abuseReport parameters', { parameters: req.body })
|
||||||
|
|
||||||
if (areValidationErrors(req, res)) return
|
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()
|
return next()
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
const abuseGetValidator = [
|
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'),
|
param('id').custom(isIdValid).not().isEmpty().withMessage('Should have a valid id'),
|
||||||
|
|
||||||
async (req: express.Request, res: express.Response, next: express.NextFunction) => {
|
async (req: express.Request, res: express.Response, next: express.NextFunction) => {
|
||||||
logger.debug('Checking abuseGetValidator parameters', { parameters: req.body })
|
logger.debug('Checking abuseGetValidator parameters', { parameters: req.body })
|
||||||
|
|
||||||
if (areValidationErrors(req, res)) return
|
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()
|
return next()
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
const abuseUpdateValidator = [
|
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'),
|
param('id').custom(isIdValid).not().isEmpty().withMessage('Should have a valid id'),
|
||||||
|
|
||||||
body('state')
|
body('state')
|
||||||
.optional()
|
.optional()
|
||||||
.custom(isAbuseStateValid).withMessage('Should have a valid video abuse state'),
|
.custom(isAbuseStateValid).withMessage('Should have a valid abuse state'),
|
||||||
body('moderationComment')
|
body('moderationComment')
|
||||||
.optional()
|
.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) => {
|
async (req: express.Request, res: express.Response, next: express.NextFunction) => {
|
||||||
logger.debug('Checking abuseUpdateValidator parameters', { parameters: req.body })
|
logger.debug('Checking abuseUpdateValidator parameters', { parameters: req.body })
|
||||||
|
|
||||||
if (areValidationErrors(req, res)) return
|
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()
|
return next()
|
||||||
}
|
}
|
||||||
|
@ -92,6 +115,10 @@ const abuseListValidator = [
|
||||||
query('id')
|
query('id')
|
||||||
.optional()
|
.optional()
|
||||||
.custom(isIdValid).withMessage('Should have a valid id'),
|
.custom(isIdValid).withMessage('Should have a valid id'),
|
||||||
|
query('filter')
|
||||||
|
.optional()
|
||||||
|
.custom(isAbuseFilterValid)
|
||||||
|
.withMessage('Should have a valid filter'),
|
||||||
query('predefinedReason')
|
query('predefinedReason')
|
||||||
.optional()
|
.optional()
|
||||||
.custom(isAbusePredefinedReasonValid)
|
.custom(isAbusePredefinedReasonValid)
|
||||||
|
@ -151,10 +178,7 @@ const videoAbuseReportValidator = [
|
||||||
.optional()
|
.optional()
|
||||||
.customSanitizer(toIntOrNull)
|
.customSanitizer(toIntOrNull)
|
||||||
.custom(isAbuseTimestampValid)
|
.custom(isAbuseTimestampValid)
|
||||||
.withMessage('Should have valid ending time value')
|
.withMessage('Should have valid ending time value'),
|
||||||
.bail()
|
|
||||||
.custom(isAbuseTimestampCoherent)
|
|
||||||
.withMessage('Should have a startAt timestamp beginning before endAt'),
|
|
||||||
|
|
||||||
async (req: express.Request, res: express.Response, next: express.NextFunction) => {
|
async (req: express.Request, res: express.Response, next: express.NextFunction) => {
|
||||||
logger.debug('Checking videoAbuseReport parameters', { parameters: req.body })
|
logger.debug('Checking videoAbuseReport parameters', { parameters: req.body })
|
||||||
|
|
|
@ -3,13 +3,16 @@ import { body, param } from 'express-validator'
|
||||||
import { MUserAccountUrl } from '@server/types/models'
|
import { MUserAccountUrl } from '@server/types/models'
|
||||||
import { UserRight } from '../../../../shared'
|
import { UserRight } from '../../../../shared'
|
||||||
import { isIdOrUUIDValid, isIdValid } from '../../../helpers/custom-validators/misc'
|
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 { logger } from '../../../helpers/logger'
|
||||||
import { doesVideoExist } from '../../../helpers/middlewares'
|
import { doesVideoExist } from '../../../helpers/middlewares'
|
||||||
import { AcceptResult, isLocalVideoCommentReplyAccepted, isLocalVideoThreadAccepted } from '../../../lib/moderation'
|
import { AcceptResult, isLocalVideoCommentReplyAccepted, isLocalVideoThreadAccepted } from '../../../lib/moderation'
|
||||||
import { Hooks } from '../../../lib/plugins/hooks'
|
import { Hooks } from '../../../lib/plugins/hooks'
|
||||||
import { VideoCommentModel } from '../../../models/video/video-comment'
|
import { MCommentOwnerVideoReply, MVideo, MVideoFullLight } from '../../../types/models/video'
|
||||||
import { MCommentOwnerVideoReply, MVideo, MVideoFullLight, MVideoId } from '../../../types/models/video'
|
|
||||||
import { areValidationErrors } from '../utils'
|
import { areValidationErrors } from '../utils'
|
||||||
|
|
||||||
const listVideoCommentThreadsValidator = [
|
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) {
|
function isVideoCommentsEnabled (video: MVideo, res: express.Response) {
|
||||||
if (video.commentsEnabled !== true) {
|
if (video.commentsEnabled !== true) {
|
||||||
res.status(409)
|
res.status(409)
|
||||||
.json({ error: 'Video comments are disabled for this video.' })
|
.json({ error: 'Video comments are disabled for this video.' })
|
||||||
.end()
|
|
||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
@ -192,7 +138,7 @@ function checkUserCanDeleteVideoComment (user: MUserAccountUrl, videoComment: MC
|
||||||
if (videoComment.isDeleted()) {
|
if (videoComment.isDeleted()) {
|
||||||
res.status(409)
|
res.status(409)
|
||||||
.json({ error: 'This comment is already deleted' })
|
.json({ error: 'This comment is already deleted' })
|
||||||
.end()
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,16 +19,17 @@ import {
|
||||||
import { isAbuseModerationCommentValid, isAbuseReasonValid, isAbuseStateValid } from '@server/helpers/custom-validators/abuses'
|
import { isAbuseModerationCommentValid, isAbuseReasonValid, isAbuseStateValid } from '@server/helpers/custom-validators/abuses'
|
||||||
import {
|
import {
|
||||||
Abuse,
|
Abuse,
|
||||||
|
AbuseFilter,
|
||||||
AbuseObject,
|
AbuseObject,
|
||||||
AbusePredefinedReasons,
|
AbusePredefinedReasons,
|
||||||
abusePredefinedReasonsMap,
|
abusePredefinedReasonsMap,
|
||||||
AbusePredefinedReasonsString,
|
AbusePredefinedReasonsString,
|
||||||
AbuseState,
|
AbuseState,
|
||||||
AbuseVideoIs,
|
AbuseVideoIs,
|
||||||
VideoAbuse
|
VideoAbuse,
|
||||||
|
VideoCommentAbuse
|
||||||
} from '@shared/models'
|
} from '@shared/models'
|
||||||
import { AbuseFilter } from '@shared/models/moderation/abuse/abuse-filter'
|
import { ABUSE_STATES, CONSTRAINTS_FIELDS } from '../../initializers/constants'
|
||||||
import { CONSTRAINTS_FIELDS, ABUSE_STATES } from '../../initializers/constants'
|
|
||||||
import { MAbuse, MAbuseAP, MAbuseFormattable, MUserAccountId } from '../../types/models'
|
import { MAbuse, MAbuseAP, MAbuseFormattable, MUserAccountId } from '../../types/models'
|
||||||
import { AccountModel, ScopeNames as AccountScopeNames } from '../account/account'
|
import { AccountModel, ScopeNames as AccountScopeNames } from '../account/account'
|
||||||
import { buildBlockedAccountSQL, getSort, searchAttribute, throwIfNotValid } from '../utils'
|
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 { ScopeNames as VideoChannelScopeNames, SummaryOptions, VideoChannelModel } from '../video/video-channel'
|
||||||
import { VideoAbuseModel } from './video-abuse'
|
import { VideoAbuseModel } from './video-abuse'
|
||||||
import { VideoCommentAbuseModel } from './video-comment-abuse'
|
import { VideoCommentAbuseModel } from './video-comment-abuse'
|
||||||
|
import { VideoCommentModel } from '../video/video-comment'
|
||||||
|
|
||||||
export enum ScopeNames {
|
export enum ScopeNames {
|
||||||
FOR_API = 'FOR_API'
|
FOR_API = 'FOR_API'
|
||||||
|
@ -66,19 +68,18 @@ export enum ScopeNames {
|
||||||
serverAccountId: number
|
serverAccountId: number
|
||||||
userAccountId: number
|
userAccountId: number
|
||||||
}) => {
|
}) => {
|
||||||
const onlyBlacklisted = options.videoIs === 'blacklisted'
|
const whereAnd: WhereOptions[] = []
|
||||||
const videoRequired = !!(onlyBlacklisted || options.searchVideo || options.searchVideoChannel)
|
|
||||||
|
|
||||||
const where = {
|
whereAnd.push({
|
||||||
reporterAccountId: {
|
reporterAccountId: {
|
||||||
[Op.notIn]: literal('(' + buildBlockedAccountSQL([ options.serverAccountId, options.userAccountId ]) + ')')
|
[Op.notIn]: literal('(' + buildBlockedAccountSQL([ options.serverAccountId, options.userAccountId ]) + ')')
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
|
|
||||||
if (options.search) {
|
if (options.search) {
|
||||||
const escapedSearch = AbuseModel.sequelize.escape('%' + options.search + '%')
|
const escapedSearch = AbuseModel.sequelize.escape('%' + options.search + '%')
|
||||||
|
|
||||||
Object.assign(where, {
|
whereAnd.push({
|
||||||
[Op.or]: [
|
[Op.or]: [
|
||||||
{
|
{
|
||||||
[Op.and]: [
|
[Op.and]: [
|
||||||
|
@ -110,11 +111,11 @@ export enum ScopeNames {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options.id) Object.assign(where, { id: options.id })
|
if (options.id) whereAnd.push({ id: options.id })
|
||||||
if (options.state) Object.assign(where, { state: options.state })
|
if (options.state) whereAnd.push({ state: options.state })
|
||||||
|
|
||||||
if (options.videoIs === 'deleted') {
|
if (options.videoIs === 'deleted') {
|
||||||
Object.assign(where, {
|
whereAnd.push({
|
||||||
'$VideoAbuse.deletedVideo$': {
|
'$VideoAbuse.deletedVideo$': {
|
||||||
[Op.not]: null
|
[Op.not]: null
|
||||||
}
|
}
|
||||||
|
@ -122,13 +123,23 @@ export enum ScopeNames {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options.predefinedReasonId) {
|
if (options.predefinedReasonId) {
|
||||||
Object.assign(where, {
|
whereAnd.push({
|
||||||
predefinedReasons: {
|
predefinedReasons: {
|
||||||
[Op.contains]: [ options.predefinedReasonId ]
|
[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 {
|
return {
|
||||||
attributes: {
|
attributes: {
|
||||||
include: [
|
include: [
|
||||||
|
@ -222,6 +233,23 @@ export enum ScopeNames {
|
||||||
required: true,
|
required: true,
|
||||||
where: searchAttribute(options.searchReportee, 'name')
|
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,
|
model: VideoAbuseModel,
|
||||||
required: options.filter === 'video' || !!options.videoIs || videoRequired,
|
required: options.filter === 'video' || !!options.videoIs || videoRequired,
|
||||||
|
@ -241,8 +269,7 @@ export enum ScopeNames {
|
||||||
include: [
|
include: [
|
||||||
{
|
{
|
||||||
model: AccountModel.scope(AccountScopeNames.SUMMARY),
|
model: AccountModel.scope(AccountScopeNames.SUMMARY),
|
||||||
required: true,
|
required: true
|
||||||
where: searchAttribute(options.searchReportee, 'name')
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@ -256,7 +283,9 @@ export enum ScopeNames {
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
where
|
where: {
|
||||||
|
[Op.and]: whereAnd
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
|
@ -348,6 +377,7 @@ export class AbuseModel extends Model<AbuseModel> {
|
||||||
})
|
})
|
||||||
VideoAbuse: VideoAbuseModel
|
VideoAbuse: VideoAbuseModel
|
||||||
|
|
||||||
|
// FIXME: deprecated in 2.3. Remove these validators
|
||||||
static loadByIdAndVideoId (id: number, videoId?: number, uuid?: string): Bluebird<MAbuse> {
|
static loadByIdAndVideoId (id: number, videoId?: number, uuid?: string): Bluebird<MAbuse> {
|
||||||
const videoWhere: WhereOptions = {}
|
const videoWhere: WhereOptions = {}
|
||||||
|
|
||||||
|
@ -369,6 +399,16 @@ export class AbuseModel extends Model<AbuseModel> {
|
||||||
return AbuseModel.findOne(query)
|
return AbuseModel.findOne(query)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static loadById (id: number): Bluebird<MAbuse> {
|
||||||
|
const query = {
|
||||||
|
where: {
|
||||||
|
id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return AbuseModel.findOne(query)
|
||||||
|
}
|
||||||
|
|
||||||
static listForApi (parameters: {
|
static listForApi (parameters: {
|
||||||
start: number
|
start: number
|
||||||
count: number
|
count: number
|
||||||
|
@ -454,6 +494,7 @@ export class AbuseModel extends Model<AbuseModel> {
|
||||||
const countReportsForReporteeDeletedVideo = this.get('countReportsForReportee__deletedVideo') as number
|
const countReportsForReporteeDeletedVideo = this.get('countReportsForReportee__deletedVideo') as number
|
||||||
|
|
||||||
let video: VideoAbuse
|
let video: VideoAbuse
|
||||||
|
let comment: VideoCommentAbuse
|
||||||
|
|
||||||
if (this.VideoAbuse) {
|
if (this.VideoAbuse) {
|
||||||
const abuseModel = 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 {
|
return {
|
||||||
id: this.id,
|
id: this.id,
|
||||||
reason: this.reason,
|
reason: this.reason,
|
||||||
|
@ -490,7 +549,7 @@ export class AbuseModel extends Model<AbuseModel> {
|
||||||
moderationComment: this.moderationComment,
|
moderationComment: this.moderationComment,
|
||||||
|
|
||||||
video,
|
video,
|
||||||
comment: null,
|
comment,
|
||||||
|
|
||||||
createdAt: this.createdAt,
|
createdAt: this.createdAt,
|
||||||
updatedAt: this.updatedAt,
|
updatedAt: this.updatedAt,
|
||||||
|
|
|
@ -25,7 +25,7 @@ export class VideoCommentAbuseModel extends Model<VideoCommentAbuseModel> {
|
||||||
@AllowNull(true)
|
@AllowNull(true)
|
||||||
@Default(null)
|
@Default(null)
|
||||||
@Column(DataType.JSONB)
|
@Column(DataType.JSONB)
|
||||||
deletedComment: VideoComment
|
deletedComment: VideoComment & { Video: { name: string, id: number, uuid: string }}
|
||||||
|
|
||||||
@ForeignKey(() => AbuseModel)
|
@ForeignKey(() => AbuseModel)
|
||||||
@Column
|
@Column
|
||||||
|
|
|
@ -1,7 +1,22 @@
|
||||||
import * as Bluebird from 'bluebird'
|
import * as Bluebird from 'bluebird'
|
||||||
import { uniq } from 'lodash'
|
import { uniq } from 'lodash'
|
||||||
import { FindOptions, Op, Order, ScopeOptions, Sequelize, Transaction } from 'sequelize'
|
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 { getServerActor } from '@server/models/application/application'
|
||||||
import { MAccount, MAccountId, MUserAccountId } from '@server/types/models'
|
import { MAccount, MAccountId, MUserAccountId } from '@server/types/models'
|
||||||
import { VideoPrivacy } from '@shared/models'
|
import { VideoPrivacy } from '@shared/models'
|
||||||
|
@ -24,6 +39,7 @@ import {
|
||||||
MCommentOwnerVideoReply,
|
MCommentOwnerVideoReply,
|
||||||
MVideoImmutable
|
MVideoImmutable
|
||||||
} from '../../types/models/video'
|
} from '../../types/models/video'
|
||||||
|
import { VideoCommentAbuseModel } from '../abuse/video-comment-abuse'
|
||||||
import { AccountModel } from '../account/account'
|
import { AccountModel } from '../account/account'
|
||||||
import { ActorModel, unusedActorAttributesForAPI } from '../activitypub/actor'
|
import { ActorModel, unusedActorAttributesForAPI } from '../activitypub/actor'
|
||||||
import { buildBlockedAccountSQL, buildLocalAccountIdsIn, getCommentSort, throwIfNotValid } from '../utils'
|
import { buildBlockedAccountSQL, buildLocalAccountIdsIn, getCommentSort, throwIfNotValid } from '../utils'
|
||||||
|
@ -224,6 +240,53 @@ export class VideoCommentModel extends Model<VideoCommentModel> {
|
||||||
})
|
})
|
||||||
Account: AccountModel
|
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> {
|
static loadById (id: number, t?: Transaction): Bluebird<MComment> {
|
||||||
const query: FindOptions = {
|
const query: FindOptions = {
|
||||||
where: {
|
where: {
|
||||||
|
|
|
@ -803,14 +803,14 @@ export class VideoModel extends Model<VideoModel> {
|
||||||
static async saveEssentialDataToAbuses (instance: VideoModel, options) {
|
static async saveEssentialDataToAbuses (instance: VideoModel, options) {
|
||||||
const tasks: Promise<any>[] = []
|
const tasks: Promise<any>[] = []
|
||||||
|
|
||||||
logger.info('Saving video abuses details of video %s.', instance.url)
|
|
||||||
|
|
||||||
if (!Array.isArray(instance.VideoAbuses)) {
|
if (!Array.isArray(instance.VideoAbuses)) {
|
||||||
instance.VideoAbuses = await instance.$get('VideoAbuses')
|
instance.VideoAbuses = await instance.$get('VideoAbuses')
|
||||||
|
|
||||||
if (instance.VideoAbuses.length === 0) return undefined
|
if (instance.VideoAbuses.length === 0) return undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
|
logger.info('Saving video abuses details of video %s.', instance.url)
|
||||||
|
|
||||||
const details = instance.toFormattedDetailsJSON()
|
const details = instance.toFormattedDetailsJSON()
|
||||||
|
|
||||||
for (const abuse of instance.VideoAbuses) {
|
for (const abuse of instance.VideoAbuses) {
|
||||||
|
|
|
@ -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 ])
|
||||||
|
})
|
||||||
|
})
|
|
@ -1,3 +1,4 @@
|
||||||
|
import './abuses'
|
||||||
import './accounts'
|
import './accounts'
|
||||||
import './blocklist'
|
import './blocklist'
|
||||||
import './bulk'
|
import './bulk'
|
||||||
|
|
|
@ -152,12 +152,6 @@ describe('Test video abuses API validators', function () {
|
||||||
await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields })
|
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 () {
|
it('Should succeed with the corret parameters (advanced)', async function () {
|
||||||
const fields: VideoAbuseCreate = { reason: 'my super reason', predefinedReasons: [ 'serverRules' ], startAt: 1, endAt: 5 }
|
const fields: VideoAbuseCreate = { reason: 'my super reason', predefinedReasons: [ 'serverRules' ], startAt: 1, endAt: 5 }
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@ import { VideoCommentAbuseModel } from '@server/models/abuse/video-comment-abuse
|
||||||
import { PickWith } from '@shared/core-utils'
|
import { PickWith } from '@shared/core-utils'
|
||||||
import { AbuseModel } from '../../../models/abuse/abuse'
|
import { AbuseModel } from '../../../models/abuse/abuse'
|
||||||
import { MAccountDefault, MAccountFormattable, MAccountLight, MAccountUrl } from '../account'
|
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'
|
import { MVideo, MVideoAccountLightBlacklistAllFiles } from '../video/video'
|
||||||
|
|
||||||
type Use<K extends keyof AbuseModel, M> = PickWith<AbuseModel, K, M>
|
type Use<K extends keyof AbuseModel, M> = PickWith<AbuseModel, K, M>
|
||||||
|
@ -51,6 +51,10 @@ export type MCommentAbuseUrl =
|
||||||
MCommentAbuse &
|
MCommentAbuse &
|
||||||
UseCommentAbuse<'VideoComment', MCommentUrl>
|
UseCommentAbuse<'VideoComment', MCommentUrl>
|
||||||
|
|
||||||
|
export type MCommentAbuseFormattable =
|
||||||
|
MCommentAbuse &
|
||||||
|
UseCommentAbuse<'VideoComment', MComment & PickWith<MCommentVideo, 'Video', Pick<MVideo, 'id' | 'uuid' | 'name'>>>
|
||||||
|
|
||||||
// ############################################################################
|
// ############################################################################
|
||||||
|
|
||||||
export type MAbuseId = Pick<AbuseModel, 'id'>
|
export type MAbuseId = Pick<AbuseModel, 'id'>
|
||||||
|
@ -94,4 +98,5 @@ export type MAbuseFull =
|
||||||
export type MAbuseFormattable =
|
export type MAbuseFormattable =
|
||||||
MAbuse &
|
MAbuse &
|
||||||
Use<'ReporterAccount', MAccountFormattable> &
|
Use<'ReporterAccount', MAccountFormattable> &
|
||||||
Use<'VideoAbuse', MVideoAbuseFormattable>
|
Use<'VideoAbuse', MVideoAbuseFormattable> &
|
||||||
|
Use<'VideoCommentAbuse', MCommentAbuseFormattable>
|
||||||
|
|
|
@ -91,6 +91,7 @@ declare module 'express' {
|
||||||
|
|
||||||
accountVideoRate?: MAccountVideoRateAccountVideo
|
accountVideoRate?: MAccountVideoRateAccountVideo
|
||||||
|
|
||||||
|
videoComment?: MComment
|
||||||
videoCommentFull?: MCommentOwnerVideoReply
|
videoCommentFull?: MCommentOwnerVideoReply
|
||||||
videoCommentThread?: MComment
|
videoCommentThread?: MComment
|
||||||
|
|
||||||
|
|
|
@ -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 (
|
import { AbuseFilter, AbusePredefinedReasonsString, AbuseState, AbuseUpdate, AbuseVideoIs } from '@shared/models'
|
||||||
url: string,
|
import { makeDeleteRequest, makeGetRequest, makePostBodyRequest, makePutBodyRequest } from '../requests/requests'
|
||||||
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)
|
function reportAbuse (options: {
|
||||||
.post(path)
|
url: string
|
||||||
.set('Accept', 'application/json')
|
token: string
|
||||||
.set('Authorization', 'Bearer ' + token)
|
|
||||||
.send({ reason, predefinedReasons, startAt, endAt })
|
reason: string
|
||||||
.expect(specialStatus)
|
|
||||||
|
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: {
|
function getAbusesList (options: {
|
||||||
|
@ -28,6 +60,7 @@ function getAbusesList (options: {
|
||||||
id?: number
|
id?: number
|
||||||
predefinedReason?: AbusePredefinedReasonsString
|
predefinedReason?: AbusePredefinedReasonsString
|
||||||
search?: string
|
search?: string
|
||||||
|
filter?: AbuseFilter,
|
||||||
state?: AbuseState
|
state?: AbuseState
|
||||||
videoIs?: AbuseVideoIs
|
videoIs?: AbuseVideoIs
|
||||||
searchReporter?: string
|
searchReporter?: string
|
||||||
|
@ -41,6 +74,7 @@ function getAbusesList (options: {
|
||||||
id,
|
id,
|
||||||
predefinedReason,
|
predefinedReason,
|
||||||
search,
|
search,
|
||||||
|
filter,
|
||||||
state,
|
state,
|
||||||
videoIs,
|
videoIs,
|
||||||
searchReporter,
|
searchReporter,
|
||||||
|
@ -48,7 +82,7 @@ function getAbusesList (options: {
|
||||||
searchVideo,
|
searchVideo,
|
||||||
searchVideoChannel
|
searchVideoChannel
|
||||||
} = options
|
} = options
|
||||||
const path = '/api/v1/videos/abuse'
|
const path = '/api/v1/abuses'
|
||||||
|
|
||||||
const query = {
|
const query = {
|
||||||
sort: 'createdAt',
|
sort: 'createdAt',
|
||||||
|
@ -56,6 +90,7 @@ function getAbusesList (options: {
|
||||||
predefinedReason,
|
predefinedReason,
|
||||||
search,
|
search,
|
||||||
state,
|
state,
|
||||||
|
filter,
|
||||||
videoIs,
|
videoIs,
|
||||||
searchReporter,
|
searchReporter,
|
||||||
searchReportee,
|
searchReportee,
|
||||||
|
@ -75,12 +110,11 @@ function getAbusesList (options: {
|
||||||
function updateAbuse (
|
function updateAbuse (
|
||||||
url: string,
|
url: string,
|
||||||
token: string,
|
token: string,
|
||||||
videoId: string | number,
|
abuseId: number,
|
||||||
videoAbuseId: number,
|
|
||||||
body: AbuseUpdate,
|
body: AbuseUpdate,
|
||||||
statusCodeExpected = 204
|
statusCodeExpected = 204
|
||||||
) {
|
) {
|
||||||
const path = '/api/v1/videos/' + videoId + '/abuse/' + videoAbuseId
|
const path = '/api/v1/abuses/' + abuseId
|
||||||
|
|
||||||
return makePutBodyRequest({
|
return makePutBodyRequest({
|
||||||
url,
|
url,
|
||||||
|
@ -91,8 +125,8 @@ function updateAbuse (
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function deleteAbuse (url: string, token: string, videoId: string | number, videoAbuseId: number, statusCodeExpected = 204) {
|
function deleteAbuse (url: string, token: string, abuseId: number, statusCodeExpected = 204) {
|
||||||
const path = '/api/v1/videos/' + videoId + '/abuse/' + videoAbuseId
|
const path = '/api/v1/abuses/' + abuseId
|
||||||
|
|
||||||
return makeDeleteRequest({
|
return makeDeleteRequest({
|
||||||
url,
|
url,
|
||||||
|
|
|
@ -1,11 +1,14 @@
|
||||||
import { AbusePredefinedReasonsString } from './abuse-reason.model'
|
import { AbusePredefinedReasonsString } from './abuse-reason.model'
|
||||||
|
|
||||||
export interface AbuseCreate {
|
export interface AbuseCreate {
|
||||||
accountId: number
|
|
||||||
|
|
||||||
reason: string
|
reason: string
|
||||||
|
|
||||||
predefinedReasons?: AbusePredefinedReasonsString[]
|
predefinedReasons?: AbusePredefinedReasonsString[]
|
||||||
|
|
||||||
|
account?: {
|
||||||
|
id: number
|
||||||
|
}
|
||||||
|
|
||||||
video?: {
|
video?: {
|
||||||
id: number
|
id: number
|
||||||
startAt?: number
|
startAt?: number
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
export type AbuseFilter = 'video' | 'comment'
|
|
|
@ -0,0 +1 @@
|
||||||
|
export type AbuseFilter = 'video' | 'comment' | 'account'
|
|
@ -9,6 +9,7 @@ export interface VideoAbuse {
|
||||||
name: string
|
name: string
|
||||||
uuid: string
|
uuid: string
|
||||||
nsfw: boolean
|
nsfw: boolean
|
||||||
|
|
||||||
deleted: boolean
|
deleted: boolean
|
||||||
blacklisted: boolean
|
blacklisted: boolean
|
||||||
|
|
||||||
|
@ -21,8 +22,15 @@ export interface VideoAbuse {
|
||||||
|
|
||||||
export interface VideoCommentAbuse {
|
export interface VideoCommentAbuse {
|
||||||
id: number
|
id: number
|
||||||
account?: Account
|
|
||||||
|
video: {
|
||||||
|
id: number
|
||||||
|
name: string
|
||||||
|
uuid: string
|
||||||
|
}
|
||||||
|
|
||||||
text: string
|
text: string
|
||||||
|
|
||||||
deleted: boolean
|
deleted: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
export * from './abuse-create.model'
|
export * from './abuse-create.model'
|
||||||
|
export * from './abuse-filter.type'
|
||||||
export * from './abuse-reason.model'
|
export * from './abuse-reason.model'
|
||||||
export * from './abuse-state.model'
|
export * from './abuse-state.model'
|
||||||
export * from './abuse-update.model'
|
export * from './abuse-update.model'
|
||||||
|
|
Loading…
Reference in New Issue