]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blob - server/lib/moderation.ts
Merge branch 'release/3.3.0' into develop
[github/Chocobozzz/PeerTube.git] / server / lib / moderation.ts
1 import { VideoUploadFile } from 'express'
2 import { PathLike } from 'fs-extra'
3 import { Transaction } from 'sequelize/types'
4 import { AbuseAuditView, auditLoggerFactory } from '@server/helpers/audit-logger'
5 import { afterCommitIfTransaction } from '@server/helpers/database-utils'
6 import { logger } from '@server/helpers/logger'
7 import { AbuseModel } from '@server/models/abuse/abuse'
8 import { VideoAbuseModel } from '@server/models/abuse/video-abuse'
9 import { VideoCommentAbuseModel } from '@server/models/abuse/video-comment-abuse'
10 import { VideoFileModel } from '@server/models/video/video-file'
11 import { FilteredModelAttributes } from '@server/types'
12 import {
13 MAbuseFull,
14 MAccountDefault,
15 MAccountLight,
16 MCommentAbuseAccountVideo,
17 MCommentOwnerVideo,
18 MUser,
19 MVideoAbuseVideoFull,
20 MVideoAccountLightBlacklistAllFiles
21 } from '@server/types/models'
22 import { ActivityCreate } from '../../shared/models/activitypub'
23 import { VideoObject } from '../../shared/models/activitypub/objects'
24 import { VideoCommentObject } from '../../shared/models/activitypub/objects/video-comment-object'
25 import { LiveVideoCreate, VideoCreate, VideoImportCreate } from '../../shared/models/videos'
26 import { VideoCommentCreate } from '../../shared/models/videos/comment'
27 import { ActorModel } from '../models/actor/actor'
28 import { UserModel } from '../models/user/user'
29 import { VideoModel } from '../models/video/video'
30 import { VideoCommentModel } from '../models/video/video-comment'
31 import { sendAbuse } from './activitypub/send/send-flag'
32 import { Notifier } from './notifier'
33
34 export type AcceptResult = {
35 accepted: boolean
36 errorMessage?: string
37 }
38
39 // Can be filtered by plugins
40 function isLocalVideoAccepted (object: {
41 videoBody: VideoCreate
42 videoFile: VideoUploadFile
43 user: UserModel
44 }): AcceptResult {
45 return { accepted: true }
46 }
47
48 function isLocalLiveVideoAccepted (object: {
49 liveVideoBody: LiveVideoCreate
50 user: UserModel
51 }): AcceptResult {
52 return { accepted: true }
53 }
54
55 function isLocalVideoThreadAccepted (_object: {
56 commentBody: VideoCommentCreate
57 video: VideoModel
58 user: UserModel
59 }): AcceptResult {
60 return { accepted: true }
61 }
62
63 function isLocalVideoCommentReplyAccepted (_object: {
64 commentBody: VideoCommentCreate
65 parentComment: VideoCommentModel
66 video: VideoModel
67 user: UserModel
68 }): AcceptResult {
69 return { accepted: true }
70 }
71
72 function isRemoteVideoAccepted (_object: {
73 activity: ActivityCreate
74 videoAP: VideoObject
75 byActor: ActorModel
76 }): AcceptResult {
77 return { accepted: true }
78 }
79
80 function isRemoteVideoCommentAccepted (_object: {
81 activity: ActivityCreate
82 commentAP: VideoCommentObject
83 byActor: ActorModel
84 }): AcceptResult {
85 return { accepted: true }
86 }
87
88 function isPreImportVideoAccepted (object: {
89 videoImportBody: VideoImportCreate
90 user: MUser
91 }): AcceptResult {
92 return { accepted: true }
93 }
94
95 function isPostImportVideoAccepted (object: {
96 videoFilePath: PathLike
97 videoFile: VideoFileModel
98 user: MUser
99 }): AcceptResult {
100 return { accepted: true }
101 }
102
103 async function createVideoAbuse (options: {
104 baseAbuse: FilteredModelAttributes<AbuseModel>
105 videoInstance: MVideoAccountLightBlacklistAllFiles
106 startAt: number
107 endAt: number
108 transaction: Transaction
109 reporterAccount: MAccountDefault
110 }) {
111 const { baseAbuse, videoInstance, startAt, endAt, transaction, reporterAccount } = options
112
113 const associateFun = async (abuseInstance: MAbuseFull) => {
114 const videoAbuseInstance: MVideoAbuseVideoFull = await VideoAbuseModel.create({
115 abuseId: abuseInstance.id,
116 videoId: videoInstance.id,
117 startAt: startAt,
118 endAt: endAt
119 }, { transaction })
120
121 videoAbuseInstance.Video = videoInstance
122 abuseInstance.VideoAbuse = videoAbuseInstance
123
124 return { isOwned: videoInstance.isOwned() }
125 }
126
127 return createAbuse({
128 base: baseAbuse,
129 reporterAccount,
130 flaggedAccount: videoInstance.VideoChannel.Account,
131 transaction,
132 associateFun
133 })
134 }
135
136 function createVideoCommentAbuse (options: {
137 baseAbuse: FilteredModelAttributes<AbuseModel>
138 commentInstance: MCommentOwnerVideo
139 transaction: Transaction
140 reporterAccount: MAccountDefault
141 }) {
142 const { baseAbuse, commentInstance, transaction, reporterAccount } = options
143
144 const associateFun = async (abuseInstance: MAbuseFull) => {
145 const commentAbuseInstance: MCommentAbuseAccountVideo = await VideoCommentAbuseModel.create({
146 abuseId: abuseInstance.id,
147 videoCommentId: commentInstance.id
148 }, { transaction })
149
150 commentAbuseInstance.VideoComment = commentInstance
151 abuseInstance.VideoCommentAbuse = commentAbuseInstance
152
153 return { isOwned: commentInstance.isOwned() }
154 }
155
156 return createAbuse({
157 base: baseAbuse,
158 reporterAccount,
159 flaggedAccount: commentInstance.Account,
160 transaction,
161 associateFun
162 })
163 }
164
165 function createAccountAbuse (options: {
166 baseAbuse: FilteredModelAttributes<AbuseModel>
167 accountInstance: MAccountDefault
168 transaction: Transaction
169 reporterAccount: MAccountDefault
170 }) {
171 const { baseAbuse, accountInstance, transaction, reporterAccount } = options
172
173 const associateFun = async () => {
174 return { isOwned: accountInstance.isOwned() }
175 }
176
177 return createAbuse({
178 base: baseAbuse,
179 reporterAccount,
180 flaggedAccount: accountInstance,
181 transaction,
182 associateFun
183 })
184 }
185
186 export {
187 isLocalLiveVideoAccepted,
188
189 isLocalVideoAccepted,
190 isLocalVideoThreadAccepted,
191 isRemoteVideoAccepted,
192 isRemoteVideoCommentAccepted,
193 isLocalVideoCommentReplyAccepted,
194 isPreImportVideoAccepted,
195 isPostImportVideoAccepted,
196
197 createAbuse,
198 createVideoAbuse,
199 createVideoCommentAbuse,
200 createAccountAbuse
201 }
202
203 // ---------------------------------------------------------------------------
204
205 async function createAbuse (options: {
206 base: FilteredModelAttributes<AbuseModel>
207 reporterAccount: MAccountDefault
208 flaggedAccount: MAccountLight
209 associateFun: (abuseInstance: MAbuseFull) => Promise<{ isOwned: boolean} >
210 transaction: Transaction
211 }) {
212 const { base, reporterAccount, flaggedAccount, associateFun, transaction } = options
213 const auditLogger = auditLoggerFactory('abuse')
214
215 const abuseAttributes = Object.assign({}, base, { flaggedAccountId: flaggedAccount.id })
216 const abuseInstance: MAbuseFull = await AbuseModel.create(abuseAttributes, { transaction })
217
218 abuseInstance.ReporterAccount = reporterAccount
219 abuseInstance.FlaggedAccount = flaggedAccount
220
221 const { isOwned } = await associateFun(abuseInstance)
222
223 if (isOwned === false) {
224 sendAbuse(reporterAccount.Actor, abuseInstance, abuseInstance.FlaggedAccount, transaction)
225 }
226
227 const abuseJSON = abuseInstance.toFormattedAdminJSON()
228 auditLogger.create(reporterAccount.Actor.getIdentifier(), new AbuseAuditView(abuseJSON))
229
230 afterCommitIfTransaction(transaction, () => {
231 Notifier.Instance.notifyOnNewAbuse({
232 abuse: abuseJSON,
233 abuseInstance,
234 reporter: reporterAccount.Actor.getIdentifier()
235 })
236 })
237
238 logger.info('Abuse report %d created.', abuseInstance.id)
239
240 return abuseJSON
241 }