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