Add moderation helpers to plugins
authorChocobozzz <me@florianbigard.com>
Thu, 7 May 2020 12:58:24 +0000 (14:58 +0200)
committerChocobozzz <me@florianbigard.com>
Thu, 7 May 2020 13:07:01 +0000 (15:07 +0200)
34 files changed:
scripts/create-import-video-file-job.ts
scripts/create-transcoding-job.ts
scripts/danger/clean/cleaner.ts
scripts/migrations/peertube-2.1.ts
scripts/optimize-old-videos.ts
scripts/prune-storage.ts
scripts/reset-password.ts
scripts/update-host.ts
server.ts
server/controllers/api/users/my-history.ts
server/controllers/api/videos/abuse.ts
server/controllers/api/videos/blacklist.ts
server/controllers/api/videos/comment.ts
server/controllers/api/videos/ownership.ts
server/initializers/index.ts [deleted file]
server/lib/activitypub/process/process-announce.ts
server/lib/activitypub/process/process-create.ts
server/lib/activitypub/process/process-delete.ts
server/lib/activitypub/process/process-dislike.ts
server/lib/activitypub/process/process-flag.ts
server/lib/activitypub/process/process-follow.ts
server/lib/activitypub/process/process-like.ts
server/lib/activitypub/process/process-reject.ts
server/lib/activitypub/process/process-undo.ts
server/lib/activitypub/process/process-update.ts
server/lib/blocklist.ts
server/lib/job-queue/handlers/video-transcoding.ts
server/lib/plugins/plugin-helpers.ts
server/lib/video-blacklist.ts
server/middlewares/validators/blocklist.ts
server/models/server/server.ts
server/tests/fixtures/peertube-plugin-test-four/main.js
server/tests/plugins/plugin-helpers.ts
server/typings/plugins/register-server-option.model.ts

index 37738ca40c896b08448ab30417304c72ede0213e..d71e82c14585479e46ce1c4d48862d39b4dfa8f8 100644 (file)
@@ -4,7 +4,7 @@ registerTSPaths()
 import * as program from 'commander'
 import { resolve } from 'path'
 import { VideoModel } from '../server/models/video/video'
-import { initDatabaseModels } from '../server/initializers'
+import { initDatabaseModels } from '../server/initializers/database'
 import { JobQueue } from '../server/lib/job-queue'
 
 program
index 1312e8952924e93ab14749894c235426af0d77d5..7ee0050e39e5837857203612fb9259cbf77eadf7 100755 (executable)
@@ -3,7 +3,7 @@ registerTSPaths()
 
 import * as program from 'commander'
 import { VideoModel } from '../server/models/video/video'
-import { initDatabaseModels } from '../server/initializers'
+import { initDatabaseModels } from '../server/initializers/database'
 import { JobQueue } from '../server/lib/job-queue'
 import { computeResolutionsToTranscode } from '@server/helpers/ffmpeg-utils'
 import { VideoTranscodingPayload } from '@shared/models'
index ed35ef79f87e77441de93e07ba9d2c4e0760b987..2d5366a9170ee21c090054405385cff33d039f68 100644 (file)
@@ -3,7 +3,7 @@ registerTSPaths()
 
 import * as Promise from 'bluebird'
 import * as rimraf from 'rimraf'
-import { initDatabaseModels, sequelizeTypescript } from '../../../server/initializers'
+import { initDatabaseModels, sequelizeTypescript } from '../../../server/initializers/database'
 import { CONFIG } from '../../../server/initializers/config'
 
 initDatabaseModels(true)
index 892497a8892735b6d4b40e1ba03907bdee53c493..e17e5816630cdf6b356c2791b1b20b9b2a9c6c9c 100644 (file)
@@ -1,7 +1,7 @@
 import { registerTSPaths } from '../../server/helpers/register-ts-paths'
 registerTSPaths()
 
-import { initDatabaseModels, sequelizeTypescript } from '../../server/initializers'
+import { initDatabaseModels, sequelizeTypescript } from '../../server/initializers/database'
 import * as Sequelize from 'sequelize'
 import { join } from 'path'
 import { HLS_STREAMING_PLAYLIST_DIRECTORY, STATIC_PATHS, WEBSERVER } from '@server/initializers/constants'
