moderation.ts 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237
  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. export type AcceptResult = {
  32. accepted: boolean
  33. errorMessage?: string
  34. }
  35. // Can be filtered by plugins
  36. function isLocalVideoAccepted (object: {
  37. videoBody: VideoCreate
  38. videoFile: Express.Multer.File & { duration?: number }
  39. user: UserModel
  40. }): AcceptResult {
  41. return { accepted: true }
  42. }
  43. function isLocalLiveVideoAccepted (object: {
  44. liveVideoBody: LiveVideoCreate
  45. user: UserModel
  46. }): AcceptResult {
  47. return { accepted: true }
  48. }
  49. function isLocalVideoThreadAccepted (_object: {
  50. commentBody: VideoCommentCreate
  51. video: VideoModel
  52. user: UserModel
  53. }): AcceptResult {
  54. return { accepted: true }
  55. }
  56. function isLocalVideoCommentReplyAccepted (_object: {
  57. commentBody: VideoCommentCreate
  58. parentComment: VideoCommentModel
  59. video: VideoModel
  60. user: UserModel
  61. }): AcceptResult {
  62. return { accepted: true }
  63. }
  64. function isRemoteVideoAccepted (_object: {
  65. activity: ActivityCreate
  66. videoAP: VideoObject
  67. byActor: ActorModel
  68. }): AcceptResult {
  69. return { accepted: true }
  70. }
  71. function isRemoteVideoCommentAccepted (_object: {
  72. activity: ActivityCreate
  73. commentAP: VideoCommentObject
  74. byActor: ActorModel
  75. }): AcceptResult {
  76. return { accepted: true }
  77. }
  78. function isPreImportVideoAccepted (object: {
  79. videoImportBody: VideoImportCreate
  80. user: MUser
  81. }): AcceptResult {
  82. return { accepted: true }
  83. }
  84. function isPostImportVideoAccepted (object: {
  85. videoFilePath: PathLike
  86. videoFile: VideoFileModel
  87. user: MUser
  88. }): AcceptResult {
  89. return { accepted: true }
  90. }
  91. async function createVideoAbuse (options: {
  92. baseAbuse: FilteredModelAttributes<AbuseModel>
  93. videoInstance: MVideoAccountLightBlacklistAllFiles
  94. startAt: number
  95. endAt: number
  96. transaction: Transaction
  97. reporterAccount: MAccountDefault
  98. }) {
  99. const { baseAbuse, videoInstance, startAt, endAt, transaction, reporterAccount } = options
  100. const associateFun = async (abuseInstance: MAbuseFull) => {
  101. const videoAbuseInstance: MVideoAbuseVideoFull = await VideoAbuseModel.create({
  102. abuseId: abuseInstance.id,
  103. videoId: videoInstance.id,
  104. startAt: startAt,
  105. endAt: endAt
  106. }, { transaction })
  107. videoAbuseInstance.Video = videoInstance
  108. abuseInstance.VideoAbuse = videoAbuseInstance
  109. return { isOwned: videoInstance.isOwned() }
  110. }
  111. return createAbuse({
  112. base: baseAbuse,
  113. reporterAccount,
  114. flaggedAccount: videoInstance.VideoChannel.Account,
  115. transaction,
  116. associateFun
  117. })
  118. }
  119. function createVideoCommentAbuse (options: {
  120. baseAbuse: FilteredModelAttributes<AbuseModel>
  121. commentInstance: MCommentOwnerVideo
  122. transaction: Transaction
  123. reporterAccount: MAccountDefault
  124. }) {
  125. const { baseAbuse, commentInstance, transaction, reporterAccount } = options
  126. const associateFun = async (abuseInstance: MAbuseFull) => {
  127. const commentAbuseInstance: MCommentAbuseAccountVideo = await VideoCommentAbuseModel.create({
  128. abuseId: abuseInstance.id,
  129. videoCommentId: commentInstance.id
  130. }, { transaction })
  131. commentAbuseInstance.VideoComment = commentInstance
  132. abuseInstance.VideoCommentAbuse = commentAbuseInstance
  133. return { isOwned: commentInstance.isOwned() }
  134. }
  135. return createAbuse({
  136. base: baseAbuse,
  137. reporterAccount,
  138. flaggedAccount: commentInstance.Account,
  139. transaction,
  140. associateFun
  141. })
  142. }
  143. function createAccountAbuse (options: {
  144. baseAbuse: FilteredModelAttributes<AbuseModel>
  145. accountInstance: MAccountDefault
  146. transaction: Transaction
  147. reporterAccount: MAccountDefault
  148. }) {
  149. const { baseAbuse, accountInstance, transaction, reporterAccount } = options
  150. const associateFun = async () => {
  151. return { isOwned: accountInstance.isOwned() }
  152. }
  153. return createAbuse({
  154. base: baseAbuse,
  155. reporterAccount,
  156. flaggedAccount: accountInstance,
  157. transaction,
  158. associateFun
  159. })
  160. }
  161. export {
  162. isLocalLiveVideoAccepted,
  163. isLocalVideoAccepted,
  164. isLocalVideoThreadAccepted,
  165. isRemoteVideoAccepted,
  166. isRemoteVideoCommentAccepted,
  167. isLocalVideoCommentReplyAccepted,
  168. isPreImportVideoAccepted,
  169. isPostImportVideoAccepted,
  170. createAbuse,
  171. createVideoAbuse,
  172. createVideoCommentAbuse,
  173. createAccountAbuse
  174. }
  175. // ---------------------------------------------------------------------------
  176. async function createAbuse (options: {
  177. base: FilteredModelAttributes<AbuseModel>
  178. reporterAccount: MAccountDefault
  179. flaggedAccount: MAccountLight
  180. associateFun: (abuseInstance: MAbuseFull) => Promise<{ isOwned: boolean} >
  181. transaction: Transaction
  182. }) {
  183. const { base, reporterAccount, flaggedAccount, associateFun, transaction } = options
  184. const auditLogger = auditLoggerFactory('abuse')
  185. const abuseAttributes = Object.assign({}, base, { flaggedAccountId: flaggedAccount.id })
  186. const abuseInstance: MAbuseFull = await AbuseModel.create(abuseAttributes, { transaction })
  187. abuseInstance.ReporterAccount = reporterAccount
  188. abuseInstance.FlaggedAccount = flaggedAccount
  189. const { isOwned } = await associateFun(abuseInstance)
  190. if (isOwned === false) {
  191. await sendAbuse(reporterAccount.Actor, abuseInstance, abuseInstance.FlaggedAccount, transaction)
  192. }
  193. const abuseJSON = abuseInstance.toFormattedAdminJSON()
  194. auditLogger.create(reporterAccount.Actor.getIdentifier(), new AbuseAuditView(abuseJSON))
  195. Notifier.Instance.notifyOnNewAbuse({
  196. abuse: abuseJSON,
  197. abuseInstance,
  198. reporter: reporterAccount.Actor.getIdentifier()
  199. })
  200. logger.info('Abuse report %d created.', abuseInstance.id)
  201. return abuseJSON
  202. }