1 import express, { 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'
17 MCommentAbuseAccountVideo,
21 MVideoAccountLightBlacklistAllFiles
22 } from '@server/types/models'
23 import { LiveVideoCreate, VideoCreate, VideoImportCreate } from '../../shared/models/videos'
24 import { VideoCommentCreate } from '../../shared/models/videos/comment'
25 import { UserModel } from '../models/user/user'
26 import { VideoModel } from '../models/video/video'
27 import { VideoCommentModel } from '../models/video/video-comment'
28 import { sendAbuse } from './activitypub/send/send-flag'
29 import { Notifier } from './notifier'
31 export type AcceptResult = {
36 // ---------------------------------------------------------------------------
38 // Stub function that can be filtered by plugins
39 function isLocalVideoAccepted (object: {
40 videoBody: VideoCreate
41 videoFile: VideoUploadFile
44 return { accepted: true }
47 // ---------------------------------------------------------------------------
49 // Stub function that can be filtered by plugins
50 function isLocalLiveVideoAccepted (object: {
51 liveVideoBody: LiveVideoCreate
54 return { accepted: true }
57 // ---------------------------------------------------------------------------
59 // Stub function that can be filtered by plugins
60 function isLocalVideoThreadAccepted (_object: {
62 commentBody: VideoCommentCreate
66 return { accepted: true }
69 // Stub function that can be filtered by plugins
70 function isLocalVideoCommentReplyAccepted (_object: {
72 commentBody: VideoCommentCreate
73 parentComment: VideoCommentModel
77 return { accepted: true }
80 // ---------------------------------------------------------------------------
82 // Stub function that can be filtered by plugins
83 function isRemoteVideoCommentAccepted (_object: {
86 return { accepted: true }
89 // ---------------------------------------------------------------------------
91 // Stub function that can be filtered by plugins
92 function isPreImportVideoAccepted (object: {
93 videoImportBody: VideoImportCreate
96 return { accepted: true }
99 // Stub function that can be filtered by plugins
100 function isPostImportVideoAccepted (object: {
101 videoFilePath: PathLike
102 videoFile: VideoFileModel
105 return { accepted: true }
108 // ---------------------------------------------------------------------------
110 async function createVideoAbuse (options: {
111 baseAbuse: FilteredModelAttributes<AbuseModel>
112 videoInstance: MVideoAccountLightBlacklistAllFiles
115 transaction: Transaction
116 reporterAccount: MAccountDefault
117 skipNotification: boolean
119 const { baseAbuse, videoInstance, startAt, endAt, transaction, reporterAccount, skipNotification } = options
121 const associateFun = async (abuseInstance: MAbuseFull) => {
122 const videoAbuseInstance: MVideoAbuseVideoFull = await VideoAbuseModel.create({
123 abuseId: abuseInstance.id,
124 videoId: videoInstance.id,
129 videoAbuseInstance.Video = videoInstance
130 abuseInstance.VideoAbuse = videoAbuseInstance
132 return { isOwned: videoInstance.isOwned() }
138 flaggedAccount: videoInstance.VideoChannel.Account,
145 function createVideoCommentAbuse (options: {
146 baseAbuse: FilteredModelAttributes<AbuseModel>
147 commentInstance: MCommentOwnerVideo
148 transaction: Transaction
149 reporterAccount: MAccountDefault
150 skipNotification: boolean
152 const { baseAbuse, commentInstance, transaction, reporterAccount, skipNotification } = options
154 const associateFun = async (abuseInstance: MAbuseFull) => {
155 const commentAbuseInstance: MCommentAbuseAccountVideo = await VideoCommentAbuseModel.create({
156 abuseId: abuseInstance.id,
157 videoCommentId: commentInstance.id
160 commentAbuseInstance.VideoComment = commentInstance
161 abuseInstance.VideoCommentAbuse = commentAbuseInstance
163 return { isOwned: commentInstance.isOwned() }
169 flaggedAccount: commentInstance.Account,
176 function createAccountAbuse (options: {
177 baseAbuse: FilteredModelAttributes<AbuseModel>
178 accountInstance: MAccountDefault
179 transaction: Transaction
180 reporterAccount: MAccountDefault
181 skipNotification: boolean
183 const { baseAbuse, accountInstance, transaction, reporterAccount, skipNotification } = options
185 const associateFun = () => {
186 return Promise.resolve({ isOwned: accountInstance.isOwned() })
192 flaggedAccount: accountInstance,
199 // ---------------------------------------------------------------------------
202 isLocalLiveVideoAccepted,
204 isLocalVideoAccepted,
205 isLocalVideoThreadAccepted,
206 isRemoteVideoCommentAccepted,
207 isLocalVideoCommentReplyAccepted,
208 isPreImportVideoAccepted,
209 isPostImportVideoAccepted,
213 createVideoCommentAbuse,
217 // ---------------------------------------------------------------------------
219 async function createAbuse (options: {
220 base: FilteredModelAttributes<AbuseModel>
221 reporterAccount: MAccountDefault
222 flaggedAccount: MAccountLight
223 associateFun: (abuseInstance: MAbuseFull) => Promise<{ isOwned: boolean }>
224 skipNotification: boolean
225 transaction: Transaction
227 const { base, reporterAccount, flaggedAccount, associateFun, transaction, skipNotification } = options
228 const auditLogger = auditLoggerFactory('abuse')
230 const abuseAttributes = Object.assign({}, base, { flaggedAccountId: flaggedAccount.id })
231 const abuseInstance: MAbuseFull = await AbuseModel.create(abuseAttributes, { transaction })
233 abuseInstance.ReporterAccount = reporterAccount
234 abuseInstance.FlaggedAccount = flaggedAccount
236 const { isOwned } = await associateFun(abuseInstance)
238 if (isOwned === false) {
239 sendAbuse(reporterAccount.Actor, abuseInstance, abuseInstance.FlaggedAccount, transaction)
242 const abuseJSON = abuseInstance.toFormattedAdminJSON()
243 auditLogger.create(reporterAccount.Actor.getIdentifier(), new AbuseAuditView(abuseJSON))
245 if (!skipNotification) {
246 afterCommitIfTransaction(transaction, () => {
247 Notifier.Instance.notifyOnNewAbuse({
250 reporter: reporterAccount.Actor.getIdentifier()
255 logger.info('Abuse report %d created.', abuseInstance.id)