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'
16 MCommentAbuseAccountVideo,
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'
34 export type AcceptResult = {
39 // Can be filtered by plugins
40 function isLocalVideoAccepted (object: {
41 videoBody: VideoCreate
42 videoFile: VideoUploadFile
45 return { accepted: true }
48 function isLocalLiveVideoAccepted (object: {
49 liveVideoBody: LiveVideoCreate
52 return { accepted: true }
55 function isLocalVideoThreadAccepted (_object: {
56 commentBody: VideoCommentCreate
60 return { accepted: true }
63 function isLocalVideoCommentReplyAccepted (_object: {
64 commentBody: VideoCommentCreate
65 parentComment: VideoCommentModel
69 return { accepted: true }
72 function isRemoteVideoAccepted (_object: {
73 activity: ActivityCreate
77 return { accepted: true }
80 function isRemoteVideoCommentAccepted (_object: {
81 activity: ActivityCreate
82 commentAP: VideoCommentObject
85 return { accepted: true }
88 function isPreImportVideoAccepted (object: {
89 videoImportBody: VideoImportCreate
92 return { accepted: true }
95 function isPostImportVideoAccepted (object: {
96 videoFilePath: PathLike
97 videoFile: VideoFileModel
100 return { accepted: true }
103 async function createVideoAbuse (options: {
104 baseAbuse: FilteredModelAttributes<AbuseModel>
105 videoInstance: MVideoAccountLightBlacklistAllFiles
108 transaction: Transaction
109 reporterAccount: MAccountDefault
110 skipNotification: boolean
112 const { baseAbuse, videoInstance, startAt, endAt, transaction, reporterAccount, skipNotification } = options
114 const associateFun = async (abuseInstance: MAbuseFull) => {
115 const videoAbuseInstance: MVideoAbuseVideoFull = await VideoAbuseModel.create({
116 abuseId: abuseInstance.id,
117 videoId: videoInstance.id,
122 videoAbuseInstance.Video = videoInstance
123 abuseInstance.VideoAbuse = videoAbuseInstance
125 return { isOwned: videoInstance.isOwned() }
131 flaggedAccount: videoInstance.VideoChannel.Account,
138 function createVideoCommentAbuse (options: {
139 baseAbuse: FilteredModelAttributes<AbuseModel>
140 commentInstance: MCommentOwnerVideo
141 transaction: Transaction
142 reporterAccount: MAccountDefault
143 skipNotification: boolean
145 const { baseAbuse, commentInstance, transaction, reporterAccount, skipNotification } = options
147 const associateFun = async (abuseInstance: MAbuseFull) => {
148 const commentAbuseInstance: MCommentAbuseAccountVideo = await VideoCommentAbuseModel.create({
149 abuseId: abuseInstance.id,
150 videoCommentId: commentInstance.id
153 commentAbuseInstance.VideoComment = commentInstance
154 abuseInstance.VideoCommentAbuse = commentAbuseInstance
156 return { isOwned: commentInstance.isOwned() }
162 flaggedAccount: commentInstance.Account,
169 function createAccountAbuse (options: {
170 baseAbuse: FilteredModelAttributes<AbuseModel>
171 accountInstance: MAccountDefault
172 transaction: Transaction
173 reporterAccount: MAccountDefault
174 skipNotification: boolean
176 const { baseAbuse, accountInstance, transaction, reporterAccount, skipNotification } = options
178 const associateFun = () => {
179 return Promise.resolve({ isOwned: accountInstance.isOwned() })
185 flaggedAccount: accountInstance,
193 isLocalLiveVideoAccepted,
195 isLocalVideoAccepted,
196 isLocalVideoThreadAccepted,
197 isRemoteVideoAccepted,
198 isRemoteVideoCommentAccepted,
199 isLocalVideoCommentReplyAccepted,
200 isPreImportVideoAccepted,
201 isPostImportVideoAccepted,
205 createVideoCommentAbuse,
209 // ---------------------------------------------------------------------------
211 async function createAbuse (options: {
212 base: FilteredModelAttributes<AbuseModel>
213 reporterAccount: MAccountDefault
214 flaggedAccount: MAccountLight
215 associateFun: (abuseInstance: MAbuseFull) => Promise<{ isOwned: boolean} >
216 skipNotification: boolean
217 transaction: Transaction
219 const { base, reporterAccount, flaggedAccount, associateFun, transaction, skipNotification } = options
220 const auditLogger = auditLoggerFactory('abuse')
222 const abuseAttributes = Object.assign({}, base, { flaggedAccountId: flaggedAccount.id })
223 const abuseInstance: MAbuseFull = await AbuseModel.create(abuseAttributes, { transaction })
225 abuseInstance.ReporterAccount = reporterAccount
226 abuseInstance.FlaggedAccount = flaggedAccount
228 const { isOwned } = await associateFun(abuseInstance)
230 if (isOwned === false) {
231 sendAbuse(reporterAccount.Actor, abuseInstance, abuseInstance.FlaggedAccount, transaction)
234 const abuseJSON = abuseInstance.toFormattedAdminJSON()
235 auditLogger.create(reporterAccount.Actor.getIdentifier(), new AbuseAuditView(abuseJSON))
237 if (!skipNotification) {
238 afterCommitIfTransaction(transaction, () => {
239 Notifier.Instance.notifyOnNewAbuse({
242 reporter: reporterAccount.Actor.getIdentifier()
247 logger.info('Abuse report %d created.', abuseInstance.id)