Add new abuses tests

This commit is contained in:
Chocobozzz 2020-07-08 15:51:46 +02:00 committed by Chocobozzz
parent 811cef146c
commit 310b5219b3
29 changed files with 869 additions and 386 deletions

View File

@ -138,8 +138,8 @@
<tr> <tr>
<td colspan="6"> <td colspan="6">
<div class="no-results"> <div class="no-results">
<ng-container *ngIf="search" i18n>No video abuses found matching current filters.</ng-container> <ng-container *ngIf="search" i18n>No abuses found matching current filters.</ng-container>
<ng-container *ngIf="!search" i18n>No video abuses found.</ng-container> <ng-container *ngIf="!search" i18n>No abuses found.</ng-container>
</div> </div>
</td> </td>
</tr> </tr>

View File

@ -118,7 +118,7 @@ export class UserNotification implements UserNotificationServer {
this.commentUrl = [ this.buildVideoUrl(this.comment.video), { threadId: this.comment.threadId } ] this.commentUrl = [ this.buildVideoUrl(this.comment.video), { threadId: this.comment.threadId } ]
break break
case UserNotificationType.NEW_VIDEO_ABUSE_FOR_MODERATORS: case UserNotificationType.NEW_ABUSE_FOR_MODERATORS:
this.abuseUrl = '/admin/moderation/abuses/list' this.abuseUrl = '/admin/moderation/abuses/list'
if (this.abuse.video) this.videoUrl = this.buildVideoUrl(this.abuse.video) if (this.abuse.video) this.videoUrl = this.buildVideoUrl(this.abuse.video)

View File

@ -42,7 +42,7 @@
</div> </div>
</ng-container> </ng-container>
<ng-container *ngSwitchCase="UserNotificationType.NEW_VIDEO_ABUSE_FOR_MODERATORS"> <ng-container *ngSwitchCase="UserNotificationType.NEW_ABUSE_FOR_MODERATORS">
<my-global-icon iconName="flag" aria-hidden="true"></my-global-icon> <my-global-icon iconName="flag" aria-hidden="true"></my-global-icon>
<div class="message" i18n> <div class="message" i18n>

View File

@ -140,7 +140,6 @@ export class VideoReportComponent extends FormReactive implements OnInit {
const { hasStart, startAt, hasEnd, endAt } = this.form.get('timestamp').value const { hasStart, startAt, hasEnd, endAt } = this.form.get('timestamp').value
this.abuseService.reportVideo({ this.abuseService.reportVideo({
accountId: this.video.account.id,
reason, reason,
predefinedReasons, predefinedReasons,
video: { video: {

View File

@ -100,7 +100,7 @@ async function updateAbuse (req: express.Request, res: express.Response) {
return abuse.save({ transaction: t }) return abuse.save({ transaction: t })
}) })
// Do not send the delete to other instances, we updated OUR copy of this video abuse // Do not send the delete to other instances, we updated OUR copy of this abuse
return res.type('json').status(204).end() return res.type('json').status(204).end()
} }
@ -112,7 +112,7 @@ async function deleteAbuse (req: express.Request, res: express.Response) {
return abuse.destroy({ transaction: t }) return abuse.destroy({ transaction: t })
}) })
// Do not send the delete to other instances, we delete OUR copy of this video abuse // Do not send the delete to other instances, we delete OUR copy of this abuse
return res.type('json').status(204).end() return res.type('json').status(204).end()
} }

View File

@ -50,7 +50,5 @@ async function removeUserHistory (req: express.Request, res: express.Response) {
return UserVideoHistoryModel.removeUserHistoryBefore(user, beforeDate, t) return UserVideoHistoryModel.removeUserHistoryBefore(user, beforeDate, t)
}) })
// Do not send the delete to other instances, we delete OUR copy of this video abuse
return res.type('json').status(204).end() return res.type('json').status(204).end()
} }

View File

