da8c1577cc8753c4e170955ea1391868472ba842
[oweals/peertube.git] / server / models / video / video-abuse.ts
1 import { AllowNull, BelongsTo, Column, CreatedAt, DataType, Default, ForeignKey, Is, Model, Table, UpdatedAt } from 'sequelize-typescript'
2 import { VideoAbuseObject } from '../../../shared/models/activitypub/objects'
3 import { VideoAbuse } from '../../../shared/models/videos'
4 import {
5   isVideoAbuseModerationCommentValid,
6   isVideoAbuseReasonValid,
7   isVideoAbuseStateValid
8 } from '../../helpers/custom-validators/video-abuses'
9 import { AccountModel } from '../account/account'
10 import { buildBlockedAccountSQL, getSort, throwIfNotValid } from '../utils'
11 import { VideoModel } from './video'
12 import { VideoAbuseState } from '../../../shared'
13 import { CONSTRAINTS_FIELDS, VIDEO_ABUSE_STATES } from '../../initializers/constants'
14 import { MUserAccountId, MVideoAbuse, MVideoAbuseFormattable, MVideoAbuseVideo } from '../../typings/models'
15 import * as Bluebird from 'bluebird'
16 import { literal, Op } from 'sequelize'
17
18 @Table({
19   tableName: 'videoAbuse',
20   indexes: [
21     {
22       fields: [ 'videoId' ]
23     },
24     {
25       fields: [ 'reporterAccountId' ]
26     }
27   ]
28 })
29 export class VideoAbuseModel extends Model<VideoAbuseModel> {
30
31   @AllowNull(false)
32   @Default(null)
33   @Is('VideoAbuseReason', value => throwIfNotValid(value, isVideoAbuseReasonValid, 'reason'))
34   @Column(DataType.STRING(CONSTRAINTS_FIELDS.VIDEO_ABUSES.REASON.max))
35   reason: string
36
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)
45   @Is('VideoAbuseModerationComment', value => throwIfNotValid(value, isVideoAbuseModerationCommentValid, 'moderationComment', true))
46   @Column(DataType.STRING(CONSTRAINTS_FIELDS.VIDEO_ABUSES.MODERATION_COMMENT.max))
47   moderationComment: string
48
49   @CreatedAt
50   createdAt: Date
51
52   @UpdatedAt
53   updatedAt: Date
54
55   @ForeignKey(() => AccountModel)
56   @Column
57   reporterAccountId: number
58
59   @BelongsTo(() => AccountModel, {
60     foreignKey: {
61       allowNull: false
62     },
63     onDelete: 'cascade'
64   })
65   Account: AccountModel
66
67   @ForeignKey(() => VideoModel)
68   @Column
69   videoId: number
70
71   @BelongsTo(() => VideoModel, {
72     foreignKey: {
73       allowNull: false
74     },
75     onDelete: 'cascade'
76   })
77   Video: VideoModel
78
79   static loadByIdAndVideoId (id: number, videoId: number): Bluebird<MVideoAbuse> {
80     const query = {
81       where: {
82         id,
83         videoId
84       }
85     }
86     return VideoAbuseModel.findOne(query)
87   }
88
89   static listForApi (parameters: {
90     start: number
91     count: number
92     sort: string
93     serverAccountId: number
94     user?: MUserAccountId
95   }) {
96     const { start, count, sort, user, serverAccountId } = parameters
97     const userAccountId = user ? user.Account.id : undefined
98
99     const query = {
100       offset: start,
101       limit: count,
102       order: getSort(sort),
103       where: {
104         reporterAccountId: {
105           [Op.notIn]: literal('(' + buildBlockedAccountSQL(serverAccountId, userAccountId) + ')')
106         }
107       },
108       include: [
109         {
110           model: AccountModel,
111           required: true
112         },
113         {
114           model: VideoModel,
115           required: true
116         }
117       ]
118     }
119
120     return VideoAbuseModel.findAndCountAll(query)
121       .then(({ rows, count }) => {
122         return { total: count, data: rows }
123       })
124   }
125
126   toFormattedJSON (this: MVideoAbuseFormattable): VideoAbuse {
127     return {
128       id: this.id,
129       reason: this.reason,
130       reporterAccount: this.Account.toFormattedJSON(),
131       state: {
132         id: this.state,
133         label: VideoAbuseModel.getStateLabel(this.state)
134       },
135       moderationComment: this.moderationComment,
136       video: {
137         id: this.Video.id,
138         uuid: this.Video.uuid,
139         name: this.Video.name
140       },
141       createdAt: this.createdAt
142     }
143   }
144
145   toActivityPubObject (this: MVideoAbuseVideo): VideoAbuseObject {
146     return {
147       type: 'Flag' as 'Flag',
148       content: this.reason,
149       object: this.Video.url
150     }
151   }
152
153   private static getStateLabel (id: number) {
154     return VIDEO_ABUSE_STATES[id] || 'Unknown'
155   }
156 }