2 AllowNull, BelongsTo, Column, CreatedAt, DataType, Default, ForeignKey, Is, Model, Table, UpdatedAt, DefaultScope
3 } from 'sequelize-typescript'
4 import { VideoAbuseObject } from '../../../shared/models/activitypub/objects'
5 import { VideoAbuse } from '../../../shared/models/videos'
7 isVideoAbuseModerationCommentValid,
8 isVideoAbuseReasonValid,
10 } from '../../helpers/custom-validators/video-abuses'
11 import { AccountModel } from '../account/account'
12 import { buildBlockedAccountSQL, getSort, throwIfNotValid } from '../utils'
13 import { VideoModel } from './video'
14 import { VideoAbuseState, Video } from '../../../shared'
15 import { CONSTRAINTS_FIELDS, VIDEO_ABUSE_STATES } from '../../initializers/constants'
16 import { MUserAccountId, MVideoAbuse, MVideoAbuseFormattable, MVideoAbuseVideo } from '../../typings/models'
17 import * as Bluebird from 'bluebird'
18 import { literal, Op } from 'sequelize'
19 import { ThumbnailModel } from './thumbnail'
20 import { VideoChannelModel } from './video-channel'
21 import { ActorModel } from '../activitypub/actor'
22 import { VideoBlacklistModel } from './video-blacklist'
24 @DefaultScope(() => ({
38 model: VideoChannelModel.unscoped(),
46 attributes: [ 'id', 'reason', 'unfederated' ],
47 model: VideoBlacklistModel
54 tableName: 'videoAbuse',
60 fields: [ 'reporterAccountId' ]
64 export class VideoAbuseModel extends Model<VideoAbuseModel> {
68 @Is('VideoAbuseReason', value => throwIfNotValid(value, isVideoAbuseReasonValid, 'reason'))
69 @Column(DataType.STRING(CONSTRAINTS_FIELDS.VIDEO_ABUSES.REASON.max))
74 @Is('VideoAbuseState', value => throwIfNotValid(value, isVideoAbuseStateValid, 'state'))
76 state: VideoAbuseState
80 @Is('VideoAbuseModerationComment', value => throwIfNotValid(value, isVideoAbuseModerationCommentValid, 'moderationComment', true))
81 @Column(DataType.STRING(CONSTRAINTS_FIELDS.VIDEO_ABUSES.MODERATION_COMMENT.max))
82 moderationComment: string
86 @Column(DataType.JSONB)
95 @ForeignKey(() => AccountModel)
97 reporterAccountId: number
99 @BelongsTo(() => AccountModel, {
105 Account: AccountModel
107 @ForeignKey(() => VideoModel)
111 @BelongsTo(() => VideoModel, {
119 static loadByIdAndVideoId (id: number, videoId?: number, uuid?: string): Bluebird<MVideoAbuse> {
120 const videoAttributes = {}
121 if (videoId) videoAttributes['videoId'] = videoId
122 if (uuid) videoAttributes['deletedVideo'] = { uuid }
130 return VideoAbuseModel.findOne(query)
133 static listForApi (parameters: {
137 serverAccountId: number
138 user?: MUserAccountId
140 const { start, count, sort, user, serverAccountId } = parameters
141 const userAccountId = user ? user.Account.id : undefined
146 order: getSort(sort),
149 [Op.notIn]: literal('(' + buildBlockedAccountSQL(serverAccountId, userAccountId) + ')')
152 col: 'VideoAbuseModel.id',
156 return VideoAbuseModel.findAndCountAll(query)
157 .then(({ rows, count }) => {
158 return { total: count, data: rows }
162 toFormattedJSON (this: MVideoAbuseFormattable): VideoAbuse {
163 const video = this.Video
170 reporterAccount: this.Account.toFormattedJSON(),
173 label: VideoAbuseModel.getStateLabel(this.state)
175 moderationComment: this.moderationComment,
181 deleted: !this.Video,
182 blacklisted: this.Video && this.Video.isBlacklisted(),
183 thumbnailPath: this.Video?.getMiniatureStaticPath(),
184 channel: this.Video?.VideoChannel.toFormattedSummaryJSON() || this.deletedVideo?.channel
186 createdAt: this.createdAt
190 toActivityPubObject (this: MVideoAbuseVideo): VideoAbuseObject {
192 type: 'Flag' as 'Flag',
193 content: this.reason,
194 object: this.Video.url
198 private static getStateLabel (id: number) {
199 return VIDEO_ABUSE_STATES[id] || 'Unknown'