index a84845068f5f24520b6d46c73e6f422a8871f5a5..9595efd9c6623af298b4369d80db272bc2d89176 100644 (file)
@@ -6,7 +6,7 @@ import { getDurationFromVideoFile, getVideoFileBitrate, getVideoFileFPS, getVide
 import { getMaxBitrate } from '../shared/models/videos'
 import { VideoModel } from '../server/models/video/video'
 import { optimizeOriginalVideofile } from '../server/lib/video-transcoding'
-import { initDatabaseModels } from '../server/initializers'
+import { initDatabaseModels } from '../server/initializers/database'
 import { basename, dirname } from 'path'
 import { copy, move, remove } from 'fs-extra'
 import { createTorrentAndSetInfoHash } from '@server/helpers/webtorrent'
index fa3d817446754cb50413dcb94ea8bdfbd12b914f..2b04e906d999ba4d1ffb54148724f33ce1bb6ab5 100755 (executable)
@@ -5,7 +5,7 @@ import * as prompt from 'prompt'
 import { join } from 'path'
 import { CONFIG } from '../server/initializers/config'
 import { VideoModel } from '../server/models/video/video'
-import { initDatabaseModels } from '../server/initializers'
+import { initDatabaseModels } from '../server/initializers/database'
 import { readdir, remove } from 'fs-extra'
 import { VideoRedundancyModel } from '../server/models/redundancy/video-redundancy'
 import * as Bluebird from 'bluebird'
index 6126c3cd09fa9da04c57b1c52852ad3196627f66..863537500a55fb61dbbeb91d15d3204ab5a2323e 100755 (executable)
@@ -2,7 +2,7 @@ import { registerTSPaths } from '../server/helpers/register-ts-paths'
 registerTSPaths()
 
 import * as program from 'commander'
-import { initDatabaseModels } from '../server/initializers'
+import { initDatabaseModels } from '../server/initializers/database'
 import { UserModel } from '../server/models/account/user'
 import { isUserPasswordValid } from '../server/helpers/custom-validators/users'
 
index 7b07dea04936f814f28d2d6478e13ca816d54318..2efe326a2b03cdafe95e3ee1345c95476153988d 100755 (executable)
@@ -17,7 +17,7 @@ import { VideoCommentModel } from '../server/models/video/video-comment'
 import { AccountModel } from '../server/models/account/account'
 import { VideoChannelModel } from '../server/models/video/video-channel'
 import { VideoStreamingPlaylistModel } from '../server/models/video/video-streaming-playlist'
-import { initDatabaseModels } from '../server/initializers'
+import { initDatabaseModels } from '../server/initializers/database'
 import { createTorrentAndSetInfoHash } from '@server/helpers/webtorrent'
 import { getServerActor } from '@server/models/application/application'
 
index b14ebf623297a9f3d56154379b737839ec5b5067..e18e2be5c305c68a8fa8b74c4b899682ea3377c6 100644 (file)
--- a/server.ts
+++ b/server.ts
@@ -84,7 +84,7 @@ migrate()
 loadLanguages()
 
 // ----------- PeerTube modules -----------
-import { installApplication } from './server/initializers'
+import { installApplication } from './server/initializers/installer'
 import { Emailer } from './server/lib/emailer'
 import { JobQueue } from './server/lib/job-queue'
 import { VideosPreviewCache, VideosCaptionCache } from './server/lib/files-cache'
index 4da1f34963e01638947c3a9565ea0ee808ee906a..77a15e5fc12c039d76e27002aea71270673688aa 100644 (file)
@@ -9,7 +9,7 @@ import {
 } from '../../../middlewares'
 import { getFormattedObjects } from '../../../helpers/utils'
 import { UserVideoHistoryModel } from '../../../models/account/user-video-history'
-import { sequelizeTypescript } from '../../../initializers'
+import { sequelizeTypescript } from '../../../initializers/database'
 
 const myVideosHistoryRouter = express.Router()
 
index 3fe7f7e51381132e6192484e01a1193304a06ea4..bce50aefbeeeb6fc12a6d9935d056febba0855fe 100644 (file)
@@ -2,7 +2,7 @@ import * as express from 'express'
 import { UserRight, VideoAbuseCreate, VideoAbuseState } from '../../../../shared'
 import { logger } from '../../../helpers/logger'
 import { getFormattedObjects } from '../../../helpers/utils'
-import { sequelizeTypescript } from '../../../initializers'
+import { sequelizeTypescript } from '../../../initializers/database'
 import {
   asyncMiddleware,
   asyncRetryTransactionMiddleware,
index abd09387cf45b44cde1d71101541ba14b65f6efd..3b25ceea28bbbfe04e3104999024180a0209eb76 100644 (file)
@@ -1,7 +1,9 @@
 import * as express from 'express'
-import { UserRight, VideoBlacklistCreate, VideoBlacklistType } from '../../../../shared'
+import { blacklistVideo, unblacklistVideo } from '@server/lib/video-blacklist'
+import { UserRight, VideoBlacklistCreate } from '../../../../shared'
 import { logger } from '../../../helpers/logger'
 import { getFormattedObjects } from '../../../helpers/utils'
+import { sequelizeTypescript } from '../../../initializers/database'
 import {
   asyncMiddleware,
   authenticate,
@@ -16,11 +18,6 @@ import {
   videosBlacklistUpdateValidator
 } from '../../../middlewares'
 import { VideoBlacklistModel } from '../../../models/video/video-blacklist'
-import { sequelizeTypescript } from '../../../initializers'
-import { Notifier } from '../../../lib/notifier'
-import { sendDeleteVideo } from '../../../lib/activitypub/send'
-import { federateVideoIfNeeded } from '../../../lib/activitypub/videos'
-import { MVideoBlacklistVideo } from '@server/typings/models'
 
 const blacklistRouter = express.Router()
 
@@ -28,7 +25,7 @@ blacklistRouter.post('/:videoId/blacklist',
   authenticate,
   ensureUserHasRight(UserRight.MANAGE_VIDEO_BLACKLIST),
   asyncMiddleware(videosBlacklistAddValidator),
-  asyncMiddleware(addVideoToBlacklist)
+  asyncMiddleware(addVideoToBlacklistController)
 )
 
 blacklistRouter.get('/blacklist',
@@ -64,29 +61,15 @@ export {
 
 // ---------------------------------------------------------------------------
 
-async function addVideoToBlacklist (req: express.Request, res: express.Response) {
+async function addVideoToBlacklistController (req: express.Request, res: express.Response) {
   const videoInstance = res.locals.videoAll
   const body: VideoBlacklistCreate = req.body
 
-  const toCreate = {
-    videoId: videoInstance.id,
-    unfederated: body.unfederate === true,
-    reason: body.reason,
-    type: VideoBlacklistType.MANUAL
-  }
-
-  const blacklist: MVideoBlacklistVideo = await VideoBlacklistModel.create(toCreate)
-  blacklist.Video = videoInstance
-
-  if (body.unfederate === true) {
-    await sendDeleteVideo(videoInstance, undefined)
-  }
-
-  Notifier.Instance.notifyOnVideoBlacklist(blacklist)
+  await blacklistVideo(videoInstance, body)
 
   logger.info('Video %s blacklisted.', videoInstance.uuid)
 
-  return res.type('json').status(204).end()
+  return res.type('json').sendStatus(204)
 }
 
 async function updateVideoBlacklistController (req: express.Request, res: express.Response) {
@@ -98,7 +81,7 @@ async function updateVideoBlacklistController (req: express.Request, res: expres
     return videoBlacklist.save({ transaction: t })
   })
 
-  return res.type('json').status(204).end()
+  return res.type('json').sendStatus(204)
 }
 
 async function listBlacklist (req: express.Request, res: express.Response) {
@@ -117,32 +100,9 @@ async function removeVideoFromBlacklistController (req: express.Request, res: ex
   const videoBlacklist = res.locals.videoBlacklist
   const video = res.locals.videoAll
 
-  const videoBlacklistType = await sequelizeTypescript.transaction(async t => {
-    const unfederated = videoBlacklist.unfederated
-    const videoBlacklistType = videoBlacklist.type
-
-    await videoBlacklist.destroy({ transaction: t })
-    video.VideoBlacklist = undefined
-
-    // Re federate the video
-    if (unfederated === true) {
-      await federateVideoIfNeeded(video, true, t)
-    }
-
-    return videoBlacklistType
-  })
-
-  Notifier.Instance.notifyOnVideoUnblacklist(video)
-
-  if (videoBlacklistType === VideoBlacklistType.AUTO_BEFORE_PUBLISHED) {
-    Notifier.Instance.notifyOnVideoPublishedAfterRemovedFromAutoBlacklist(video)
-
-    // Delete on object so new video notifications will send
-    delete video.VideoBlacklist
-    Notifier.Instance.notifyOnNewVideoIfNeeded(video)
-  }
+  await unblacklistVideo(videoBlacklist, video)
 
   logger.info('Video %s removed from blacklist.', video.uuid)
 
-  return res.type('json').status(204).end()
+  return res.type('json').sendStatus(204)
 }
index 5f3fed5c098f246a5984667e9ca8414e06cec025..5070bb3c03bd4bf05e517d44dc28e3b71fc1cf06 100644 (file)
@@ -4,7 +4,7 @@ import { ResultList } from '../../../../shared/models'
 import { VideoCommentCreate } from '../../../../shared/models/videos/video-comment.model'
 import { logger } from '../../../helpers/logger'
 import { getFormattedObjects } from '../../../helpers/utils'
-import { sequelizeTypescript } from '../../../initializers'
+import { sequelizeTypescript } from '../../../initializers/database'
 import { buildFormattedCommentTree, createVideoComment, markCommentAsDeleted } from '../../../lib/video-comment'
 import {
   asyncMiddleware,
index 190036f85ee60a3002633281666e0da98e4da338..540a49010198833c2d928f83215039b4d9b3f9b4 100644 (file)
@@ -1,6 +1,6 @@
 import * as express from 'express'
 import { logger } from '../../../helpers/logger'
-import { sequelizeTypescript } from '../../../initializers'
+import { sequelizeTypescript } from '../../../initializers/database'
 import {
   asyncMiddleware,
   asyncRetryTransactionMiddleware,
diff --git a/server/initializers/index.ts b/server/initializers/index.ts
deleted file mode 100644 (file)
index 0fc1a73..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-export * from './database'
-export * from './installer'
-export * from './migrator'
index 7e22125d5ce644a64b7bbc527b2576f51eceebb0..26427aaa1ede0dcbc5e519de6ffe661a10d4bea9 100644 (file)
@@ -1,6 +1,6 @@
 import { ActivityAnnounce } from '../../../../shared/models/activitypub'
 import { retryTransactionWrapper } from '../../../helpers/database-utils'
-import { sequelizeTypescript } from '../../../initializers'
+import { sequelizeTypescript } from '../../../initializers/database'
 import { VideoShareModel } from '../../../models/video/video-share'
 import { forwardVideoRelatedActivity } from '../send/utils'
 import { getOrCreateVideoAndAccountAndChannel } from '../videos'
index d375e29e30c60af79eaf53fd9203220c4c014cd8..566bf6992144d033772bbfb619a22ad5de7a4fca 100644 (file)
@@ -2,7 +2,7 @@ import { ActivityCreate, CacheFileObject, VideoTorrentObject } from '../../../..
 import { VideoCommentObject } from '../../../../shared/models/activitypub/objects/video-comment-object'
 import { retryTransactionWrapper } from '../../../helpers/database-utils'
 import { logger } from '../../../helpers/logger'
-import { sequelizeTypescript } from '../../../initializers'
+import { sequelizeTypescript } from '../../../initializers/database'
 import { resolveThread } from '../video-comments'
 import { getOrCreateVideoAndAccountAndChannel } from '../videos'
 import { forwardVideoRelatedActivity } from '../send/utils'
index e76132f915d9bbbb28685f7a058de3cd36a3dfaa..7c8dc83e8395009c197d99ad5911b2653ef8f67e 100644 (file)
@@ -1,7 +1,7 @@
 import { ActivityDelete } from '../../../../shared/models/activitypub'
 import { retryTransactionWrapper } from '../../../helpers/database-utils'
 import { logger } from '../../../helpers/logger'
-import { sequelizeTypescript } from '../../../initializers'
+import { sequelizeTypescript } from '../../../initializers/database'
 import { ActorModel } from '../../../models/activitypub/actor'
 import { VideoModel } from '../../../models/video/video'
 import { VideoCommentModel } from '../../../models/video/video-comment'
index debd8a67ca43d4346d6b2d8e28761fd479f32733..fcdd0b86e92496d1e7e32f52cb9deb0ae7effdd2 100644 (file)
@@ -1,7 +1,7 @@
 import { ActivityCreate, ActivityDislike } from '../../../../shared'
 import { DislikeObject } from '../../../../shared/models/activitypub/objects'
 import { retryTransactionWrapper } from '../../../helpers/database-utils'
-import { sequelizeTypescript } from '../../../initializers'
+import { sequelizeTypescript } from '../../../initializers/database'
 import { AccountVideoRateModel } from '../../../models/account/account-video-rate'
 import { getOrCreateVideoAndAccountAndChannel } from '../videos'
 import { forwardVideoRelatedActivity } from '../send/utils'
index e6e9084de2aa23edde37192b2b38e29d9fc1c062..9a488a47300024a53ecaedf7aacee2b40317ec62 100644 (file)
@@ -2,7 +2,7 @@ import { ActivityCreate, ActivityFlag, VideoAbuseState } from '../../../../share
 import { VideoAbuseObject } from '../../../../shared/models/activitypub/objects'
 import { retryTransactionWrapper } from '../../../helpers/database-utils'
 import { logger } from '../../../helpers/logger'
-import { sequelizeTypescript } from '../../../initializers'
+import { sequelizeTypescript } from '../../../initializers/database'
 import { VideoAbuseModel } from '../../../models/video/video-abuse'
 import { getOrCreateVideoAndAccountAndChannel } from '../videos'
 import { Notifier } from '../../notifier'
index 8f7828e417ec78f76aa1c6853b91b33b229ab09b..950d421ddf5d718ff1d59b5f9a5f9d1155b5243a 100644 (file)
@@ -1,7 +1,7 @@
 import { ActivityFollow } from '../../../../shared/models/activitypub'
 import { retryTransactionWrapper } from '../../../helpers/database-utils'
 import { logger } from '../../../helpers/logger'
-import { sequelizeTypescript } from '../../../initializers'
+import { sequelizeTypescript } from '../../../initializers/database'
 import { ActorModel } from '../../../models/activitypub/actor'
 import { ActorFollowModel } from '../../../models/activitypub/actor-follow'
 import { sendAccept, sendReject } from '../send'
index 62be0de42f30aa82b076f6d41e64539f5effcc1a..fba3c76a4a24a1a67c81a76e5eab2bfaa32a23bf 100644 (file)
@@ -1,6 +1,6 @@
 import { ActivityLike } from '../../../../shared/models/activitypub'
 import { retryTransactionWrapper } from '../../../helpers/database-utils'
-import { sequelizeTypescript } from '../../../initializers'
+import { sequelizeTypescript } from '../../../initializers/database'
 import { AccountVideoRateModel } from '../../../models/account/account-video-rate'
 import { forwardVideoRelatedActivity } from '../send/utils'
 import { getOrCreateVideoAndAccountAndChannel } from '../videos'
index 00e9afa109035443b328617d550e2c87e4e149dc..9804436a2468c97fe1ee5268126d23efbd3d26e8 100644 (file)
@@ -1,5 +1,5 @@
 import { ActivityReject } from '../../../../shared/models/activitypub/activity'
-import { sequelizeTypescript } from '../../../initializers'
+import { sequelizeTypescript } from '../../../initializers/database'
 import { ActorFollowModel } from '../../../models/activitypub/actor-follow'
 import { APProcessorOptions } from '../../../typings/activitypub-processor.model'
 import { MActor } from '../../../typings/models'
index 10643b2e99d9210535aa5998740abea0d479d4f1..9ef6a8a973a5638ad571e5c1070351eb735d6cfe 100644 (file)
@@ -2,7 +2,7 @@ import { ActivityAnnounce, ActivityFollow, ActivityLike, ActivityUndo, CacheFile
 import { DislikeObject } from '../../../../shared/models/activitypub/objects'
 import { retryTransactionWrapper } from '../../../helpers/database-utils'
 import { logger } from '../../../helpers/logger'
-import { sequelizeTypescript } from '../../../initializers'
+import { sequelizeTypescript } from '../../../initializers/database'
 import { AccountVideoRateModel } from '../../../models/account/account-video-rate'
 import { ActorModel } from '../../../models/activitypub/actor'
 import { ActorFollowModel } from '../../../models/activitypub/actor-follow'
index 9579512b7e6062ab00a2078a75d501377489106c..98ab0f83def9e597de1ecfe6291ccacb468ddd36 100644 (file)
@@ -2,7 +2,7 @@ import { ActivityUpdate, CacheFileObject, VideoTorrentObject } from '../../../..
 import { ActivityPubActor } from '../../../../shared/models/activitypub/activitypub-actor'
 import { resetSequelizeInstance, retryTransactionWrapper } from '../../../helpers/database-utils'
 import { logger } from '../../../helpers/logger'
-import { sequelizeTypescript } from '../../../initializers'
+import { sequelizeTypescript } from '../../../initializers/database'
 import { AccountModel } from '../../../models/account/account'
 import { ActorModel } from '../../../models/activitypub/actor'
 import { VideoChannelModel } from '../../../models/video/video-channel'
index 28c69b46e767744a73d267fa97f9fcabb9aaba8d..842eecb5b467b1f36e502d1cb242d7fba2632ab5 100644 (file)
@@ -1,7 +1,7 @@
-import { sequelizeTypescript } from '../initializers'
+import { sequelizeTypescript } from '@server/initializers/database'
+import { MAccountBlocklist, MServerBlocklist } from '@server/typings/models'
 import { AccountBlocklistModel } from '../models/account/account-blocklist'
 import { ServerBlocklistModel } from '../models/server/server-blocklist'
-import { MAccountBlocklist, MServerBlocklist } from '@server/typings/models'
 
 function addAccountInBlocklist (byAccountId: number, targetAccountId: number) {
   return sequelizeTypescript.transaction(async t => {
index 04aac515f68e636674e57158a5a2d9a88ebb2d88..46d52e1cf043a6424e0b57e119b15f8a01349420 100644 (file)
@@ -10,7 +10,7 @@ import { VideoModel } from '../../../models/video/video'
 import { JobQueue } from '../job-queue'
 import { federateVideoIfNeeded } from '../../activitypub/videos'
 import { retryTransactionWrapper } from '../../../helpers/database-utils'
-import { sequelizeTypescript } from '../../../initializers'
+import { sequelizeTypescript } from '../../../initializers/database'
 import { computeResolutionsToTranscode } from '../../../helpers/ffmpeg-utils'
 import { generateHlsPlaylist, mergeAudioVideofile, optimizeOriginalVideofile, transcodeNewResolution } from '../../video-transcoding'
 import { Notifier } from '../../notifier'
index 608207e05a1c264be24d50aecde6f40843ebdfbc..de82b4918866185b30cd0ee92af7e467ce4a6186 100644 (file)
@@ -3,6 +3,15 @@ import { sequelizeTypescript } from '@server/initializers/database'
 import { buildLogger } from '@server/helpers/logger'
 import { VideoModel } from '@server/models/video/video'
 import { WEBSERVER } from '@server/initializers/constants'
+import { ServerModel } from '@server/models/server/server'
+import { getServerActor } from '@server/models/application/application'
+import { addServerInBlocklist, removeServerFromBlocklist, addAccountInBlocklist, removeAccountFromBlocklist } from '../blocklist'
+import { ServerBlocklistModel } from '@server/models/server/server-blocklist'
+import { AccountModel } from '@server/models/account/account'
+import { VideoBlacklistCreate } from '@shared/models'
+import { blacklistVideo, unblacklistVideo } from '../video-blacklist'
+import { VideoBlacklistModel } from '@server/models/video/video-blacklist'
+import { AccountBlocklistModel } from '@server/models/account/account-blocklist'
 
 function buildPluginHelpers (npmName: string): PeerTubeHelpers {
   const logger = buildPluginLogger(npmName)
@@ -12,11 +21,17 @@ function buildPluginHelpers (npmName: string): PeerTubeHelpers {
 
   const config = buildConfigHelpers()
 
+  const server = buildServerHelpers()
+
+  const moderation = buildModerationHelpers()
+
   return {
     logger,
     database,
     videos,
-    config
+    config,
+    moderation,
+    server
   }
 }
 
@@ -36,8 +51,18 @@ function buildDatabaseHelpers () {
   }
 }
 
+function buildServerHelpers () {
+  return {
+    getServerActor: () => getServerActor()
+  }
+}
+
 function buildVideosHelpers () {
   return {
+    loadByUrl: (url: string) => {
+      return VideoModel.loadByUrl(url)
+    },
+
     removeVideo: (id: number) => {
       return sequelizeTypescript.transaction(async t => {
         const video = await VideoModel.loadAndPopulateAccountAndServerAndTags(id, t)
@@ -48,6 +73,57 @@ function buildVideosHelpers () {
   }
 }
 
+function buildModerationHelpers () {
+  return {
+    blockServer: async (options: { byAccountId: number, hostToBlock: string }) => {
+      const serverToBlock = await ServerModel.loadOrCreateByHost(options.hostToBlock)
+
+      await addServerInBlocklist(options.byAccountId, serverToBlock.id)
+    },
+
+    unblockServer: async (options: { byAccountId: number, hostToUnblock: string }) => {
+      const serverBlock = await ServerBlocklistModel.loadByAccountAndHost(options.byAccountId, options.hostToUnblock)
+      if (!serverBlock) return
+
+      await removeServerFromBlocklist(serverBlock)
+    },
+
+    blockAccount: async (options: { byAccountId: number, handleToBlock: string }) => {
+      const accountToBlock = await AccountModel.loadByNameWithHost(options.handleToBlock)
+      if (!accountToBlock) return
+
+      await addAccountInBlocklist(options.byAccountId, accountToBlock.id)
+    },
+
+    unblockAccount: async (options: { byAccountId: number, handleToUnblock: string }) => {
+      const targetAccount = await AccountModel.loadByNameWithHost(options.handleToUnblock)
+      if (!targetAccount) return
+
+      const accountBlock = await AccountBlocklistModel.loadByAccountAndTarget(options.byAccountId, targetAccount.id)
+      if (!accountBlock) return
+
+      await removeAccountFromBlocklist(accountBlock)
+    },
+
+    blacklistVideo: async (options: { videoIdOrUUID: number | string, createOptions: VideoBlacklistCreate }) => {
+      const video = await VideoModel.loadAndPopulateAccountAndServerAndTags(options.videoIdOrUUID)
+      if (!video) return
+
+      await blacklistVideo(video, options.createOptions)
+    },
+
+    unblacklistVideo: async (options: { videoIdOrUUID: number | string }) => {
+      const video = await VideoModel.loadAndPopulateAccountAndServerAndTags(options.videoIdOrUUID)
+      if (!video) return
+
+      const videoBlacklist = await VideoBlacklistModel.loadByVideoId(video.id)
+      if (!videoBlacklist) return
+
+      await unblacklistVideo(videoBlacklist, video)
+    }
+  }
+}
+
 function buildConfigHelpers () {
   return {
     getWebserverUrl () {
index 3b90b1b9415982071d6deff43dacbeb18ec47b59..bd60c6201c93d1cde2ff762b717103f5ef2dc714 100644 (file)
@@ -1,12 +1,22 @@
 import { Transaction } from 'sequelize'
+import { sequelizeTypescript } from '@server/initializers/database'
+import {
+  MUser,
+  MVideoAccountLight,
+  MVideoBlacklist,
+  MVideoBlacklistVideo,
+  MVideoFullLight,
+  MVideoWithBlacklistLight
+} from '@server/typings/models'
+import { UserRight, VideoBlacklistCreate, VideoBlacklistType } from '../../shared/models'
+import { UserAdminFlag } from '../../shared/models/users/user-flag.model'
+import { logger } from '../helpers/logger'
 import { CONFIG } from '../initializers/config'
-import { UserRight, VideoBlacklistType } from '../../shared/models'
 import { VideoBlacklistModel } from '../models/video/video-blacklist'
-import { logger } from '../helpers/logger'
-import { UserAdminFlag } from '../../shared/models/users/user-flag.model'
-import { Hooks } from './plugins/hooks'
+import { sendDeleteVideo } from './activitypub/send'
+import { federateVideoIfNeeded } from './activitypub/videos'
 import { Notifier } from './notifier'
-import { MUser, MVideoBlacklistVideo, MVideoWithBlacklistLight } from '@server/typings/models'
+import { Hooks } from './plugins/hooks'
 
 async function autoBlacklistVideoIfNeeded (parameters: {
   video: MVideoWithBlacklistLight
@@ -49,6 +59,60 @@ async function autoBlacklistVideoIfNeeded (parameters: {
   return true
 }
 
+async function blacklistVideo (videoInstance: MVideoAccountLight, options: VideoBlacklistCreate) {
+  const blacklist: MVideoBlacklistVideo = await VideoBlacklistModel.create({
+    videoId: videoInstance.id,
+    unfederated: options.unfederate === true,
+    reason: options.reason,
+    type: VideoBlacklistType.MANUAL
+  }
+  )
+  blacklist.Video = videoInstance
+
+  if (options.unfederate === true) {
+    await sendDeleteVideo(videoInstance, undefined)
+  }
+
+  Notifier.Instance.notifyOnVideoBlacklist(blacklist)
+}
+
+async function unblacklistVideo (videoBlacklist: MVideoBlacklist, video: MVideoFullLight) {
+  const videoBlacklistType = await sequelizeTypescript.transaction(async t => {
+    const unfederated = videoBlacklist.unfederated
+    const videoBlacklistType = videoBlacklist.type
+
+    await videoBlacklist.destroy({ transaction: t })
+    video.VideoBlacklist = undefined
+
+    // Re federate the video
+    if (unfederated === true) {
+      await federateVideoIfNeeded(video, true, t)
+    }
+
+    return videoBlacklistType
+  })
+
+  Notifier.Instance.notifyOnVideoUnblacklist(video)
+
+  if (videoBlacklistType === VideoBlacklistType.AUTO_BEFORE_PUBLISHED) {
+    Notifier.Instance.notifyOnVideoPublishedAfterRemovedFromAutoBlacklist(video)
+
+    // Delete on object so new video notifications will send
+    delete video.VideoBlacklist
+    Notifier.Instance.notifyOnNewVideoIfNeeded(video)
+  }
+}
+
+// ---------------------------------------------------------------------------
+
+export {
+  autoBlacklistVideoIfNeeded,
+  blacklistVideo,
+  unblacklistVideo
+}
+
+// ---------------------------------------------------------------------------
+
 function autoBlacklistNeeded (parameters: {
   video: MVideoWithBlacklistLight
   isRemote: boolean
@@ -66,9 +130,3 @@ function autoBlacklistNeeded (parameters: {
 
   return true
 }
-
-// ---------------------------------------------------------------------------
-
-export {
-  autoBlacklistVideoIfNeeded
-}
index c00a7e4df5e22c204f7d958e106bd8c7992bf0e4..27224ff9b5c5218a45e20d52817dc9fd528034ac 100644 (file)
@@ -84,10 +84,7 @@ const blockServerValidator = [
         .end()
     }
 
-    let server = await ServerModel.loadByHost(host)
-    if (!server) {
-      server = await ServerModel.create({ host })
-    }
+    const server = await ServerModel.loadOrCreateByHost(host)
 
     res.locals.server = server
 
index 8b07115f1f810a43c0403f7ef7b75b3702ff64a0..5131257ec3d65bd6a8cb13b6c1729b284153345a 100644 (file)
@@ -71,6 +71,13 @@ export class ServerModel extends Model<ServerModel> {
     return ServerModel.findOne(query)
   }
 
+  static async loadOrCreateByHost (host: string) {
+    let server = await ServerModel.loadByHost(host)
+    if (!server) server = await ServerModel.create({ host })
+
+    return server
+  }
+
   isBlocked () {
     return this.BlockedByAccounts && this.BlockedByAccounts.length !== 0
   }
index 2e81550c1575e9633b7c7f1d11d2df345b94832e..067c3fe15163376a4b98fa7f19ec55a8f9b2c23a 100644 (file)
@@ -1,30 +1,70 @@
 async function register ({
   peertubeHelpers,
-  registerHook
+  registerHook,
+  getRouter
 }) {
   const logger = peertubeHelpers.logger
 
   logger.info('Hello world from plugin four')
 
-  const username = 'root'
-  const results = await peertubeHelpers.database.query(
-    'SELECT "email" from "user" WHERE "username" = $username',
-    {
-      type: 'SELECT',
-      bind: { username }
-    }
-  )
+  {
+    const username = 'root'
+    const results = await peertubeHelpers.database.query(
+      'SELECT "email" from "user" WHERE "username" = $username',
+      {
+        type: 'SELECT',
+        bind: { username }
+      }
+    )
+
+    logger.info('root email is ' + results[0]['email'])
+  }
+
+  {
+    registerHook({
+      target: 'action:api.video.viewed',
+      handler: async ({ video }) => {
+        const videoFromDB = await peertubeHelpers.videos.loadByUrl(video.url)
+        logger.info('video from DB uuid is %s.', videoFromDB.uuid)
+
+        await peertubeHelpers.videos.removeVideo(video.id)
 
-  logger.info('root email is ' + results[0]['email'])
+        logger.info('Video deleted by plugin four.')
+      }
+    })
+  }
 
-  registerHook({
-    target: 'action:api.video.viewed',
-    handler: async ({ video }) => {
-      await peertubeHelpers.videos.removeVideo(video.id)
+  {
+    const serverActor = await peertubeHelpers.server.getServerActor()
+    logger.info('server actor name is %s', serverActor.preferredUsername)
+  }
 
-      logger.info('Video deleted by plugin four.')
+  {
+    logger.info('server url is %s', peertubeHelpers.config.getWebserverUrl())
+  }
+
+  {
+    const actions = {
+      blockServer,
+      unblockServer,
+      blockAccount,
+      unblockAccount,
+      blacklist,
+      unblacklist
     }
-  })
+
+    const router = getRouter()
+    router.post('/commander', async (req, res) => {
+      try {
+        await actions[req.body.command](peertubeHelpers, req.body)
+
+        res.sendStatus(204)
+      } catch (err) {
+        logger.error('Error in commander.', { err })
+        res.sendStatus(500)
+      }
+    })
+  }
 }
 
 async function unregister () {
@@ -37,3 +77,38 @@ module.exports = {
 }
 
 // ###########################################################################
+
+async function blockServer (peertubeHelpers, body) {
+  const serverActor = await peertubeHelpers.server.getServerActor()
+
+  await peertubeHelpers.moderation.blockServer({ byAccountId: serverActor.Account.id, hostToBlock: body.hostToBlock })
+}
+
+async function unblockServer (peertubeHelpers, body) {
+  const serverActor = await peertubeHelpers.server.getServerActor()
+
+  await peertubeHelpers.moderation.unblockServer({ byAccountId: serverActor.Account.id, hostToUnblock: body.hostToUnblock })
+}
+
+async function blockAccount (peertubeHelpers, body) {
+  const serverActor = await peertubeHelpers.server.getServerActor()
+
+  await peertubeHelpers.moderation.blockAccount({ byAccountId: serverActor.Account.id, handleToBlock: body.handleToBlock })
+}
+
+async function unblockAccount (peertubeHelpers, body) {
+  const serverActor = await peertubeHelpers.server.getServerActor()
+
+  await peertubeHelpers.moderation.unblockAccount({ byAccountId: serverActor.Account.id, handleToUnblock: body.handleToUnblock })
+}
+
+async function blacklist (peertubeHelpers, body) {
+  await peertubeHelpers.moderation.blacklistVideo({
+    videoIdOrUUID: body.videoUUID,
+    createOptions: body
+  })
+}
+
+async function unblacklist (peertubeHelpers, body) {
+  await peertubeHelpers.moderation.unblacklistVideo({ videoIdOrUUID: body.videoUUID })
+}
index dfe8ebe555d11c6addf2e2b10ebb3db915c1206c..0915603d046b77eb3ae44cede41635502cdf555f 100644 (file)
 /* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
 
 import 'mocha'
-import { cleanupTests, flushAndRunServer, ServerInfo, waitUntilLog } from '../../../shared/extra-utils/server/servers'
 import {
   checkVideoFilesWereRemoved,
+  doubleFollow,
   getPluginTestPath,
   getVideo,
   installPlugin,
+  makePostBodyRequest,
   setAccessTokensToServers,
   uploadVideoAndGetId,
-  viewVideo
+  viewVideo,
+  getVideosList,
+  waitJobs
 } from '../../../shared/extra-utils'
+import { cleanupTests, flushAndRunMultipleServers, ServerInfo, waitUntilLog } from '../../../shared/extra-utils/server/servers'
+import { expect } from 'chai'
+
+function postCommand (server: ServerInfo, command: string, bodyArg?: object) {
+  const body = { command }
+  if (bodyArg) Object.assign(body, bodyArg)
+
+  return makePostBodyRequest({
+    url: server.url,
+    path: '/plugins/test-four/router/commander',
+    fields: body,
+    statusCodeExpected: 204
+  })
+}
 
 describe('Test plugin helpers', function () {
-  let server: ServerInfo
+  let servers: ServerInfo[]
 
   before(async function () {
-    this.timeout(30000)
+    this.timeout(60000)
+
+    servers = await flushAndRunMultipleServers(2)
+    await setAccessTokensToServers(servers)
 
-    server = await flushAndRunServer(1)
-    await setAccessTokensToServers([ server ])
+    await doubleFollow(servers[0], servers[1])
 
     await installPlugin({
-      url: server.url,
-      accessToken: server.accessToken,
+      url: servers[0].url,
+      accessToken: servers[0].accessToken,
       path: getPluginTestPath('-four')
     })
   })
 
-  it('Should have logged things', async function () {
-    await waitUntilLog(server, 'localhost:' + server.port + ' peertube-plugin-test-four', 1, false)
-    await waitUntilLog(server, 'Hello world from plugin four', 1)
+  describe('Logger', function () {
+
+    it('Should have logged things', async function () {
+      await waitUntilLog(servers[0], 'localhost:' + servers[0].port + ' peertube-plugin-test-four', 1, false)
+      await waitUntilLog(servers[0], 'Hello world from plugin four', 1)
+    })
   })
 
-  it('Should have made a query', async function () {
-    await waitUntilLog(server, `root email is admin${server.internalServerNumber}@example.com`, 1)
+  describe('Database', function () {
+
+    it('Should have made a query', async function () {
+      await waitUntilLog(servers[0], `root email is admin${servers[0].internalServerNumber}@example.com`)
+    })
+  })
+
+  describe('Config', function () {
+
+    it('Should have the correct webserver url', async function () {
+      await waitUntilLog(servers[0], `server url is http://localhost:${servers[0].port}`)
+    })
+  })
+
+  describe('Server', function () {
+
+    it('Should get the server actor', async function () {
+      await waitUntilLog(servers[0], 'server actor name is peertube')
+    })
+  })
+
+  describe('Moderation', function () {
+    let videoUUIDServer1: string
+
+    before(async function () {
+      this.timeout(15000)
+
+      {
+        const res = await uploadVideoAndGetId({ server: servers[0], videoName: 'video server 1' })
+        videoUUIDServer1 = res.uuid
+      }
+
+      {
+        await uploadVideoAndGetId({ server: servers[1], videoName: 'video server 2' })
+      }
+
+      await waitJobs(servers)
+
+      const res = await getVideosList(servers[0].url)
+      const videos = res.body.data
+
+      expect(videos).to.have.lengthOf(2)
+    })
+
+    it('Should mute server 2', async function () {
+      this.timeout(10000)
+      await postCommand(servers[0], 'blockServer', { hostToBlock: `localhost:${servers[1].port}` })
+
+      const res = await getVideosList(servers[0].url)
+      const videos = res.body.data
+
+      expect(videos).to.have.lengthOf(1)
+      expect(videos[0].name).to.equal('video server 1')
+    })
+
+    it('Should unmute server 2', async function () {
+      await postCommand(servers[0], 'unblockServer', { hostToUnblock: `localhost:${servers[1].port}` })
+
+      const res = await getVideosList(servers[0].url)
+      const videos = res.body.data
+
+      expect(videos).to.have.lengthOf(2)
+    })
+
+    it('Should mute account of server 2', async function () {
+      await postCommand(servers[0], 'blockAccount', { handleToBlock: `root@localhost:${servers[1].port}` })
+
+      const res = await getVideosList(servers[0].url)
+      const videos = res.body.data
+
+      expect(videos).to.have.lengthOf(1)
+      expect(videos[0].name).to.equal('video server 1')
+    })
+
+    it('Should unmute account of server 2', async function () {
+      await postCommand(servers[0], 'unblockAccount', { handleToUnblock: `root@localhost:${servers[1].port}` })
+
+      const res = await getVideosList(servers[0].url)
+      const videos = res.body.data
+
+      expect(videos).to.have.lengthOf(2)
+    })
+
+    it('Should blacklist video', async function () {
+      this.timeout(10000)
+
+      await postCommand(servers[0], 'blacklist', { videoUUID: videoUUIDServer1, unfederate: true })
+
+      await waitJobs(servers)
+
+      for (const server of servers) {
+        const res = await getVideosList(server.url)
+        const videos = res.body.data
+
+        expect(videos).to.have.lengthOf(1)
+        expect(videos[0].name).to.equal('video server 2')
+      }
+    })
+
+    it('Should unblacklist video', async function () {
+      this.timeout(10000)
+
+      await postCommand(servers[0], 'unblacklist', { videoUUID: videoUUIDServer1 })
+
+      await waitJobs(servers)
+
+      for (const server of servers) {
+        const res = await getVideosList(server.url)
+        const videos = res.body.data
+
+        expect(videos).to.have.lengthOf(2)
+      }
+    })
   })
 
-  it('Should remove a video after a view', async function () {
-    this.timeout(20000)
+  describe('Videos', function () {
+    let videoUUID: string
+
+    before(async () => {
+      const res = await uploadVideoAndGetId({ server: servers[0], videoName: 'video1' })
+      videoUUID = res.uuid
+    })
 
-    const videoUUID = (await uploadVideoAndGetId({ server: server, videoName: 'video1' })).uuid
+    it('Should remove a video after a view', async function () {
+      this.timeout(20000)
 
-    // Should not throw -> video exists
-    await getVideo(server.url, videoUUID)
-    // Should delete the video
-    await viewVideo(server.url, videoUUID)
+      // Should not throw -> video exists
+      await getVideo(servers[0].url, videoUUID)
+      // Should delete the video
+      await viewVideo(servers[0].url, videoUUID)
 
-    await waitUntilLog(server, 'Video deleted by plugin four.', 1)
+      await waitUntilLog(servers[0], 'Video deleted by plugin four.')
 
-    try {
-      // Should throw because the video should have been deleted
-      await getVideo(server.url, videoUUID)
-      throw new Error('Video exists')
-    } catch (err) {
-      if (err.message.includes('exists')) throw err
-    }
+      try {
+        // Should throw because the video should have been deleted
+        await getVideo(servers[0].url, videoUUID)
+        throw new Error('Video exists')
+      } catch (err) {
+        if (err.message.includes('exists')) throw err
+      }
 
-    await checkVideoFilesWereRemoved(videoUUID, server.internalServerNumber)
+      await checkVideoFilesWereRemoved(videoUUID, servers[0].internalServerNumber)
+    })
+
+    it('Should have fetched the video by URL', async function () {
+      await waitUntilLog(servers[0], `video from DB uuid is ${videoUUID}`)
+    })
   })
 
   after(async function () {
-    await cleanupTests([ server ])
+    await cleanupTests(servers)
   })
 })
index 7f933b43aa4f602eeabf21239d13a41936785029..8f1d66007df44e46aaef41332e03d07a3ca8e358 100644 (file)
@@ -1,19 +1,23 @@
-import { PluginSettingsManager } from '../../../shared/models/plugins/plugin-settings-manager.model'
-import { PluginStorageManager } from '../../../shared/models/plugins/plugin-storage-manager.model'
-import { RegisterServerHookOptions } from '../../../shared/models/plugins/register-server-hook.model'
-import { RegisterServerSettingOptions } from '../../../shared/models/plugins/register-server-setting.model'
-import { PluginVideoCategoryManager } from '../../../shared/models/plugins/plugin-video-category-manager.model'
-import { PluginVideoLanguageManager } from '../../../shared/models/plugins/plugin-video-language-manager.model'
-import { PluginVideoLicenceManager } from '../../../shared/models/plugins/plugin-video-licence-manager.model'
-import { Logger } from 'winston'
+import * as Bluebird from 'bluebird'
 import { Router } from 'express'
-import { PluginVideoPrivacyManager } from '@shared/models/plugins/plugin-video-privacy-manager.model'
+import { Logger } from 'winston'
+import { ActorModel } from '@server/models/activitypub/actor'
+import { VideoBlacklistCreate } from '@shared/models'
 import { PluginPlaylistPrivacyManager } from '@shared/models/plugins/plugin-playlist-privacy-manager.model'
+import { PluginVideoPrivacyManager } from '@shared/models/plugins/plugin-video-privacy-manager.model'
 import {
   RegisterServerAuthExternalOptions,
   RegisterServerAuthExternalResult,
   RegisterServerAuthPassOptions
 } from '@shared/models/plugins/register-server-auth.model'
+import { PluginSettingsManager } from '../../../shared/models/plugins/plugin-settings-manager.model'
+import { PluginStorageManager } from '../../../shared/models/plugins/plugin-storage-manager.model'
+import { PluginVideoCategoryManager } from '../../../shared/models/plugins/plugin-video-category-manager.model'
+import { PluginVideoLanguageManager } from '../../../shared/models/plugins/plugin-video-language-manager.model'
+import { PluginVideoLicenceManager } from '../../../shared/models/plugins/plugin-video-licence-manager.model'
+import { RegisterServerHookOptions } from '../../../shared/models/plugins/register-server-hook.model'
+import { RegisterServerSettingOptions } from '../../../shared/models/plugins/register-server-setting.model'
+import { MVideoThumbnail } from '../models'
 
 export type PeerTubeHelpers = {
   logger: Logger
@@ -23,12 +27,28 @@ export type PeerTubeHelpers = {
   }
 
   videos: {
+    loadByUrl: (url: string) => Bluebird<MVideoThumbnail>
+
     removeVideo: (videoId: number) => Promise<void>
   }
 
   config: {
     getWebserverUrl: () => string
   }
+
+  moderation: {
+    blockServer: (options: { byAccountId: number, hostToBlock: string }) => Promise<void>
+    unblockServer: (options: { byAccountId: number, hostToUnblock: string }) => Promise<void>
+    blockAccount: (options: { byAccountId: number, handleToBlock: string }) => Promise<void>
+    unblockAccount: (options: { byAccountId: number, handleToUnblock: string }) => Promise<void>
+
+    blacklistVideo: (options: { videoIdOrUUID: number | string, createOptions: VideoBlacklistCreate }) => Promise<void>
+    unblacklistVideo: (options: { videoIdOrUUID: number | string }) => Promise<void>
+  }
+
+  server: {
+    getServerActor: () => Promise<ActorModel>
+  }
 }
 
 export type RegisterServerOptions = {