@ -3,10 +3,10 @@ import { AbuseFilter, abusePredefinedReasonsMap, AbusePredefinedReasonsString, A
import { ABUSE_STATES, CONSTRAINTS_FIELDS } 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 ABUSES_CONSTRAINTS_FIELDS = CONSTRAINTS_FIELDS.ABUSES
function isAbuseReasonValid (value: string) { function isAbuseReasonValid (value: string) {
return exists(value) && validator.isLength(value, VIDEO_ABUSES_CONSTRAINTS_FIELDS.REASON) return exists(value) && validator.isLength(value, ABUSES_CONSTRAINTS_FIELDS.REASON)
} }
function isAbusePredefinedReasonValid (value: AbusePredefinedReasonsString) { function isAbusePredefinedReasonValid (value: AbusePredefinedReasonsString) {
@ -32,7 +32,7 @@ function isAbuseTimestampCoherent (endAt: number, { req }) {
} }
function isAbuseModerationCommentValid (value: string) { function isAbuseModerationCommentValid (value: string) {
return exists(value) && validator.isLength(value, VIDEO_ABUSES_CONSTRAINTS_FIELDS.MODERATION_COMMENT) return exists(value) && validator.isLength(value, ABUSES_CONSTRAINTS_FIELDS.MODERATION_COMMENT)
} }
function isAbuseStateValid (value: string) { function isAbuseStateValid (value: string) {

View File

@ -68,7 +68,7 @@ async function doesVideoCommentExist (idArg: number | string, video: MVideoId, r
async function doesCommentIdExist (idArg: number | string, res: express.Response) { async function doesCommentIdExist (idArg: number | string, res: express.Response) {
const id = parseInt(idArg + '', 10) const id = parseInt(idArg + '', 10)
const videoComment = await VideoCommentModel.loadById(id) const videoComment = await VideoCommentModel.loadByIdAndPopulateVideoAndAccountAndReply(id)
if (!videoComment) { if (!videoComment) {
res.status(404) res.status(404)
@ -77,7 +77,7 @@ async function doesCommentIdExist (idArg: number | string, res: express.Response
return false return false
} }
res.locals.videoComment = videoComment res.locals.videoCommentFull = videoComment
return true return true
} }

View File

@ -30,7 +30,7 @@ async function doesAbuseExist (abuseId: number | string, res: Response) {
if (!abuse) { if (!abuse) {
res.status(404) res.status(404)
.json({ error: 'Video abuse not found' }) .json({ error: 'Abuse not found' })
return false return false
} }

View File

@ -43,12 +43,10 @@ async function up (utils: {
await utils.sequelize.query(` await utils.sequelize.query(`
CREATE TABLE IF NOT EXISTS "commentAbuse" ( CREATE TABLE IF NOT EXISTS "commentAbuse" (
"id" serial, "id" serial,
"deletedComment" jsonb DEFAULT NULL,
"abuseId" integer NOT NULL REFERENCES "abuse" ("id") ON DELETE CASCADE ON UPDATE CASCADE, "abuseId" integer NOT NULL REFERENCES "abuse" ("id") ON DELETE CASCADE ON UPDATE CASCADE,
"videoCommentId" integer REFERENCES "videoComment" ("id") ON DELETE SET NULL ON UPDATE CASCADE, "videoCommentId" integer REFERENCES "videoComment" ("id") ON DELETE SET NULL ON UPDATE CASCADE,
"createdAt" timestamp WITH time zone NOT NULL, "createdAt" timestamp WITH time zone NOT NULL,
"updatedAt" timestamp WITH time zone NOT NULL, "updatedAt" timestamp WITH time zone NOT NULL,
"commentId" integer REFERENCES "videoComment" ("id") ON DELETE SET NULL ON UPDATE CASCADE,
PRIMARY KEY ("id") PRIMARY KEY ("id")
); );
`) `)

View File

@ -325,6 +325,7 @@ class Emailer {
subject: `New comment abuse report from ${reporter}`, subject: `New comment abuse report from ${reporter}`,
locals: { locals: {
commentUrl, commentUrl,
videoName: comment.Video.name,
isLocal: comment.isOwned(), isLocal: comment.isOwned(),
commentCreatedAt: new Date(comment.createdAt).toLocaleString(), commentCreatedAt: new Date(comment.createdAt).toLocaleString(),
reason: abuse.reason, reason: abuse.reason,

View File

@ -7,7 +7,8 @@ block title
block content block content
p p
| #[a(href=WEBSERVER.URL) #{WEBSERVER.HOST}] received an abuse report for the #{isLocal ? '' : 'remote '}comment " | #[a(href=WEBSERVER.URL) #{WEBSERVER.HOST}] received an abuse report for the #{isLocal ? '' : 'remote '}comment "
a(href=commentUrl) of #{flaggedAccount} a(href=commentUrl) on video #{videoName}
| of #{flaggedAccount}
| created on #{commentCreatedAt} | created on #{commentCreatedAt}
p The reporter, #{reporter}, cited the following reason(s): p The reporter, #{reporter}, cited the following reason(s):

View File

@ -371,7 +371,7 @@ class Notifier {
async function notificationCreator (user: MUserWithNotificationSetting) { async function notificationCreator (user: MUserWithNotificationSetting) {
const notification = await UserNotificationModel.create<UserNotificationModelForApi>({ const notification = await UserNotificationModel.create<UserNotificationModelForApi>({
type: UserNotificationType.NEW_VIDEO_ABUSE_FOR_MODERATORS, type: UserNotificationType.NEW_ABUSE_FOR_MODERATORS,
userId: user.id, userId: user.id,
abuseId: abuse.id abuseId: abuse.id
}) })

View File

@ -128,7 +128,7 @@ const abuseListValidator = [
.custom(exists).withMessage('Should have a valid search'), .custom(exists).withMessage('Should have a valid search'),
query('state') query('state')
.optional() .optional()
.custom(isAbuseStateValid).withMessage('Should have a valid video abuse state'), .custom(isAbuseStateValid).withMessage('Should have a valid abuse state'),
query('videoIs') query('videoIs')
.optional() .optional()
.custom(isAbuseVideoIsValid).withMessage('Should have a valid "video is" attribute'), .custom(isAbuseVideoIsValid).withMessage('Should have a valid "video is" attribute'),

View File

@ -362,8 +362,8 @@ export class AbuseModel extends Model<AbuseModel> {
const countReportsForReporter = this.get('countReportsForReporter') as number const countReportsForReporter = this.get('countReportsForReporter') as number
const countReportsForReportee = this.get('countReportsForReportee') as number const countReportsForReportee = this.get('countReportsForReportee') as number
let video: VideoAbuse let video: VideoAbuse = null
let comment: VideoCommentAbuse let comment: VideoCommentAbuse = null
if (this.VideoAbuse) { if (this.VideoAbuse) {
const abuseModel = this.VideoAbuse const abuseModel = this.VideoAbuse
@ -391,13 +391,13 @@ export class AbuseModel extends Model<AbuseModel> {
if (this.VideoCommentAbuse) { if (this.VideoCommentAbuse) {
const abuseModel = this.VideoCommentAbuse const abuseModel = this.VideoCommentAbuse
const entity = abuseModel.VideoComment || abuseModel.deletedComment const entity = abuseModel.VideoComment
comment = { comment = {
id: entity.id, id: entity.id,
text: entity.text, text: entity.text ?? '',
deleted: !abuseModel.VideoComment, deleted: entity.isDeleted(),
video: { video: {
id: entity.Video.id, id: entity.Video.id,

View File

@ -1,5 +1,4 @@
import { AllowNull, BelongsTo, Column, CreatedAt, DataType, Default, ForeignKey, Model, Table, UpdatedAt } from 'sequelize-typescript' import { BelongsTo, Column, CreatedAt, ForeignKey, Model, Table, UpdatedAt } from 'sequelize-typescript'
import { VideoComment } from '@shared/models'
import { VideoCommentModel } from '../video/video-comment' import { VideoCommentModel } from '../video/video-comment'
import { AbuseModel } from './abuse' import { AbuseModel } from './abuse'
@ -22,11 +21,6 @@ export class VideoCommentAbuseModel extends Model<VideoCommentAbuseModel> {
@UpdatedAt @UpdatedAt
updatedAt: Date updatedAt: Date
@AllowNull(true)
@Default(null)
@Column(DataType.JSONB)
deletedComment: VideoComment & { Video: { name: string, id: number, uuid: string }}
@ForeignKey(() => AbuseModel) @ForeignKey(() => AbuseModel)
@Column @Column
abuseId: number abuseId: number

View File

@ -109,7 +109,7 @@ function buildAccountInclude (required: boolean, withActor = false) {
required: true, required: true,
include: [ include: [
{ {
attributes: [ 'uuid' ], attributes: [ 'id', 'name', 'uuid' ],
model: VideoModel.unscoped(), model: VideoModel.unscoped(),
required: true required: true
} }
@ -492,6 +492,8 @@ export class UserNotificationModel extends Model<UserNotificationModel> {
threadId: abuse.VideoCommentAbuse.VideoComment.getThreadId(), threadId: abuse.VideoCommentAbuse.VideoComment.getThreadId(),
video: { video: {
id: abuse.VideoCommentAbuse.VideoComment.Video.id,
name: abuse.VideoCommentAbuse.VideoComment.Video.name,
uuid: abuse.VideoCommentAbuse.VideoComment.Video.uuid uuid: abuse.VideoCommentAbuse.VideoComment.Video.uuid
} }
} : undefined } : undefined

View File

@ -3,7 +3,6 @@ import { uniq } from 'lodash'
import { FindOptions, Op, Order, ScopeOptions, Sequelize, Transaction } from 'sequelize' import { FindOptions, Op, Order, ScopeOptions, Sequelize, Transaction } from 'sequelize'
import { import {
AllowNull, AllowNull,
BeforeDestroy,
BelongsTo, BelongsTo,
Column, Column,
CreatedAt, CreatedAt,
@ -16,7 +15,6 @@ import {
Table, Table,
UpdatedAt UpdatedAt
} from 'sequelize-typescript' } 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'
@ -242,51 +240,13 @@ export class VideoCommentModel extends Model<VideoCommentModel> {
@HasMany(() => VideoCommentAbuseModel, { @HasMany(() => VideoCommentAbuseModel, {
foreignKey: { foreignKey: {
name: 'commentId', name: 'videoCommentId',
allowNull: true allowNull: true
}, },
onDelete: 'set null' onDelete: 'set null'
}) })
CommentAbuses: VideoCommentAbuseModel[] 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: {

View File

@ -21,9 +21,7 @@ import {
checkBadStartPagination checkBadStartPagination
} from '../../../../shared/extra-utils/requests/check-api-params' } from '../../../../shared/extra-utils/requests/check-api-params'
// FIXME: deprecated in 2.3. Remove this controller describe('Test abuses API validators', function () {
describe('Test video abuses API validators', function () {
const basePath = '/api/v1/abuses/' const basePath = '/api/v1/abuses/'
let server: ServerInfo let server: ServerInfo

File diff suppressed because it is too large Load Diff

View File

@ -3,10 +3,16 @@
import 'mocha' import 'mocha'
import { v4 as uuidv4 } from 'uuid' import { v4 as uuidv4 } from 'uuid'
import { import {
addVideoCommentThread,
addVideoToBlacklist, addVideoToBlacklist,
cleanupTests, cleanupTests,
createUser,
follow, follow,
generateUserAccessToken,
getAccount,
getCustomConfig, getCustomConfig,
getVideoCommentThreads,
getVideoIdFromUUID,
immutableAssign, immutableAssign,
MockInstancesIndex, MockInstancesIndex,
registerUser, registerUser,
@ -23,7 +29,9 @@ import { waitJobs } from '../../../../shared/extra-utils/server/jobs'
import { import {
checkAutoInstanceFollowing, checkAutoInstanceFollowing,
CheckerBaseParams, CheckerBaseParams,
checkNewAccountAbuseForModerators,
checkNewBlacklistOnMyVideo, checkNewBlacklistOnMyVideo,
checkNewCommentAbuseForModerators,
checkNewInstanceFollower, checkNewInstanceFollower,
checkNewVideoAbuseForModerators, checkNewVideoAbuseForModerators,
checkNewVideoFromSubscription, checkNewVideoFromSubscription,
@ -91,11 +99,74 @@ describe('Test moderation notifications', function () {
await waitJobs(servers) await waitJobs(servers)
await reportAbuse({ url: servers[1].url, token: servers[1].accessToken, videoId: video.id, reason: 'super reason' }) const videoId = await getVideoIdFromUUID(servers[1].url, video.uuid)
await reportAbuse({ url: servers[1].url, token: servers[1].accessToken, videoId, reason: 'super reason' })
await waitJobs(servers) await waitJobs(servers)
await checkNewVideoAbuseForModerators(baseParams, video.uuid, name, 'presence') await checkNewVideoAbuseForModerators(baseParams, video.uuid, name, 'presence')
}) })
it('Should send a notification to moderators on local comment abuse', async function () {
this.timeout(10000)
const name = 'video for abuse ' + uuidv4()
const resVideo = await uploadVideo(servers[0].url, userAccessToken, { name })
const video = resVideo.body.video
const resComment = await addVideoCommentThread(servers[0].url, userAccessToken, video.id, 'comment abuse ' + uuidv4())
const comment = resComment.body.comment
await reportAbuse({ url: servers[0].url, token: servers[0].accessToken, commentId: comment.id, reason: 'super reason' })
await waitJobs(servers)
await checkNewCommentAbuseForModerators(baseParams, video.uuid, name, 'presence')
})
it('Should send a notification to moderators on remote comment abuse', async function () {
this.timeout(10000)
const name = 'video for abuse ' + uuidv4()
const resVideo = await uploadVideo(servers[0].url, userAccessToken, { name })
const video = resVideo.body.video
await addVideoCommentThread(servers[0].url, userAccessToken, video.id, 'comment abuse ' + uuidv4())
await waitJobs(servers)
const resComments = await getVideoCommentThreads(servers[1].url, video.uuid, 0, 5)
const commentId = resComments.body.data[0].id
await reportAbuse({ url: servers[1].url, token: servers[1].accessToken, commentId, reason: 'super reason' })
await waitJobs(servers)
await checkNewCommentAbuseForModerators(baseParams, video.uuid, name, 'presence')
})
it('Should send a notification to moderators on local account abuse', async function () {
this.timeout(10000)
const username = 'user' + new Date().getTime()
const resUser = await createUser({ url: servers[0].url, accessToken: servers[0].accessToken, username, password: 'donald' })
const accountId = resUser.body.user.account.id
await reportAbuse({ url: servers[0].url, token: servers[0].accessToken, accountId, reason: 'super reason' })
await waitJobs(servers)
await checkNewAccountAbuseForModerators(baseParams, username, 'presence')
})
it('Should send a notification to moderators on remote account abuse', async function () {
this.timeout(10000)
const username = 'user' + new Date().getTime()
const tmpToken = await generateUserAccessToken(servers[0], username)
await uploadVideo(servers[0].url, tmpToken, { name: 'super video' })
await waitJobs(servers)
const resAccount = await getAccount(servers[1].url, username + '@' + servers[0].host)
await reportAbuse({ url: servers[1].url, token: servers[1].accessToken, accountId: resAccount.body.id, reason: 'super reason' })
await waitJobs(servers)
await checkNewAccountAbuseForModerators(baseParams, username, 'presence')
})
}) })
describe('Video blacklist on my video', function () { describe('Video blacklist on my video', function () {

View File

@ -180,7 +180,7 @@ describe('Test emails', function () {
}) })
}) })
describe('When creating a video abuse', function () { describe('When creating an abuse', function () {
it('Should send the notification email', async function () { it('Should send the notification email', async function () {
this.timeout(10000) this.timeout(10000)

View File

@ -53,7 +53,7 @@ export module UserNotificationIncludes {
Pick<VideoCommentAbuseModel, 'id'> & Pick<VideoCommentAbuseModel, 'id'> &
PickWith<VideoCommentAbuseModel, 'VideoComment', PickWith<VideoCommentAbuseModel, 'VideoComment',
Pick<VideoCommentModel, 'id' | 'originCommentId' | 'getThreadId'> & Pick<VideoCommentModel, 'id' | 'originCommentId' | 'getThreadId'> &
PickWith<VideoCommentModel, 'Video', Pick<VideoModel, 'uuid'>>> PickWith<VideoCommentModel, 'Video', Pick<VideoModel, 'id' | 'name' | 'uuid'>>>
export type AbuseInclude = export type AbuseInclude =
Pick<AbuseModel, 'id'> & Pick<AbuseModel, 'id'> &

View File

@ -91,7 +91,6 @@ declare module 'express' {
accountVideoRate?: MAccountVideoRateAccountVideo accountVideoRate?: MAccountVideoRateAccountVideo
videoComment?: MComment
videoCommentFull?: MCommentOwnerVideoReply videoCommentFull?: MCommentOwnerVideoReply
videoCommentThread?: MComment videoCommentThread?: MComment

View File

@ -57,10 +57,15 @@ function reportAbuse (options: {
function getAbusesList (options: { function getAbusesList (options: {
url: string url: string
token: string token: string
start?: number
count?: number
sort?: string
id?: number id?: number
predefinedReason?: AbusePredefinedReasonsString predefinedReason?: AbusePredefinedReasonsString
search?: string search?: string
filter?: AbuseFilter, filter?: AbuseFilter
state?: AbuseState state?: AbuseState
videoIs?: AbuseVideoIs videoIs?: AbuseVideoIs
searchReporter?: string searchReporter?: string
@ -71,6 +76,9 @@ function getAbusesList (options: {
const { const {
url, url,
token, token,
start,
count,
sort,
id, id,
predefinedReason, predefinedReason,
search, search,
@ -85,13 +93,15 @@ function getAbusesList (options: {
const path = '/api/v1/abuses' const path = '/api/v1/abuses'
const query = { const query = {
sort: 'createdAt',
id, id,
predefinedReason, predefinedReason,
search, search,
state, state,
filter, filter,
videoIs, videoIs,
start,
count,
sort: sort || 'createdAt',
searchReporter, searchReporter,
searchReportee, searchReportee,
searchVideo, searchVideo,

View File

@ -37,8 +37,8 @@ interface ServerInfo {
video?: { video?: {
id: number id: number
uuid: string uuid: string
name: string name?: string
account: { account?: {
name: string name: string
} }
} }

View File

@ -139,13 +139,17 @@ async function checkNotification (
} }
function checkVideo (video: any, videoName?: string, videoUUID?: string) { function checkVideo (video: any, videoName?: string, videoUUID?: string) {
expect(video.name).to.be.a('string') if (videoName) {
expect(video.name).to.not.be.empty expect(video.name).to.be.a('string')
if (videoName) expect(video.name).to.equal(videoName) expect(video.name).to.not.be.empty
expect(video.name).to.equal(videoName)
}
expect(video.uuid).to.be.a('string') if (videoUUID) {
expect(video.uuid).to.not.be.empty expect(video.uuid).to.be.a('string')
if (videoUUID) expect(video.uuid).to.equal(videoUUID) expect(video.uuid).to.not.be.empty
expect(video.uuid).to.equal(videoUUID)
}
expect(video.id).to.be.a('number') expect(video.id).to.be.a('number')
} }
@ -436,7 +440,7 @@ async function checkNewCommentOnMyVideo (base: CheckerBaseParams, uuid: string,
} }
async function checkNewVideoAbuseForModerators (base: CheckerBaseParams, videoUUID: string, videoName: string, type: CheckerType) { async function checkNewVideoAbuseForModerators (base: CheckerBaseParams, videoUUID: string, videoName: string, type: CheckerType) {
const notificationType = UserNotificationType.NEW_VIDEO_ABUSE_FOR_MODERATORS const notificationType = UserNotificationType.NEW_ABUSE_FOR_MODERATORS
function notificationChecker (notification: UserNotification, type: CheckerType) { function notificationChecker (notification: UserNotification, type: CheckerType) {
if (type === 'presence') { if (type === 'presence') {
@ -460,6 +464,56 @@ async function checkNewVideoAbuseForModerators (base: CheckerBaseParams, videoUU
await checkNotification(base, notificationChecker, emailNotificationFinder, type) await checkNotification(base, notificationChecker, emailNotificationFinder, type)
} }
async function checkNewCommentAbuseForModerators (base: CheckerBaseParams, videoUUID: string, videoName: string, type: CheckerType) {
const notificationType = UserNotificationType.NEW_ABUSE_FOR_MODERATORS
function notificationChecker (notification: UserNotification, type: CheckerType) {
if (type === 'presence') {
expect(notification).to.not.be.undefined
expect(notification.type).to.equal(notificationType)
expect(notification.abuse.id).to.be.a('number')
checkVideo(notification.abuse.comment.video, videoName, videoUUID)
} else {
expect(notification).to.satisfy((n: UserNotification) => {
return n === undefined || n.abuse === undefined || n.abuse.comment.video.uuid !== videoUUID
})
}
}
function emailNotificationFinder (email: object) {
const text = email['text']
return text.indexOf(videoUUID) !== -1 && text.indexOf('abuse') !== -1
}
await checkNotification(base, notificationChecker, emailNotificationFinder, type)
}
async function checkNewAccountAbuseForModerators (base: CheckerBaseParams, displayName: string, type: CheckerType) {
const notificationType = UserNotificationType.NEW_ABUSE_FOR_MODERATORS
function notificationChecker (notification: UserNotification, type: CheckerType) {
if (type === 'presence') {
expect(notification).to.not.be.undefined
expect(notification.type).to.equal(notificationType)
expect(notification.abuse.id).to.be.a('number')
expect(notification.abuse.account.displayName).to.equal(displayName)
} else {
expect(notification).to.satisfy((n: UserNotification) => {
return n === undefined || n.abuse === undefined || n.abuse.account.displayName !== displayName
})
}
}
function emailNotificationFinder (email: object) {
const text = email['text']
return text.indexOf(displayName) !== -1 && text.indexOf('abuse') !== -1
}
await checkNotification(base, notificationChecker, emailNotificationFinder, type)
}
async function checkVideoAutoBlacklistForModerators (base: CheckerBaseParams, videoUUID: string, videoName: string, type: CheckerType) { async function checkVideoAutoBlacklistForModerators (base: CheckerBaseParams, videoUUID: string, videoName: string, type: CheckerType) {
const notificationType = UserNotificationType.VIDEO_AUTO_BLACKLIST_FOR_MODERATORS const notificationType = UserNotificationType.VIDEO_AUTO_BLACKLIST_FOR_MODERATORS
@ -541,6 +595,9 @@ async function prepareNotificationsTest (serversCount = 3) {
smtp: { smtp: {
hostname: 'localhost', hostname: 'localhost',
port port
},
signup: {
limit: 20
} }
} }
const servers = await flushAndRunMultipleServers(serversCount, overrideConfig) const servers = await flushAndRunMultipleServers(serversCount, overrideConfig)
@ -623,5 +680,7 @@ export {
markAsReadNotifications, markAsReadNotifications,
getLastNotification, getLastNotification,
checkNewInstanceFollower, checkNewInstanceFollower,
prepareNotificationsTest prepareNotificationsTest,
checkNewCommentAbuseForModerators,
checkNewAccountAbuseForModerators
} }

View File

@ -3,7 +3,7 @@ import { FollowState } from '../actors'
export enum UserNotificationType { export enum UserNotificationType {
NEW_VIDEO_FROM_SUBSCRIPTION = 1, NEW_VIDEO_FROM_SUBSCRIPTION = 1,
NEW_COMMENT_ON_MY_VIDEO = 2, NEW_COMMENT_ON_MY_VIDEO = 2,
NEW_VIDEO_ABUSE_FOR_MODERATORS = 3, NEW_ABUSE_FOR_MODERATORS = 3,
BLACKLIST_ON_MY_VIDEO = 4, BLACKLIST_ON_MY_VIDEO = 4,
UNBLACKLIST_ON_MY_VIDEO = 5, UNBLACKLIST_ON_MY_VIDEO = 5,

View File

@ -106,9 +106,9 @@ tags:
Managing plugins installed from a local path or from NPM, or search for new ones. Managing plugins installed from a local path or from NPM, or search for new ones.
externalDocs: externalDocs:
url: https://docs.joinpeertube.org/#/api-plugins url: https://docs.joinpeertube.org/#/api-plugins
- name: Video Abuses - name: Abuses
description: | description: |
Video abuses deal with reports of local or remote videos alike. Abuses deal with reports of local or remote videos/comments/accounts alike.
- name: Video - name: Video
description: | description: |
Operations dealing with listing, uploading, fetching or modifying videos. Operations dealing with listing, uploading, fetching or modifying videos.
@ -166,7 +166,7 @@ x-tagGroups:
- Search - Search
- name: Moderation - name: Moderation
tags: tags:
- Video Abuses - Abuses
- Video Blocks - Video Blocks
- Account Blocks - Account Blocks
- Server Blocks - Server Blocks
@ -1474,13 +1474,13 @@ paths:
/videos/abuse: /videos/abuse:
get: get:
deprecated: true deprecated: true
summary: List video abuses summary: List abuses
security: security:
- OAuth2: - OAuth2:
- admin - admin
- moderator - moderator
tags: tags:
- Video Abuses - Abuses
parameters: parameters:
- name: id - name: id
in: query in: query
@ -1508,7 +1508,7 @@ paths:
type: string type: string
- name: state - name: state
in: query in: query
description: 'The video playlist privacy (Pending = `1`, Rejected = `2`, Accepted = `3`)' description: 'The abuse state (Pending = `1`, Rejected = `2`, Accepted = `3`)'
schema: schema:
type: integer type: integer
enum: enum:
@ -1554,7 +1554,7 @@ paths:
security: security:
- OAuth2: [] - OAuth2: []
tags: tags:
- Video Abuses - Abuses
- Videos - Videos
parameters: parameters:
- $ref: '#/components/parameters/idOrUUID' - $ref: '#/components/parameters/idOrUUID'
@ -1607,7 +1607,7 @@ paths:
- admin - admin
- moderator - moderator
tags: tags:
- Video Abuses - Abuses
parameters: parameters:
- $ref: '#/components/parameters/idOrUUID' - $ref: '#/components/parameters/idOrUUID'
- $ref: '#/components/parameters/abuseId' - $ref: '#/components/parameters/abuseId'
@ -1626,11 +1626,11 @@ paths:
'204': '204':
description: successful operation description: successful operation
'404': '404':
description: video abuse not found description: abuse not found
delete: delete:
deprecated: true deprecated: true
tags: tags:
- Video Abuses - Abuses
summary: Delete an abuse summary: Delete an abuse
security: security:
- OAuth2: - OAuth2:
@ -3320,7 +3320,7 @@ components:
name: abuseId name: abuseId
in: path in: path
required: true required: true
description: Video abuse id description: Abuse id
schema: schema:
type: integer type: integer
captionLanguage: captionLanguage:
@ -5098,7 +5098,7 @@ components:
- `2` NEW_COMMENT_ON_MY_VIDEO - `2` NEW_COMMENT_ON_MY_VIDEO
- `3` NEW_VIDEO_ABUSE_FOR_MODERATORS - `3` NEW_ABUSE_FOR_MODERATORS
- `4` BLACKLIST_ON_MY_VIDEO - `4` BLACKLIST_ON_MY_VIDEO