]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blame - server/models/video/video-abuse.ts
Make sure a report doesn't get deleted upon the deletion of its video
[github/Chocobozzz/PeerTube.git] / server / models / video / video-abuse.ts
CommitLineData
1506307f 1import { AllowNull, BelongsTo, Column, CreatedAt, DataType, Default, ForeignKey, Is, Model, Table, UpdatedAt } from 'sequelize-typescript'
3fd3ab2d 2import { VideoAbuseObject } from '../../../shared/models/activitypub/objects'
19a3b914 3import { VideoAbuse } from '../../../shared/models/videos'
268eebed
C
4import {
5 isVideoAbuseModerationCommentValid,
6 isVideoAbuseReasonValid,
7 isVideoAbuseStateValid
8} from '../../helpers/custom-validators/video-abuses'
3fd3ab2d 9import { AccountModel } from '../account/account'
f0a47bc9 10import { buildBlockedAccountSQL, getSort, throwIfNotValid } from '../utils'
3fd3ab2d 11import { VideoModel } from './video'
68d19a0a 12import { VideoAbuseState, Video } from '../../../shared'
74dc3bca 13import { CONSTRAINTS_FIELDS, VIDEO_ABUSE_STATES } from '../../initializers/constants'
f0a47bc9 14import { MUserAccountId, MVideoAbuse, MVideoAbuseFormattable, MVideoAbuseVideo } from '../../typings/models'
453e83ea 15import * as Bluebird from 'bluebird'
f0a47bc9 16import { literal, Op } from 'sequelize'
3fd3ab2d
C
17
18@Table({
19 tableName: 'videoAbuse',
20 indexes: [
55fa55a9 21 {
3fd3ab2d 22 fields: [ 'videoId' ]
55fa55a9
C
23 },
24 {
3fd3ab2d 25 fields: [ 'reporterAccountId' ]
55fa55a9 26 }
e02643f3 27 ]
3fd3ab2d
C
28})
29export class VideoAbuseModel extends Model<VideoAbuseModel> {
e02643f3 30
3fd3ab2d 31 @AllowNull(false)
1506307f 32 @Default(null)
3fd3ab2d 33 @Is('VideoAbuseReason', value => throwIfNotValid(value, isVideoAbuseReasonValid, 'reason'))
1506307f 34 @Column(DataType.STRING(CONSTRAINTS_FIELDS.VIDEO_ABUSES.REASON.max))
3fd3ab2d 35 reason: string
21e0727a 36
268eebed
C
37 @AllowNull(false)
38 @Default(null)
39 @Is('VideoAbuseState', value => throwIfNotValid(value, isVideoAbuseStateValid, 'state'))
40 @Column
41 state: VideoAbuseState
42
43 @AllowNull(true)
44 @Default(null)
1735c825 45 @Is('VideoAbuseModerationComment', value => throwIfNotValid(value, isVideoAbuseModerationCommentValid, 'moderationComment', true))
268eebed
C
46 @Column(DataType.STRING(CONSTRAINTS_FIELDS.VIDEO_ABUSES.MODERATION_COMMENT.max))
47 moderationComment: string
48
68d19a0a
RK
49 @AllowNull(true)
50 @Default(null)
51 @Column(DataType.JSONB)
52 deletedVideo: Video
53
3fd3ab2d
C
54 @CreatedAt
55 createdAt: Date
21e0727a 56
3fd3ab2d
C
57 @UpdatedAt
58 updatedAt: Date
e02643f3 59
3fd3ab2d
C
60 @ForeignKey(() => AccountModel)
61 @Column
62 reporterAccountId: number
55fa55a9 63
3fd3ab2d 64 @BelongsTo(() => AccountModel, {
55fa55a9 65 foreignKey: {
68d19a0a 66 allowNull: true
55fa55a9 67 },
68d19a0a 68 onDelete: 'set null'
55fa55a9 69 })
3fd3ab2d
C
70 Account: AccountModel
71
72 @ForeignKey(() => VideoModel)
73 @Column
74 videoId: number
55fa55a9 75
3fd3ab2d 76 @BelongsTo(() => VideoModel, {
55fa55a9 77 foreignKey: {
68d19a0a 78 allowNull: true
55fa55a9 79 },
68d19a0a 80 onDelete: 'set null'
55fa55a9 81 })
3fd3ab2d
C
82 Video: VideoModel
83
68d19a0a
RK
84 static loadByIdAndVideoId (id: number, videoId?: number, uuid?: string): Bluebird<MVideoAbuse> {
85 const videoAttributes = {}
86 if (videoId) videoAttributes['videoId'] = videoId
87 if (uuid) videoAttributes['deletedVideo'] = { uuid }
88
268eebed
C
89 const query = {
90 where: {
91 id,
68d19a0a 92 ...videoAttributes
268eebed
C
93 }
94 }
95 return VideoAbuseModel.findOne(query)
96 }
97
f0a47bc9 98 static listForApi (parameters: {
a1587156
C
99 start: number
100 count: number
101 sort: string
f0a47bc9
C
102 serverAccountId: number
103 user?: MUserAccountId
104 }) {
105 const { start, count, sort, user, serverAccountId } = parameters
106 const userAccountId = user ? user.Account.id : undefined
107
3fd3ab2d
C
108 const query = {
109 offset: start,
110 limit: count,
3bb6c526 111 order: getSort(sort),
f0a47bc9
C
112 where: {
113 reporterAccountId: {
114 [Op.notIn]: literal('(' + buildBlockedAccountSQL(serverAccountId, userAccountId) + ')')
115 }
116 },
3fd3ab2d
C
117 include: [
118 {
119 model: AccountModel,
50d6de9c 120 required: true
3fd3ab2d
C
121 },
122 {
123 model: VideoModel,
68d19a0a 124 required: false
3fd3ab2d
C
125 }
126 ]
127 }
55fa55a9 128
3fd3ab2d
C
129 return VideoAbuseModel.findAndCountAll(query)
130 .then(({ rows, count }) => {
131 return { total: count, data: rows }
132 })
55fa55a9
C
133 }
134
1ca9f7c3 135 toFormattedJSON (this: MVideoAbuseFormattable): VideoAbuse {
68d19a0a
RK
136 const video = this.Video
137 ? this.Video
138 : this.deletedVideo
139
3fd3ab2d
C
140 return {
141 id: this.id,
142 reason: this.reason,
19a3b914 143 reporterAccount: this.Account.toFormattedJSON(),
268eebed
C
144 state: {
145 id: this.state,
146 label: VideoAbuseModel.getStateLabel(this.state)
147 },
148 moderationComment: this.moderationComment,
19a3b914 149 video: {
68d19a0a
RK
150 id: video.id,
151 uuid: video.uuid,
152 name: video.name,
153 nsfw: video.nsfw,
154 deleted: !this.Video
19a3b914 155 },
3fd3ab2d
C
156 createdAt: this.createdAt
157 }
158 }
159
453e83ea 160 toActivityPubObject (this: MVideoAbuseVideo): VideoAbuseObject {
3fd3ab2d
C
161 return {
162 type: 'Flag' as 'Flag',
163 content: this.reason,
164 object: this.Video.url
165 }
166 }
268eebed
C
167
168 private static getStateLabel (id: number) {
169 return VIDEO_ABUSE_STATES[id] || 'Unknown'
170 }
55fa55a9 171}