Add state and moderationComment for abuses on server side
This commit is contained in:
parent
904a463c77
commit
268eebed92
|
@ -1,5 +1,5 @@
|
||||||
import * as express from 'express'
|
import * as express from 'express'
|
||||||
import { UserRight, VideoAbuseCreate } from '../../../../shared'
|
import { UserRight, VideoAbuseCreate, VideoAbuseState } from '../../../../shared'
|
||||||
import { logger } from '../../../helpers/logger'
|
import { logger } from '../../../helpers/logger'
|
||||||
import { getFormattedObjects } from '../../../helpers/utils'
|
import { getFormattedObjects } from '../../../helpers/utils'
|
||||||
import { sequelizeTypescript } from '../../../initializers'
|
import { sequelizeTypescript } from '../../../initializers'
|
||||||
|
@ -12,8 +12,10 @@ import {
|
||||||
paginationValidator,
|
paginationValidator,
|
||||||
setDefaultPagination,
|
setDefaultPagination,
|
||||||
setDefaultSort,
|
setDefaultSort,
|
||||||
|
videoAbuseGetValidator,
|
||||||
videoAbuseReportValidator,
|
videoAbuseReportValidator,
|
||||||
videoAbusesSortValidator
|
videoAbusesSortValidator,
|
||||||
|
videoAbuseUpdateValidator
|
||||||
} from '../../../middlewares'
|
} from '../../../middlewares'
|
||||||
import { AccountModel } from '../../../models/account/account'
|
import { AccountModel } from '../../../models/account/account'
|
||||||
import { VideoModel } from '../../../models/video/video'
|
import { VideoModel } from '../../../models/video/video'
|
||||||
|
@ -32,11 +34,23 @@ abuseVideoRouter.get('/abuse',
|
||||||
setDefaultPagination,
|
setDefaultPagination,
|
||||||
asyncMiddleware(listVideoAbuses)
|
asyncMiddleware(listVideoAbuses)
|
||||||
)
|
)
|
||||||
abuseVideoRouter.post('/:id/abuse',
|
abuseVideoRouter.put('/:videoId/abuse/:id',
|
||||||
|
authenticate,
|
||||||
|
ensureUserHasRight(UserRight.MANAGE_VIDEO_ABUSES),
|
||||||
|
asyncMiddleware(videoAbuseUpdateValidator),
|
||||||
|
asyncRetryTransactionMiddleware(updateVideoAbuse)
|
||||||
|
)
|
||||||
|
abuseVideoRouter.post('/:videoId/abuse',
|
||||||
authenticate,
|
authenticate,
|
||||||
asyncMiddleware(videoAbuseReportValidator),
|
asyncMiddleware(videoAbuseReportValidator),
|
||||||
asyncRetryTransactionMiddleware(reportVideoAbuse)
|
asyncRetryTransactionMiddleware(reportVideoAbuse)
|
||||||
)
|
)
|
||||||
|
abuseVideoRouter.delete('/:videoId/abuse/:id',
|
||||||
|
authenticate,
|
||||||
|
ensureUserHasRight(UserRight.MANAGE_VIDEO_ABUSES),
|
||||||
|
asyncMiddleware(videoAbuseGetValidator),
|
||||||
|
asyncRetryTransactionMiddleware(deleteVideoAbuse)
|
||||||
|
)
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
@ -46,12 +60,39 @@ export {
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
async function listVideoAbuses (req: express.Request, res: express.Response, next: express.NextFunction) {
|
async function listVideoAbuses (req: express.Request, res: express.Response) {
|
||||||
const resultList = await VideoAbuseModel.listForApi(req.query.start, req.query.count, req.query.sort)
|
const resultList = await VideoAbuseModel.listForApi(req.query.start, req.query.count, req.query.sort)
|
||||||
|
|
||||||
return res.json(getFormattedObjects(resultList.data, resultList.total))
|
return res.json(getFormattedObjects(resultList.data, resultList.total))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function updateVideoAbuse (req: express.Request, res: express.Response) {
|
||||||
|
const videoAbuse: VideoAbuseModel = res.locals.videoAbuse
|
||||||
|
|
||||||
|
if (req.body.moderationComment !== undefined) videoAbuse.moderationComment = req.body.moderationComment
|
||||||
|
if (req.body.state !== undefined) videoAbuse.state = req.body.state
|
||||||
|
|
||||||
|
await sequelizeTypescript.transaction(t => {
|
||||||
|
return videoAbuse.save({ transaction: t })
|
||||||
|
})
|
||||||
|
|
||||||
|
// Do not send the delete to other instances, we updated OUR copy of this video abuse
|
||||||
|
|
||||||
|
return res.type('json').status(204).end()
|
||||||
|
}
|
||||||
|
|
||||||
|
async function deleteVideoAbuse (req: express.Request, res: express.Response) {
|
||||||
|
const videoAbuse: VideoAbuseModel = res.locals.videoAbuse
|
||||||
|
|
||||||
|
await sequelizeTypescript.transaction(t => {
|
||||||
|
return videoAbuse.destroy({ transaction: t })
|
||||||
|
})
|
||||||
|
|
||||||
|
// Do not send the delete to other instances, we delete OUR copy of this video abuse
|
||||||
|
|
||||||
|
return res.type('json').status(204).end()
|
||||||
|
}
|
||||||
|
|
||||||
async function reportVideoAbuse (req: express.Request, res: express.Response) {
|
async function reportVideoAbuse (req: express.Request, res: express.Response) {
|
||||||
const videoInstance = res.locals.video as VideoModel
|
const videoInstance = res.locals.video as VideoModel
|
||||||
const reporterAccount = res.locals.oauth.token.User.Account as AccountModel
|
const reporterAccount = res.locals.oauth.token.User.Account as AccountModel
|
||||||
|
@ -60,10 +101,11 @@ async function reportVideoAbuse (req: express.Request, res: express.Response) {
|
||||||
const abuseToCreate = {
|
const abuseToCreate = {
|
||||||
reporterAccountId: reporterAccount.id,
|
reporterAccountId: reporterAccount.id,
|
||||||
reason: body.reason,
|
reason: body.reason,
|
||||||
videoId: videoInstance.id
|
videoId: videoInstance.id,
|
||||||
|
state: VideoAbuseState.PENDING
|
||||||
}
|
}
|
||||||
|
|
||||||
await sequelizeTypescript.transaction(async t => {
|
const videoAbuse: VideoAbuseModel = await sequelizeTypescript.transaction(async t => {
|
||||||
const videoAbuseInstance = await VideoAbuseModel.create(abuseToCreate, { transaction: t })
|
const videoAbuseInstance = await VideoAbuseModel.create(abuseToCreate, { transaction: t })
|
||||||
videoAbuseInstance.Video = videoInstance
|
videoAbuseInstance.Video = videoInstance
|
||||||
videoAbuseInstance.Account = reporterAccount
|
videoAbuseInstance.Account = reporterAccount
|
||||||
|
@ -74,8 +116,12 @@ async function reportVideoAbuse (req: express.Request, res: express.Response) {
|
||||||
}
|
}
|
||||||
|
|
||||||
auditLogger.create(reporterAccount.Actor.getIdentifier(), new VideoAbuseAuditView(videoAbuseInstance.toFormattedJSON()))
|
auditLogger.create(reporterAccount.Actor.getIdentifier(), new VideoAbuseAuditView(videoAbuseInstance.toFormattedJSON()))
|
||||||
logger.info('Abuse report for video %s created.', videoInstance.name)
|
|
||||||
|
return videoAbuseInstance
|
||||||
})
|
})
|
||||||
|
|
||||||
return res.type('json').status(204).end()
|
logger.info('Abuse report for video %s created.', videoInstance.name)
|
||||||
|
return res.json({
|
||||||
|
videoAbuse: videoAbuse.toFormattedJSON()
|
||||||
|
}).end()
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,6 @@ import { ACTIVITY_PUB, CONSTRAINTS_FIELDS } from '../../../initializers'
|
||||||
import { peertubeTruncate } from '../../core-utils'
|
import { peertubeTruncate } from '../../core-utils'
|
||||||
import { exists, isBooleanValid, isDateValid, isUUIDValid } from '../misc'
|
import { exists, isBooleanValid, isDateValid, isUUIDValid } from '../misc'
|
||||||
import {
|
import {
|
||||||
isVideoAbuseReasonValid,
|
|
||||||
isVideoDurationValid,
|
isVideoDurationValid,
|
||||||
isVideoNameValid,
|
isVideoNameValid,
|
||||||
isVideoStateValid,
|
isVideoStateValid,
|
||||||
|
@ -13,6 +12,7 @@ import {
|
||||||
} from '../videos'
|
} from '../videos'
|
||||||
import { isActivityPubUrlValid, isBaseActivityValid, setValidAttributedTo } from './misc'
|
import { isActivityPubUrlValid, isBaseActivityValid, setValidAttributedTo } from './misc'
|
||||||
import { VideoState } from '../../../../shared/models/videos'
|
import { VideoState } from '../../../../shared/models/videos'
|
||||||
|
import { isVideoAbuseReasonValid } from '../video-abuses'
|
||||||
|
|
||||||
function sanitizeAndCheckVideoTorrentCreateActivity (activity: any) {
|
function sanitizeAndCheckVideoTorrentCreateActivity (activity: any) {
|
||||||
return isBaseActivityValid(activity, 'Create') &&
|
return isBaseActivityValid(activity, 'Create') &&
|
||||||
|
|
|
@ -0,0 +1,43 @@
|
||||||
|
import { Response } from 'express'
|
||||||
|
import * as validator from 'validator'
|
||||||
|
import { CONSTRAINTS_FIELDS, VIDEO_ABUSE_STATES } from '../../initializers'
|
||||||
|
import { exists } from './misc'
|
||||||
|
import { VideoAbuseModel } from '../../models/video/video-abuse'
|
||||||
|
|
||||||
|
const VIDEO_ABUSES_CONSTRAINTS_FIELDS = CONSTRAINTS_FIELDS.VIDEO_ABUSES
|
||||||
|
|
||||||
|
function isVideoAbuseReasonValid (value: string) {
|
||||||
|
return exists(value) && validator.isLength(value, VIDEO_ABUSES_CONSTRAINTS_FIELDS.REASON)
|
||||||
|
}
|
||||||
|
|
||||||
|
function isVideoAbuseModerationCommentValid (value: string) {
|
||||||
|
return exists(value) && validator.isLength(value, VIDEO_ABUSES_CONSTRAINTS_FIELDS.MODERATION_COMMENT)
|
||||||
|
}
|
||||||
|
|
||||||
|
function isVideoAbuseStateValid (value: string) {
|
||||||
|
return exists(value) && VIDEO_ABUSE_STATES[ value ] !== undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
async function isVideoAbuseExist (abuseId: number, videoId: number, res: Response) {
|
||||||
|
const videoAbuse = await VideoAbuseModel.loadByIdAndVideoId(abuseId, videoId)
|
||||||
|
|
||||||
|
if (videoAbuse === null) {
|
||||||
|
res.status(404)
|
||||||
|
.json({ error: 'Video abuse not found' })
|
||||||
|
.end()
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
res.locals.videoAbuse = videoAbuse
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
export {
|
||||||
|
isVideoAbuseExist,
|
||||||
|
isVideoAbuseStateValid,
|
||||||
|
isVideoAbuseReasonValid,
|
||||||
|
isVideoAbuseModerationCommentValid
|
||||||
|
}
|
|
@ -6,6 +6,7 @@ import * as validator from 'validator'
|
||||||
import { UserRight, VideoPrivacy, VideoRateType } from '../../../shared'
|
import { UserRight, VideoPrivacy, VideoRateType } from '../../../shared'
|
||||||
import {
|
import {
|
||||||
CONSTRAINTS_FIELDS,
|
CONSTRAINTS_FIELDS,
|
||||||
|
VIDEO_ABUSE_STATES,
|
||||||
VIDEO_CATEGORIES,
|
VIDEO_CATEGORIES,
|
||||||
VIDEO_LICENCES,
|
VIDEO_LICENCES,
|
||||||
VIDEO_MIMETYPE_EXT,
|
VIDEO_MIMETYPE_EXT,
|
||||||
|
@ -18,6 +19,7 @@ import { exists, isArray, isFileValid } from './misc'
|
||||||
import { VideoChannelModel } from '../../models/video/video-channel'
|
import { VideoChannelModel } from '../../models/video/video-channel'
|
||||||
import { UserModel } from '../../models/account/user'
|
import { UserModel } from '../../models/account/user'
|
||||||
import * as magnetUtil from 'magnet-uri'
|
import * as magnetUtil from 'magnet-uri'
|
||||||
|
import { VideoAbuseModel } from '../../models/video/video-abuse'
|
||||||
|
|
||||||
const VIDEOS_CONSTRAINTS_FIELDS = CONSTRAINTS_FIELDS.VIDEOS
|
const VIDEOS_CONSTRAINTS_FIELDS = CONSTRAINTS_FIELDS.VIDEOS
|
||||||
const VIDEO_ABUSES_CONSTRAINTS_FIELDS = CONSTRAINTS_FIELDS.VIDEO_ABUSES
|
const VIDEO_ABUSES_CONSTRAINTS_FIELDS = CONSTRAINTS_FIELDS.VIDEO_ABUSES
|
||||||
|
@ -71,10 +73,6 @@ function isVideoTagsValid (tags: string[]) {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function isVideoAbuseReasonValid (value: string) {
|
|
||||||
return exists(value) && validator.isLength(value, VIDEO_ABUSES_CONSTRAINTS_FIELDS.REASON)
|
|
||||||
}
|
|
||||||
|
|
||||||
function isVideoViewsValid (value: string) {
|
function isVideoViewsValid (value: string) {
|
||||||
return exists(value) && validator.isInt(value + '', VIDEOS_CONSTRAINTS_FIELDS.VIEWS)
|
return exists(value) && validator.isInt(value + '', VIDEOS_CONSTRAINTS_FIELDS.VIEWS)
|
||||||
}
|
}
|
||||||
|
@ -220,7 +218,6 @@ export {
|
||||||
isVideoTagsValid,
|
isVideoTagsValid,
|
||||||
isVideoFPSResolutionValid,
|
isVideoFPSResolutionValid,
|
||||||
isScheduleVideoUpdatePrivacyValid,
|
isScheduleVideoUpdatePrivacyValid,
|
||||||
isVideoAbuseReasonValid,
|
|
||||||
isVideoFile,
|
isVideoFile,
|
||||||
isVideoMagnetUriValid,
|
isVideoMagnetUriValid,
|
||||||
isVideoStateValid,
|
isVideoStateValid,
|
||||||
|
|
|
@ -3,7 +3,7 @@ import { dirname, join } from 'path'
|
||||||
import { JobType, VideoRateType, VideoState } from '../../shared/models'
|
import { JobType, VideoRateType, VideoState } from '../../shared/models'
|
||||||
import { ActivityPubActorType } from '../../shared/models/activitypub'
|
import { ActivityPubActorType } from '../../shared/models/activitypub'
|
||||||
import { FollowState } from '../../shared/models/actors'
|
import { FollowState } from '../../shared/models/actors'
|
||||||
import { VideoPrivacy } from '../../shared/models/videos'
|
import { VideoPrivacy, VideoAbuseState } from '../../shared/models/videos'
|
||||||
// Do not use barrels, remain constants as independent as possible
|
// Do not use barrels, remain constants as independent as possible
|
||||||
import { buildPath, isTestInstance, root, sanitizeHost, sanitizeUrl } from '../helpers/core-utils'
|
import { buildPath, isTestInstance, root, sanitizeHost, sanitizeUrl } from '../helpers/core-utils'
|
||||||
import { NSFWPolicyType } from '../../shared/models/videos/nsfw-policy.type'
|
import { NSFWPolicyType } from '../../shared/models/videos/nsfw-policy.type'
|
||||||
|
@ -15,7 +15,7 @@ let config: IConfig = require('config')
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
const LAST_MIGRATION_VERSION = 245
|
const LAST_MIGRATION_VERSION = 250
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
@ -258,7 +258,8 @@ const CONSTRAINTS_FIELDS = {
|
||||||
BLOCKED_REASON: { min: 3, max: 250 } // Length
|
BLOCKED_REASON: { min: 3, max: 250 } // Length
|
||||||
},
|
},
|
||||||
VIDEO_ABUSES: {
|
VIDEO_ABUSES: {
|
||||||
REASON: { min: 2, max: 300 } // Length
|
REASON: { min: 2, max: 300 }, // Length
|
||||||
|
MODERATION_COMMENT: { min: 2, max: 300 } // Length
|
||||||
},
|
},
|
||||||
VIDEO_CHANNELS: {
|
VIDEO_CHANNELS: {
|
||||||
NAME: { min: 3, max: 120 }, // Length
|
NAME: { min: 3, max: 120 }, // Length
|
||||||
|
@ -409,6 +410,12 @@ const VIDEO_IMPORT_STATES = {
|
||||||
[VideoImportState.SUCCESS]: 'Success'
|
[VideoImportState.SUCCESS]: 'Success'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const VIDEO_ABUSE_STATES = {
|
||||||
|
[VideoAbuseState.PENDING]: 'Pending',
|
||||||
|
[VideoAbuseState.REJECTED]: 'Rejected',
|
||||||
|
[VideoAbuseState.ACCEPTED]: 'Accepted'
|
||||||
|
}
|
||||||
|
|
||||||
const VIDEO_MIMETYPE_EXT = {
|
const VIDEO_MIMETYPE_EXT = {
|
||||||
'video/webm': '.webm',
|
'video/webm': '.webm',
|
||||||
'video/ogg': '.ogv',
|
'video/ogg': '.ogv',
|
||||||
|
@ -625,6 +632,7 @@ export {
|
||||||
VIDEO_MIMETYPE_EXT,
|
VIDEO_MIMETYPE_EXT,
|
||||||
VIDEO_TRANSCODING_FPS,
|
VIDEO_TRANSCODING_FPS,
|
||||||
FFMPEG_NICE,
|
FFMPEG_NICE,
|
||||||
|
VIDEO_ABUSE_STATES,
|
||||||
JOB_REQUEST_TIMEOUT,
|
JOB_REQUEST_TIMEOUT,
|
||||||
USER_PASSWORD_RESET_LIFETIME,
|
USER_PASSWORD_RESET_LIFETIME,
|
||||||
IMAGE_MIMETYPE_EXT,
|
IMAGE_MIMETYPE_EXT,
|
||||||
|
|
|
@ -0,0 +1,47 @@
|
||||||
|
import * as Sequelize from 'sequelize'
|
||||||
|
import { CONSTRAINTS_FIELDS } from '../constants'
|
||||||
|
import { VideoAbuseState } from '../../../shared/models/videos'
|
||||||
|
|
||||||
|
async function up (utils: {
|
||||||
|
transaction: Sequelize.Transaction
|
||||||
|
queryInterface: Sequelize.QueryInterface
|
||||||
|
sequelize: Sequelize.Sequelize
|
||||||
|
}): Promise<any> {
|
||||||
|
{
|
||||||
|
const data = {
|
||||||
|
type: Sequelize.INTEGER,
|
||||||
|
allowNull: true,
|
||||||
|
defaultValue: null
|
||||||
|
}
|
||||||
|
await utils.queryInterface.addColumn('videoAbuse', 'state', data)
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
const query = 'UPDATE "videoAbuse" SET "state" = ' + VideoAbuseState.PENDING
|
||||||
|
await utils.sequelize.query(query)
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
const data = {
|
||||||
|
type: Sequelize.INTEGER,
|
||||||
|
allowNull: false,
|
||||||
|
defaultValue: null
|
||||||
|
}
|
||||||
|
await utils.queryInterface.changeColumn('videoAbuse', 'state', data)
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
const data = {
|
||||||
|
type: Sequelize.STRING(CONSTRAINTS_FIELDS.VIDEO_ABUSES.MODERATION_COMMENT.max),
|
||||||
|
allowNull: true,
|
||||||
|
defaultValue: null
|
||||||
|
}
|
||||||
|
await utils.queryInterface.addColumn('videoAbuse', 'moderationComment', data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function down (options) {
|
||||||
|
throw new Error('Not implemented.')
|
||||||
|
}
|
||||||
|
|
||||||
|
export { up, down }
|
|
@ -1,4 +1,4 @@
|
||||||
import { ActivityCreate, VideoTorrentObject } from '../../../../shared'
|
import { ActivityCreate, VideoAbuseState, VideoTorrentObject } from '../../../../shared'
|
||||||
import { DislikeObject, VideoAbuseObject, ViewObject } from '../../../../shared/models/activitypub/objects'
|
import { DislikeObject, VideoAbuseObject, ViewObject } from '../../../../shared/models/activitypub/objects'
|
||||||
import { VideoCommentObject } from '../../../../shared/models/activitypub/objects/video-comment-object'
|
import { VideoCommentObject } from '../../../../shared/models/activitypub/objects/video-comment-object'
|
||||||
import { retryTransactionWrapper } from '../../../helpers/database-utils'
|
import { retryTransactionWrapper } from '../../../helpers/database-utils'
|
||||||
|
@ -112,7 +112,8 @@ async function processCreateVideoAbuse (actor: ActorModel, videoAbuseToCreateDat
|
||||||
const videoAbuseData = {
|
const videoAbuseData = {
|
||||||
reporterAccountId: account.id,
|
reporterAccountId: account.id,
|
||||||
reason: videoAbuseToCreateData.content,
|
reason: videoAbuseToCreateData.content,
|
||||||
videoId: video.id
|
videoId: video.id,
|
||||||
|
state: VideoAbuseState.PENDING
|
||||||
}
|
}
|
||||||
|
|
||||||
await VideoAbuseModel.create(videoAbuseData)
|
await VideoAbuseModel.create(videoAbuseData)
|
||||||
|
|
|
@ -7,6 +7,7 @@ export * from './feeds'
|
||||||
export * from './sort'
|
export * from './sort'
|
||||||
export * from './users'
|
export * from './users'
|
||||||
export * from './videos'
|
export * from './videos'
|
||||||
|
export * from './video-abuses'
|
||||||
export * from './video-blacklist'
|
export * from './video-blacklist'
|
||||||
export * from './video-channels'
|
export * from './video-channels'
|
||||||
export * from './webfinger'
|
export * from './webfinger'
|
||||||
|
|
|
@ -0,0 +1,71 @@
|
||||||
|
import * as express from 'express'
|
||||||
|
import 'express-validator'
|
||||||
|
import { body, param } from 'express-validator/check'
|
||||||
|
import { isIdOrUUIDValid, isIdValid } from '../../helpers/custom-validators/misc'
|
||||||
|
import { isVideoExist } from '../../helpers/custom-validators/videos'
|
||||||
|
import { logger } from '../../helpers/logger'
|
||||||
|
import { areValidationErrors } from './utils'
|
||||||
|
import {
|
||||||
|
isVideoAbuseExist,
|
||||||
|
isVideoAbuseModerationCommentValid,
|
||||||
|
isVideoAbuseReasonValid,
|
||||||
|
isVideoAbuseStateValid
|
||||||
|
} from '../../helpers/custom-validators/video-abuses'
|
||||||
|
|
||||||
|
const videoAbuseReportValidator = [
|
||||||
|
param('videoId').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid videoId'),
|
||||||
|
body('reason').custom(isVideoAbuseReasonValid).withMessage('Should have a valid reason'),
|
||||||
|
|
||||||
|
async (req: express.Request, res: express.Response, next: express.NextFunction) => {
|
||||||
|
logger.debug('Checking videoAbuseReport parameters', { parameters: req.body })
|
||||||
|
|
||||||
|
if (areValidationErrors(req, res)) return
|
||||||
|
if (!await isVideoExist(req.params.videoId, res)) return
|
||||||
|
|
||||||
|
return next()
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
const videoAbuseGetValidator = [
|
||||||
|
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 videoAbuseGetValidator parameters', { parameters: req.body })
|
||||||
|
|
||||||
|
if (areValidationErrors(req, res)) return
|
||||||
|
if (!await isVideoExist(req.params.videoId, res)) return
|
||||||
|
if (!await isVideoAbuseExist(req.params.id, res.locals.video.id, res)) return
|
||||||
|
|
||||||
|
return next()
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
const videoAbuseUpdateValidator = [
|
||||||
|
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(isVideoAbuseStateValid).withMessage('Should have a valid video abuse state'),
|
||||||
|
body('moderationComment')
|
||||||
|
.optional()
|
||||||
|
.custom(isVideoAbuseModerationCommentValid).withMessage('Should have a valid video moderation comment'),
|
||||||
|
|
||||||
|
async (req: express.Request, res: express.Response, next: express.NextFunction) => {
|
||||||
|
logger.debug('Checking videoAbuseUpdateValidator parameters', { parameters: req.body })
|
||||||
|
|
||||||
|
if (areValidationErrors(req, res)) return
|
||||||
|
if (!await isVideoExist(req.params.videoId, res)) return
|
||||||
|
if (!await isVideoAbuseExist(req.params.id, res.locals.video.id, res)) return
|
||||||
|
|
||||||
|
return next()
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
export {
|
||||||
|
videoAbuseReportValidator,
|
||||||
|
videoAbuseGetValidator,
|
||||||
|
videoAbuseUpdateValidator
|
||||||
|
}
|
|
@ -14,7 +14,6 @@ import {
|
||||||
import {
|
import {
|
||||||
checkUserCanManageVideo,
|
checkUserCanManageVideo,
|
||||||
isScheduleVideoUpdatePrivacyValid,
|
isScheduleVideoUpdatePrivacyValid,
|
||||||
isVideoAbuseReasonValid,
|
|
||||||
isVideoCategoryValid,
|
isVideoCategoryValid,
|
||||||
isVideoChannelOfAccountExist,
|
isVideoChannelOfAccountExist,
|
||||||
isVideoDescriptionValid,
|
isVideoDescriptionValid,
|
||||||
|
@ -174,20 +173,6 @@ const videosRemoveValidator = [
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
const videoAbuseReportValidator = [
|
|
||||||
param('id').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid id'),
|
|
||||||
body('reason').custom(isVideoAbuseReasonValid).withMessage('Should have a valid reason'),
|
|
||||||
|
|
||||||
async (req: express.Request, res: express.Response, next: express.NextFunction) => {
|
|
||||||
logger.debug('Checking videoAbuseReport parameters', { parameters: req.body })
|
|
||||||
|
|
||||||
if (areValidationErrors(req, res)) return
|
|
||||||
if (!await isVideoExist(req.params.id, res)) return
|
|
||||||
|
|
||||||
return next()
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
const videoRateValidator = [
|
const videoRateValidator = [
|
||||||
param('id').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid id'),
|
param('id').custom(isIdOrUUIDValid).not().isEmpty().withMessage('Should have a valid id'),
|
||||||
body('rating').custom(isVideoRatingTypeValid).withMessage('Should have a valid rate type'),
|
body('rating').custom(isVideoRatingTypeValid).withMessage('Should have a valid rate type'),
|
||||||
|
@ -299,8 +284,6 @@ export {
|
||||||
videosRemoveValidator,
|
videosRemoveValidator,
|
||||||
videosShareValidator,
|
videosShareValidator,
|
||||||
|
|
||||||
videoAbuseReportValidator,
|
|
||||||
|
|
||||||
videoRateValidator,
|
videoRateValidator,
|
||||||
|
|
||||||
getCommonVideoAttributes
|
getCommonVideoAttributes
|
||||||
|
|
|
@ -1,11 +1,30 @@
|
||||||
import { AfterCreate, AllowNull, BelongsTo, Column, CreatedAt, ForeignKey, Is, Model, Table, UpdatedAt } from 'sequelize-typescript'
|
import {
|
||||||
|
AfterCreate,
|
||||||
|
AllowNull,
|
||||||
|
BelongsTo,
|
||||||
|
Column,
|
||||||
|
CreatedAt,
|
||||||
|
DataType,
|
||||||
|
Default,
|
||||||
|
ForeignKey,
|
||||||
|
Is,
|
||||||
|
Model,
|
||||||
|
Table,
|
||||||
|
UpdatedAt
|
||||||
|
} from 'sequelize-typescript'
|
||||||
import { VideoAbuseObject } from '../../../shared/models/activitypub/objects'
|
import { VideoAbuseObject } from '../../../shared/models/activitypub/objects'
|
||||||
import { VideoAbuse } from '../../../shared/models/videos'
|
import { VideoAbuse } from '../../../shared/models/videos'
|
||||||
import { isVideoAbuseReasonValid } from '../../helpers/custom-validators/videos'
|
import {
|
||||||
|
isVideoAbuseModerationCommentValid,
|
||||||
|
isVideoAbuseReasonValid,
|
||||||
|
isVideoAbuseStateValid
|
||||||
|
} from '../../helpers/custom-validators/video-abuses'
|
||||||
import { Emailer } from '../../lib/emailer'
|
import { Emailer } from '../../lib/emailer'
|
||||||
import { AccountModel } from '../account/account'
|
import { AccountModel } from '../account/account'
|
||||||
import { getSort, throwIfNotValid } from '../utils'
|
import { getSort, throwIfNotValid } from '../utils'
|
||||||
import { VideoModel } from './video'
|
import { VideoModel } from './video'
|
||||||
|
import { VideoAbuseState } from '../../../shared'
|
||||||
|
import { CONSTRAINTS_FIELDS, VIDEO_ABUSE_STATES } from '../../initializers'
|
||||||
|
|
||||||
@Table({
|
@Table({
|
||||||
tableName: 'videoAbuse',
|
tableName: 'videoAbuse',
|
||||||
|
@ -25,6 +44,18 @@ export class VideoAbuseModel extends Model<VideoAbuseModel> {
|
||||||
@Column
|
@Column
|
||||||
reason: string
|
reason: string
|
||||||
|
|
||||||
|
@AllowNull(false)
|
||||||
|
@Default(null)
|
||||||
|
@Is('VideoAbuseState', value => throwIfNotValid(value, isVideoAbuseStateValid, 'state'))
|
||||||
|
@Column
|
||||||
|
state: VideoAbuseState
|
||||||
|
|
||||||
|
@AllowNull(true)
|
||||||
|
@Default(null)
|
||||||
|
@Is('VideoAbuseModerationComment', value => throwIfNotValid(value, isVideoAbuseModerationCommentValid, 'moderationComment'))
|
||||||
|
@Column(DataType.STRING(CONSTRAINTS_FIELDS.VIDEO_ABUSES.MODERATION_COMMENT.max))
|
||||||
|
moderationComment: string
|
||||||
|
|
||||||
@CreatedAt
|
@CreatedAt
|
||||||
createdAt: Date
|
createdAt: Date
|
||||||
|
|
||||||
|
@ -60,6 +91,16 @@ export class VideoAbuseModel extends Model<VideoAbuseModel> {
|
||||||
return Emailer.Instance.addVideoAbuseReportJob(instance.videoId)
|
return Emailer.Instance.addVideoAbuseReportJob(instance.videoId)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static loadByIdAndVideoId (id: number, videoId: number) {
|
||||||
|
const query = {
|
||||||
|
where: {
|
||||||
|
id,
|
||||||
|
videoId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return VideoAbuseModel.findOne(query)
|
||||||
|
}
|
||||||
|
|
||||||
static listForApi (start: number, count: number, sort: string) {
|
static listForApi (start: number, count: number, sort: string) {
|
||||||
const query = {
|
const query = {
|
||||||
offset: start,
|
offset: start,
|
||||||
|
@ -88,6 +129,11 @@ export class VideoAbuseModel extends Model<VideoAbuseModel> {
|
||||||
id: this.id,
|
id: this.id,
|
||||||
reason: this.reason,
|
reason: this.reason,
|
||||||
reporterAccount: this.Account.toFormattedJSON(),
|
reporterAccount: this.Account.toFormattedJSON(),
|
||||||
|
state: {
|
||||||
|
id: this.state,
|
||||||
|
label: VideoAbuseModel.getStateLabel(this.state)
|
||||||
|
},
|
||||||
|
moderationComment: this.moderationComment,
|
||||||
video: {
|
video: {
|
||||||
id: this.Video.id,
|
id: this.Video.id,
|
||||||
uuid: this.Video.uuid,
|
uuid: this.Video.uuid,
|
||||||
|
@ -105,4 +151,8 @@ export class VideoAbuseModel extends Model<VideoAbuseModel> {
|
||||||
object: this.Video.url
|
object: this.Video.url
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static getStateLabel (id: number) {
|
||||||
|
return VIDEO_ABUSE_STATES[id] || 'Unknown'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -171,6 +171,7 @@ export class VideoImportModel extends Model<VideoImportModel> {
|
||||||
video
|
video
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static getStateLabel (id: number) {
|
private static getStateLabel (id: number) {
|
||||||
return VIDEO_IMPORT_STATES[id] || 'Unknown'
|
return VIDEO_IMPORT_STATES[id] || 'Unknown'
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,14 +3,26 @@
|
||||||
import 'mocha'
|
import 'mocha'
|
||||||
|
|
||||||
import {
|
import {
|
||||||
createUser, flushTests, killallServers, makeGetRequest, makePostBodyRequest, runServer, ServerInfo, setAccessTokensToServers,
|
createUser,
|
||||||
uploadVideo, userLogin
|
deleteVideoAbuse,
|
||||||
|
flushTests,
|
||||||
|
killallServers,
|
||||||
|
makeGetRequest,
|
||||||
|
makePostBodyRequest,
|
||||||
|
runServer,
|
||||||
|
ServerInfo,
|
||||||
|
setAccessTokensToServers,
|
||||||
|
updateVideoAbuse,
|
||||||
|
uploadVideo,
|
||||||
|
userLogin
|
||||||
} from '../../utils'
|
} from '../../utils'
|
||||||
import { checkBadCountPagination, checkBadSortPagination, checkBadStartPagination } from '../../utils/requests/check-api-params'
|
import { checkBadCountPagination, checkBadSortPagination, checkBadStartPagination } from '../../utils/requests/check-api-params'
|
||||||
|
import { VideoAbuseState } from '../../../../shared/models/videos'
|
||||||
|
|
||||||
describe('Test video abuses API validators', function () {
|
describe('Test video abuses API validators', function () {
|
||||||
let server: ServerInfo
|
let server: ServerInfo
|
||||||
let userAccessToken = ''
|
let userAccessToken = ''
|
||||||
|
let videoAbuseId: number
|
||||||
|
|
||||||
// ---------------------------------------------------------------
|
// ---------------------------------------------------------------
|
||||||
|
|
||||||
|
@ -67,44 +79,111 @@ describe('Test video abuses API validators', function () {
|
||||||
|
|
||||||
describe('When reporting a video abuse', function () {
|
describe('When reporting a video abuse', function () {
|
||||||
const basePath = '/api/v1/videos/'
|
const basePath = '/api/v1/videos/'
|
||||||
|
let path: string
|
||||||
|
|
||||||
|
before(() => {
|
||||||
|
path = basePath + server.video.id + '/abuse'
|
||||||
|
})
|
||||||
|
|
||||||
it('Should fail with nothing', async function () {
|
it('Should fail with nothing', async function () {
|
||||||
const path = basePath + server.video.id + '/abuse'
|
|
||||||
const fields = {}
|
const fields = {}
|
||||||
await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields })
|
await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields })
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Should fail with a wrong video', async function () {
|
it('Should fail with a wrong video', async function () {
|
||||||
const wrongPath = '/api/v1/videos/blabla/abuse'
|
const wrongPath = '/api/v1/videos/blabla/abuse'
|
||||||
const fields = {
|
const fields = { reason: 'my super reason' }
|
||||||
reason: 'my super reason'
|
|
||||||
}
|
|
||||||
await makePostBodyRequest({ url: server.url, path: wrongPath, token: server.accessToken, fields })
|
await makePostBodyRequest({ url: server.url, path: wrongPath, token: server.accessToken, fields })
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Should fail with a non authenticated user', async function () {
|
it('Should fail with a non authenticated user', async function () {
|
||||||
const path = basePath + server.video.id + '/abuse'
|
const fields = { reason: 'my super reason' }
|
||||||
const fields = {
|
|
||||||
reason: 'my super reason'
|
|
||||||
}
|
|
||||||
await makePostBodyRequest({ url: server.url, path, token: 'hello', fields, statusCodeExpected: 401 })
|
await makePostBodyRequest({ url: server.url, path, token: 'hello', fields, statusCodeExpected: 401 })
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Should fail with a reason too short', async function () {
|
it('Should fail with a reason too short', async function () {
|
||||||
const path = basePath + server.video.id + '/abuse'
|
const fields = { reason: 'h' }
|
||||||
const fields = {
|
|
||||||
reason: 'h'
|
|
||||||
}
|
|
||||||
await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields })
|
await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields })
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Should fail with a reason too big', async function () {
|
it('Should fail with a reason too big', async function () {
|
||||||
const path = basePath + server.video.id + '/abuse'
|
const fields = { reason: 'super'.repeat(61) }
|
||||||
const fields = {
|
|
||||||
reason: 'super'.repeat(61)
|
|
||||||
}
|
|
||||||
await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields })
|
await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields })
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('Should succeed with the correct parameters', async function () {
|
||||||
|
const fields = { reason: 'super reason' }
|
||||||
|
|
||||||
|
const res = await makePostBodyRequest({ url: server.url, path, token: server.accessToken, fields, statusCodeExpected: 200 })
|
||||||
|
videoAbuseId = res.body.videoAbuse.id
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('When updating a video abuse', function () {
|
||||||
|
const basePath = '/api/v1/videos/'
|
||||||
|
let path: string
|
||||||
|
|
||||||
|
before(() => {
|
||||||
|
path = basePath + server.video.id + '/abuse/' + videoAbuseId
|
||||||
|
})
|
||||||
|
|
||||||
|
it('Should fail with a non authenticated user', async function () {
|
||||||
|
await updateVideoAbuse(server.url, 'blabla', server.video.uuid, videoAbuseId, {}, 401)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('Should fail with a non admin user', async function () {
|
||||||
|
await updateVideoAbuse(server.url, userAccessToken, server.video.uuid, videoAbuseId, {}, 403)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('Should fail with a bad video id or bad video abuse id', async function () {
|
||||||
|
await updateVideoAbuse(server.url, server.accessToken, server.video.uuid, 45, {}, 404)
|
||||||
|
await updateVideoAbuse(server.url, server.accessToken, 52, videoAbuseId, {}, 404)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('Should fail with a bad state', async function () {
|
||||||
|
const body = { state: 5 }
|
||||||
|
await updateVideoAbuse(server.url, server.accessToken, server.video.uuid, videoAbuseId, body, 400)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('Should fail with a bad moderation comment', async function () {
|
||||||
|
const body = { moderationComment: 'b'.repeat(305) }
|
||||||
|
await updateVideoAbuse(server.url, server.accessToken, server.video.uuid, videoAbuseId, body, 400)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('Should succeed with the correct params', async function () {
|
||||||
|
const body = { state: VideoAbuseState.ACCEPTED }
|
||||||
|
await updateVideoAbuse(server.url, server.accessToken, server.video.uuid, videoAbuseId, body)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('When deleting a video abuse', function () {
|
||||||
|
const basePath = '/api/v1/videos/'
|
||||||
|
let path: string
|
||||||
|
|
||||||
|
before(() => {
|
||||||
|
path = basePath + server.video.id + '/abuse/' + videoAbuseId
|
||||||
|
})
|
||||||
|
|
||||||
|
it('Should fail with a non authenticated user', async function () {
|
||||||
|
await deleteVideoAbuse(server.url, 'blabla', server.video.uuid, videoAbuseId, 401)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('Should fail with a non admin user', async function () {
|
||||||
|
await deleteVideoAbuse(server.url, userAccessToken, server.video.uuid, videoAbuseId, 403)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('Should fail with a bad video id or bad video abuse id', async function () {
|
||||||
|
await deleteVideoAbuse(server.url, server.accessToken, server.video.uuid, 45, 404)
|
||||||
|
await deleteVideoAbuse(server.url, server.accessToken, 52, videoAbuseId, 404)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('Should succeed with the correct params', async function () {
|
||||||
|
await deleteVideoAbuse(server.url, server.accessToken, server.video.uuid, videoAbuseId)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
after(async function () {
|
after(async function () {
|
||||||
|
|
|
@ -2,8 +2,9 @@
|
||||||
|
|
||||||
import * as chai from 'chai'
|
import * as chai from 'chai'
|
||||||
import 'mocha'
|
import 'mocha'
|
||||||
import { VideoAbuse } from '../../../../shared/models/videos'
|
import { VideoAbuse, VideoAbuseState } from '../../../../shared/models/videos'
|
||||||
import {
|
import {
|
||||||
|
deleteVideoAbuse,
|
||||||
flushAndRunMultipleServers,
|
flushAndRunMultipleServers,
|
||||||
getVideoAbusesList,
|
getVideoAbusesList,
|
||||||
getVideosList,
|
getVideosList,
|
||||||
|
@ -11,6 +12,7 @@ import {
|
||||||
reportVideoAbuse,
|
reportVideoAbuse,
|
||||||
ServerInfo,
|
ServerInfo,
|
||||||
setAccessTokensToServers,
|
setAccessTokensToServers,
|
||||||
|
updateVideoAbuse,
|
||||||
uploadVideo
|
uploadVideo
|
||||||
} from '../../utils/index'
|
} from '../../utils/index'
|
||||||
import { doubleFollow } from '../../utils/server/follows'
|
import { doubleFollow } from '../../utils/server/follows'
|
||||||
|
@ -20,6 +22,7 @@ const expect = chai.expect
|
||||||
|
|
||||||
describe('Test video abuses', function () {
|
describe('Test video abuses', function () {
|
||||||
let servers: ServerInfo[] = []
|
let servers: ServerInfo[] = []
|
||||||
|
let abuseServer2: VideoAbuse
|
||||||
|
|
||||||
before(async function () {
|
before(async function () {
|
||||||
this.timeout(50000)
|
this.timeout(50000)
|
||||||
|
@ -105,7 +108,7 @@ describe('Test video abuses', function () {
|
||||||
await waitJobs(servers)
|
await waitJobs(servers)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Should have 2 video abuse on server 1 and 1 on server 2', async function () {
|
it('Should have 2 video abuses on server 1 and 1 on server 2', async function () {
|
||||||
const res1 = await getVideoAbusesList(servers[0].url, servers[0].accessToken)
|
const res1 = await getVideoAbusesList(servers[0].url, servers[0].accessToken)
|
||||||
expect(res1.body.total).to.equal(2)
|
expect(res1.body.total).to.equal(2)
|
||||||
expect(res1.body.data).to.be.an('array')
|
expect(res1.body.data).to.be.an('array')
|
||||||
|
@ -116,22 +119,57 @@ describe('Test video abuses', function () {
|
||||||
expect(abuse1.reporterAccount.name).to.equal('root')
|
expect(abuse1.reporterAccount.name).to.equal('root')
|
||||||
expect(abuse1.reporterAccount.host).to.equal('localhost:9001')
|
expect(abuse1.reporterAccount.host).to.equal('localhost:9001')
|
||||||
expect(abuse1.video.id).to.equal(servers[0].video.id)
|
expect(abuse1.video.id).to.equal(servers[0].video.id)
|
||||||
|
expect(abuse1.state.id).to.equal(VideoAbuseState.PENDING)
|
||||||
|
expect(abuse1.state.label).to.equal('Pending')
|
||||||
|
expect(abuse1.moderationComment).to.be.null
|
||||||
|
|
||||||
const abuse2: VideoAbuse = res1.body.data[1]
|
const abuse2: VideoAbuse = res1.body.data[1]
|
||||||
expect(abuse2.reason).to.equal('my super bad reason 2')
|
expect(abuse2.reason).to.equal('my super bad reason 2')
|
||||||
expect(abuse2.reporterAccount.name).to.equal('root')
|
expect(abuse2.reporterAccount.name).to.equal('root')
|
||||||
expect(abuse2.reporterAccount.host).to.equal('localhost:9001')
|
expect(abuse2.reporterAccount.host).to.equal('localhost:9001')
|
||||||
expect(abuse2.video.id).to.equal(servers[1].video.id)
|
expect(abuse2.video.id).to.equal(servers[1].video.id)
|
||||||
|
expect(abuse2.state.id).to.equal(VideoAbuseState.PENDING)
|
||||||
|
expect(abuse2.state.label).to.equal('Pending')
|
||||||
|
expect(abuse2.moderationComment).to.be.null
|
||||||
|
|
||||||
const res2 = await getVideoAbusesList(servers[1].url, servers[1].accessToken)
|
const res2 = await getVideoAbusesList(servers[1].url, servers[1].accessToken)
|
||||||
expect(res2.body.total).to.equal(1)
|
expect(res2.body.total).to.equal(1)
|
||||||
expect(res2.body.data).to.be.an('array')
|
expect(res2.body.data).to.be.an('array')
|
||||||
expect(res2.body.data.length).to.equal(1)
|
expect(res2.body.data.length).to.equal(1)
|
||||||
|
|
||||||
const abuse3: VideoAbuse = res2.body.data[0]
|
abuseServer2 = res2.body.data[0]
|
||||||
expect(abuse3.reason).to.equal('my super bad reason 2')
|
expect(abuseServer2.reason).to.equal('my super bad reason 2')
|
||||||
expect(abuse3.reporterAccount.name).to.equal('root')
|
expect(abuseServer2.reporterAccount.name).to.equal('root')
|
||||||
expect(abuse3.reporterAccount.host).to.equal('localhost:9001')
|
expect(abuseServer2.reporterAccount.host).to.equal('localhost:9001')
|
||||||
|
expect(abuseServer2.state.id).to.equal(VideoAbuseState.PENDING)
|
||||||
|
expect(abuseServer2.state.label).to.equal('Pending')
|
||||||
|
expect(abuseServer2.moderationComment).to.be.null
|
||||||
|
})
|
||||||
|
|
||||||
|
it('Should update the state of a video abuse', async function () {
|
||||||
|
const body = { state: VideoAbuseState.REJECTED }
|
||||||
|
await updateVideoAbuse(servers[1].url, servers[1].accessToken, abuseServer2.video.uuid, abuseServer2.id, body)
|
||||||
|
|
||||||
|
const res = await getVideoAbusesList(servers[1].url, servers[1].accessToken)
|
||||||
|
expect(res.body.data[0].state.id).to.equal(VideoAbuseState.REJECTED)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('Should add a moderation comment', async function () {
|
||||||
|
const body = { state: VideoAbuseState.ACCEPTED, moderationComment: 'It is valid' }
|
||||||
|
await updateVideoAbuse(servers[1].url, servers[1].accessToken, abuseServer2.video.uuid, abuseServer2.id, body)
|
||||||
|
|
||||||
|
const res = await getVideoAbusesList(servers[1].url, servers[1].accessToken)
|
||||||
|
expect(res.body.data[0].state.id).to.equal(VideoAbuseState.ACCEPTED)
|
||||||
|
expect(res.body.data[0].moderationComment).to.equal('It is valid')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('Should delete the video abuse', async function () {
|
||||||
|
await deleteVideoAbuse(servers[1].url, servers[1].accessToken, abuseServer2.video.uuid, abuseServer2.id)
|
||||||
|
|
||||||
|
const res = await getVideoAbusesList(servers[1].url, servers[1].accessToken)
|
||||||
|
expect(res.body.total).to.equal(0)
|
||||||
|
expect(res.body.data).to.be.an('array')
|
||||||
|
expect(res.body.data.length).to.equal(0)
|
||||||
})
|
})
|
||||||
|
|
||||||
after(async function () {
|
after(async function () {
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
import * as request from 'supertest'
|
import * as request from 'supertest'
|
||||||
|
import { VideoAbuseUpdate } from '../../../../shared/models/videos/video-abuse-update.model'
|
||||||
|
import { makeDeleteRequest, makePutBodyRequest } from '..'
|
||||||
|
|
||||||
function reportVideoAbuse (url: string, token: string, videoId: number | string, reason: string, specialStatus = 204) {
|
function reportVideoAbuse (url: string, token: string, videoId: number | string, reason: string, specialStatus = 200) {
|
||||||
const path = '/api/v1/videos/' + videoId + '/abuse'
|
const path = '/api/v1/videos/' + videoId + '/abuse'
|
||||||
|
|
||||||
return request(url)
|
return request(url)
|
||||||
|
@ -23,9 +25,41 @@ function getVideoAbusesList (url: string, token: string) {
|
||||||
.expect('Content-Type', /json/)
|
.expect('Content-Type', /json/)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function updateVideoAbuse (
|
||||||
|
url: string,
|
||||||
|
token: string,
|
||||||
|
videoId: string | number,
|
||||||
|
videoAbuseId: number,
|
||||||
|
body: VideoAbuseUpdate,
|
||||||
|
statusCodeExpected = 204
|
||||||
|
) {
|
||||||
|
const path = '/api/v1/videos/' + videoId + '/abuse/' + videoAbuseId
|
||||||
|
|
||||||
|
return makePutBodyRequest({
|
||||||
|
url,
|
||||||
|
token,
|
||||||
|
path,
|
||||||
|
fields: body,
|
||||||
|
statusCodeExpected
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function deleteVideoAbuse (url: string, token: string, videoId: string | number, videoAbuseId: number, statusCodeExpected = 204) {
|
||||||
|
const path = '/api/v1/videos/' + videoId + '/abuse/' + videoAbuseId
|
||||||
|
|
||||||
|
return makeDeleteRequest({
|
||||||
|
url,
|
||||||
|
token,
|
||||||
|
path,
|
||||||
|
statusCodeExpected
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
export {
|
export {
|
||||||
reportVideoAbuse,
|
reportVideoAbuse,
|
||||||
getVideoAbusesList
|
getVideoAbusesList,
|
||||||
|
updateVideoAbuse,
|
||||||
|
deleteVideoAbuse
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
export * from './user-video-rate-update.model'
|
export * from './user-video-rate-update.model'
|
||||||
export * from './user-video-rate.model'
|
export * from './user-video-rate.model'
|
||||||
export * from './user-video-rate.type'
|
export * from './user-video-rate.type'
|
||||||
|
export * from './video-abuse-state.model'
|
||||||
export * from './video-abuse-create.model'
|
export * from './video-abuse-create.model'
|
||||||
export * from './video-abuse.model'
|
export * from './video-abuse.model'
|
||||||
export * from './video-blacklist.model'
|
export * from './video-blacklist.model'
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
export enum VideoAbuseState {
|
||||||
|
PENDING = 1,
|
||||||
|
REJECTED = 2,
|
||||||
|
ACCEPTED = 3
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
import { VideoAbuseState } from './video-abuse-state.model'
|
||||||
|
|
||||||
|
export interface VideoAbuseUpdate {
|
||||||
|
moderationComment?: string
|
||||||
|
state?: VideoAbuseState
|
||||||
|
}
|
|
@ -1,14 +1,21 @@
|
||||||
import { Account } from '../actors'
|
import { Account } from '../actors'
|
||||||
|
import { VideoConstant } from './video-constant.model'
|
||||||
|
import { VideoAbuseState } from './video-abuse-state.model'
|
||||||
|
|
||||||
export interface VideoAbuse {
|
export interface VideoAbuse {
|
||||||
id: number
|
id: number
|
||||||
reason: string
|
reason: string
|
||||||
reporterAccount: Account
|
reporterAccount: Account
|
||||||
|
|
||||||
|
state: VideoConstant<VideoAbuseState>
|
||||||
|
moderationComment?: string
|
||||||
|
|
||||||
video: {
|
video: {
|
||||||
id: number
|
id: number
|
||||||
name: string
|
name: string
|
||||||
uuid: string
|
uuid: string
|
||||||
url: string
|
url: string
|
||||||
}
|
}
|
||||||
|
|
||||||
createdAt: Date
|
createdAt: Date
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue