From 7e98a7df7d04e19ba67163a86c7b876d78d76839 Mon Sep 17 00:00:00 2001
From: Chocobozzz <me@florianbigard.com>
Date: Wed, 23 Mar 2022 14:24:50 +0100
Subject: [PATCH] Remove activitypub helper

Put functions in lib/activitypub instead
---
 server/controllers/activitypub/client.ts      |  9 +-
 server/controllers/activitypub/outbox.ts      |  3 +-
 server/lib/activitypub/activity.ts            | 21 ++++
 server/lib/activitypub/actors/get.ts          |  4 +-
 .../actors/shared/url-to-object.ts            |  3 +-
 server/lib/activitypub/collection.ts          | 62 ++++++++++++
 .../activitypub/context.ts}                   | 98 +------------------
 .../activitypub/playlists/create-update.ts    |  4 +-
 server/lib/activitypub/playlists/get.ts       |  2 +-
 .../playlists/shared/url-to-object.ts         |  2 +-
 .../activitypub/process/process-announce.ts   | 10 +-
 .../lib/activitypub/process/process-flag.ts   |  2 +-
 .../lib/activitypub/process/process-follow.ts |  2 +-
 .../lib/activitypub/process/process-like.ts   |  2 +-
 server/lib/activitypub/process/process.ts     |  3 +-
 server/lib/activitypub/share.ts               |  4 +-
 server/lib/activitypub/url.ts                 | 31 +++++-
 server/lib/activitypub/video-comments.ts      |  2 +-
 server/lib/activitypub/videos/get.ts          |  2 +-
 .../videos/shared/abstract-builder.ts         |  2 +-
 .../lib/activitypub/videos/shared/trackers.ts |  2 +-
 .../videos/shared/url-to-object.ts            |  2 +-
 .../job-queue/handlers/activitypub-cleaner.ts |  2 +-
 .../handlers/utils/activitypub-http-utils.ts  |  2 +-
 server/middlewares/activitypub.ts             |  2 +-
 server/models/actor/actor.ts                  |  2 +-
 server/models/video/video-file.ts             |  2 +-
 server/models/video/video-playlist.ts         |  2 +-
 server/tests/api/activitypub/helpers.ts       |  2 +-
 server/tests/api/activitypub/security.ts      |  3 +-
 server/tests/shared/requests.ts               |  2 +-
 31 files changed, 155 insertions(+), 136 deletions(-)
 create mode 100644 server/lib/activitypub/activity.ts
 create mode 100644 server/lib/activitypub/collection.ts
 rename server/{helpers/activitypub.ts => lib/activitypub/context.ts} (54%)

diff --git a/server/controllers/activitypub/client.ts b/server/controllers/activitypub/client.ts
index fc27ebbe8..99637dbab 100644
--- a/server/controllers/activitypub/client.ts
+++ b/server/controllers/activitypub/client.ts
@@ -1,10 +1,11 @@
 import cors from 'cors'
 import express from 'express'
+import { activityPubCollectionPagination } from '@server/lib/activitypub/collection'
+import { activityPubContextify } from '@server/lib/activitypub/context'
 import { getServerActor } from '@server/models/application/application'
 import { MAccountId, MActorId, MChannelId, MVideoId } from '@server/types/models'
 import { VideoPrivacy, VideoRateType } from '../../../shared/models/videos'
 import { VideoPlaylistPrivacy } from '../../../shared/models/videos/playlist/video-playlist-privacy.model'
-import { activityPubCollectionPagination, activityPubContextify } from '../../helpers/activitypub'
 import { ROUTE_CACHE_LIFETIME, WEBSERVER } from '../../initializers/constants'
 import { audiencify, getAudience } from '../../lib/activitypub/audience'
 import { buildAnnounceWithVideoAudience, buildLikeActivity } from '../../lib/activitypub/send'
@@ -400,7 +401,7 @@ function videoPlaylistElementController (req: express.Request, res: express.Resp
 
 // ---------------------------------------------------------------------------
 
-async function actorFollowing (req: express.Request, actor: MActorId) {
+function actorFollowing (req: express.Request, actor: MActorId) {
   const handler = (start: number, count: number) => {
     return ActorFollowModel.listAcceptedFollowingUrlsForApi([ actor.id ], undefined, start, count)
   }
@@ -408,7 +409,7 @@ async function actorFollowing (req: express.Request, actor: MActorId) {
   return activityPubCollectionPagination(WEBSERVER.URL + req.path, handler, req.query.page)
 }
 
-async function actorFollowers (req: express.Request, actor: MActorId) {
+function actorFollowers (req: express.Request, actor: MActorId) {
   const handler = (start: number, count: number) => {
     return ActorFollowModel.listAcceptedFollowerUrlsForAP([ actor.id ], undefined, start, count)
   }
@@ -416,7 +417,7 @@ async function actorFollowers (req: express.Request, actor: MActorId) {
   return activityPubCollectionPagination(WEBSERVER.URL + req.path, handler, req.query.page)
 }
 
-async function actorPlaylists (req: express.Request, options: { account: MAccountId } | { channel: MChannelId }) {
+function actorPlaylists (req: express.Request, options: { account: MAccountId } | { channel: MChannelId }) {
   const handler = (start: number, count: number) => {
     return VideoPlaylistModel.listPublicUrlsOfForAP(options, start, count)
   }
diff --git a/server/controllers/activitypub/outbox.ts b/server/controllers/activitypub/outbox.ts
index cdef8e969..4e7a3afeb 100644
--- a/server/controllers/activitypub/outbox.ts
+++ b/server/controllers/activitypub/outbox.ts
@@ -1,8 +1,9 @@
 import express from 'express'
+import { activityPubCollectionPagination } from '@server/lib/activitypub/collection'
+import { activityPubContextify } from '@server/lib/activitypub/context'
 import { MActorLight } from '@server/types/models'
 import { Activity } from '../../../shared/models/activitypub/activity'
 import { VideoPrivacy } from '../../../shared/models/videos'
-import { activityPubCollectionPagination, activityPubContextify } from '../../helpers/activitypub'
 import { logger } from '../../helpers/logger'
 import { buildAudience } from '../../lib/activitypub/audience'
 import { buildAnnounceActivity, buildCreateActivity } from '../../lib/activitypub/send'
diff --git a/server/lib/activitypub/activity.ts b/server/lib/activitypub/activity.ts
new file mode 100644
index 000000000..215b50b69
--- /dev/null
+++ b/server/lib/activitypub/activity.ts
@@ -0,0 +1,21 @@
+import { signJsonLDObject } from '@server/helpers/peertube-crypto'
+import { MActor } from '@server/types/models'
+import { ContextType } from '@shared/models'
+import { activityPubContextify } from './context'
+
+function buildSignedActivity <T> (byActor: MActor, data: T, contextType?: ContextType) {
+  const activity = activityPubContextify(data, contextType)
+
+  return signJsonLDObject(byActor, activity)
+}
+
+function getAPId (object: string | { id: string }) {
+  if (typeof object === 'string') return object
+
+  return object.id
+}
+
+export {
+  buildSignedActivity,
+  getAPId
+}
diff --git a/server/lib/activitypub/actors/get.ts b/server/lib/activitypub/actors/get.ts
index 4200ddb4d..d2b651082 100644
--- a/server/lib/activitypub/actors/get.ts
+++ b/server/lib/activitypub/actors/get.ts
@@ -1,11 +1,11 @@
-
-import { checkUrlsSameHost, getAPId } from '@server/helpers/activitypub'
 import { retryTransactionWrapper } from '@server/helpers/database-utils'
 import { logger } from '@server/helpers/logger'
 import { JobQueue } from '@server/lib/job-queue'
 import { ActorLoadByUrlType, loadActorByUrl } from '@server/lib/model-loaders'
 import { MActor, MActorAccountChannelId, MActorAccountChannelIdActor, MActorAccountId, MActorFullActor } from '@server/types/models'
 import { ActivityPubActor } from '@shared/models'
+import { getAPId } from '../activity'
+import { checkUrlsSameHost } from '../url'
 import { refreshActorIfNeeded } from './refresh'
 import { APActorCreator, fetchRemoteActor } from './shared'
 
diff --git a/server/lib/activitypub/actors/shared/url-to-object.ts b/server/lib/activitypub/actors/shared/url-to-object.ts
index f4f16b044..982d52b79 100644
--- a/server/lib/activitypub/actors/shared/url-to-object.ts
+++ b/server/lib/activitypub/actors/shared/url-to-object.ts
@@ -1,9 +1,8 @@
-
-import { checkUrlsSameHost } from '@server/helpers/activitypub'
 import { sanitizeAndCheckActorObject } from '@server/helpers/custom-validators/activitypub/actor'
 import { logger } from '@server/helpers/logger'
 import { doJSONRequest } from '@server/helpers/requests'
 import { ActivityPubActor, ActivityPubOrderedCollection } from '@shared/models'
+import { checkUrlsSameHost } from '../../url'
 
 async function fetchRemoteActor (actorUrl: string): Promise<{ statusCode: number, actorObject: ActivityPubActor }> {
   logger.info('Fetching remote actor %s.', actorUrl)
diff --git a/server/lib/activitypub/collection.ts b/server/lib/activitypub/collection.ts
new file mode 100644
index 000000000..43a704aa4
--- /dev/null
+++ b/server/lib/activitypub/collection.ts
@@ -0,0 +1,62 @@
+import Bluebird from 'bluebird'
+import validator from 'validator'
+import { pageToStartAndCount } from '@server/helpers/core-utils'
+import { ACTIVITY_PUB } from '@server/initializers/constants'
+import { ResultList } from '@shared/models'
+
+type ActivityPubCollectionPaginationHandler = (start: number, count: number) => Bluebird<ResultList<any>> | Promise<ResultList<any>>
+
+async function activityPubCollectionPagination (
+  baseUrl: string,
+  handler: ActivityPubCollectionPaginationHandler,
+  page?: any,
+  size = ACTIVITY_PUB.COLLECTION_ITEMS_PER_PAGE
+) {
+  if (!page || !validator.isInt(page)) {
+    // We just display the first page URL, we only need the total items
+    const result = await handler(0, 1)
+
+    return {
+      id: baseUrl,
+      type: 'OrderedCollectionPage',
+      totalItems: result.total,
+      first: result.data.length === 0
+        ? undefined
+        : baseUrl + '?page=1'
+    }
+  }
+
+  const { start, count } = pageToStartAndCount(page, size)
+  const result = await handler(start, count)
+
+  let next: string | undefined
+  let prev: string | undefined
+
+  // Assert page is a number
+  page = parseInt(page, 10)
+
+  // There are more results
+  if (result.total > page * size) {
+    next = baseUrl + '?page=' + (page + 1)
+  }
+
+  if (page > 1) {
+    prev = baseUrl + '?page=' + (page - 1)
+  }
+
+  return {
+    id: baseUrl + '?page=' + page,
+    type: 'OrderedCollectionPage',
+    prev,
+    next,
+    partOf: baseUrl,
+    orderedItems: result.data,
+    totalItems: result.total
+  }
+}
+
+// ---------------------------------------------------------------------------
+
+export {
+  activityPubCollectionPagination
+}
diff --git a/server/helpers/activitypub.ts b/server/lib/activitypub/context.ts
similarity index 54%
rename from server/helpers/activitypub.ts
rename to server/lib/activitypub/context.ts
index 9d6d8b2fa..71f08da80 100644
--- a/server/helpers/activitypub.ts
+++ b/server/lib/activitypub/context.ts
@@ -1,12 +1,4 @@
-import Bluebird from 'bluebird'
-import { URL } from 'url'
-import validator from 'validator'
-import { ContextType } from '@shared/models/activitypub/context'
-import { ResultList } from '../../shared/models'
-import { ACTIVITY_PUB, REMOTE_SCHEME } from '../initializers/constants'
-import { MActor, MVideoWithHost } from '../types/models'
-import { pageToStartAndCount } from './core-utils'
-import { signJsonLDObject } from './peertube-crypto'
+import { ContextType } from '@shared/models'
 
 function getContextData (type: ContextType) {
   const context: any[] = [
@@ -139,91 +131,7 @@ function activityPubContextify <T> (data: T, type: ContextType = 'All') {
   return Object.assign({}, data, getContextData(type))
 }
 
-type ActivityPubCollectionPaginationHandler = (start: number, count: number) => Bluebird<ResultList<any>> | Promise<ResultList<any>>
-async function activityPubCollectionPagination (
-  baseUrl: string,
-  handler: ActivityPubCollectionPaginationHandler,
-  page?: any,
-  size = ACTIVITY_PUB.COLLECTION_ITEMS_PER_PAGE
-) {
-  if (!page || !validator.isInt(page)) {
-    // We just display the first page URL, we only need the total items
-    const result = await handler(0, 1)
-
-    return {
-      id: baseUrl,
-      type: 'OrderedCollectionPage',
-      totalItems: result.total,
-      first: result.data.length === 0
-        ? undefined
-        : baseUrl + '?page=1'
-    }
-  }
-
-  const { start, count } = pageToStartAndCount(page, size)
-  const result = await handler(start, count)
-
-  let next: string | undefined
-  let prev: string | undefined
-
-  // Assert page is a number
-  page = parseInt(page, 10)
-
-  // There are more results
-  if (result.total > page * size) {
-    next = baseUrl + '?page=' + (page + 1)
-  }
-
-  if (page > 1) {
-    prev = baseUrl + '?page=' + (page - 1)
-  }
-
-  return {
-    id: baseUrl + '?page=' + page,
-    type: 'OrderedCollectionPage',
-    prev,
-    next,
-    partOf: baseUrl,
-    orderedItems: result.data,
-    totalItems: result.total
-  }
-
-}
-
-function buildSignedActivity <T> (byActor: MActor, data: T, contextType?: ContextType) {
-  const activity = activityPubContextify(data, contextType)
-
-  return signJsonLDObject(byActor, activity)
-}
-
-function getAPId (object: string | { id: string }) {
-  if (typeof object === 'string') return object
-
-  return object.id
-}
-
-function checkUrlsSameHost (url1: string, url2: string) {
-  const idHost = new URL(url1).host
-  const actorHost = new URL(url2).host
-
-  return idHost && actorHost && idHost.toLowerCase() === actorHost.toLowerCase()
-}
-
-function buildRemoteVideoBaseUrl (video: MVideoWithHost, path: string, scheme?: string) {
-  if (!scheme) scheme = REMOTE_SCHEME.HTTP
-
-  const host = video.VideoChannel.Actor.Server.host
-
-  return scheme + '://' + host + path
-}
-
-// ---------------------------------------------------------------------------
-
 export {
-  checkUrlsSameHost,
-  getAPId,
-  activityPubContextify,
-  activityPubCollectionPagination,
-  buildSignedActivity,
-  buildRemoteVideoBaseUrl
+  getContextData,
+  activityPubContextify
 }
diff --git a/server/lib/activitypub/playlists/create-update.ts b/server/lib/activitypub/playlists/create-update.ts
index ef572c803..c28700be6 100644
--- a/server/lib/activitypub/playlists/create-update.ts
+++ b/server/lib/activitypub/playlists/create-update.ts
@@ -1,5 +1,4 @@
 import { map } from 'bluebird'
-import { getAPId } from '@server/helpers/activitypub'
 import { isArray } from '@server/helpers/custom-validators/misc'
 import { logger, loggerTagsFactory } from '@server/helpers/logger'
 import { CRAWL_REQUEST_CONCURRENCY } from '@server/initializers/constants'
@@ -9,8 +8,9 @@ import { VideoPlaylistModel } from '@server/models/video/video-playlist'
 import { VideoPlaylistElementModel } from '@server/models/video/video-playlist-element'
 import { FilteredModelAttributes } from '@server/types'
 import { MThumbnail, MVideoPlaylist, MVideoPlaylistFull, MVideoPlaylistVideosLength } from '@server/types/models'
-import { AttributesOnly } from '@shared/typescript-utils'
 import { PlaylistObject } from '@shared/models'
+import { AttributesOnly } from '@shared/typescript-utils'
+import { getAPId } from '../activity'
 import { getOrCreateAPActor } from '../actors'
 import { crawlCollectionPage } from '../crawl'
 import { getOrCreateAPVideo } from '../videos'
diff --git a/server/lib/activitypub/playlists/get.ts b/server/lib/activitypub/playlists/get.ts
index be8456b19..bfaf52cc9 100644
--- a/server/lib/activitypub/playlists/get.ts
+++ b/server/lib/activitypub/playlists/get.ts
@@ -1,7 +1,7 @@
-import { getAPId } from '@server/helpers/activitypub'
 import { VideoPlaylistModel } from '@server/models/video/video-playlist'
 import { MVideoPlaylistFullSummary } from '@server/types/models'
 import { APObject } from '@shared/models'
+import { getAPId } from '../activity'
 import { createOrUpdateVideoPlaylist } from './create-update'
 import { scheduleRefreshIfNeeded } from './refresh'
 import { fetchRemoteVideoPlaylist } from './shared'
diff --git a/server/lib/activitypub/playlists/shared/url-to-object.ts b/server/lib/activitypub/playlists/shared/url-to-object.ts
index ec8c01255..f895db587 100644
--- a/server/lib/activitypub/playlists/shared/url-to-object.ts
+++ b/server/lib/activitypub/playlists/shared/url-to-object.ts
@@ -1,9 +1,9 @@
 import { isArray } from 'lodash'
-import { checkUrlsSameHost } from '@server/helpers/activitypub'
 import { isPlaylistElementObjectValid, isPlaylistObjectValid } from '@server/helpers/custom-validators/activitypub/playlist'
 import { logger, loggerTagsFactory } from '@server/helpers/logger'
 import { doJSONRequest } from '@server/helpers/requests'
 import { PlaylistElementObject, PlaylistObject } from '@shared/models'
+import { checkUrlsSameHost } from '../../url'
 
 async function fetchRemoteVideoPlaylist (playlistUrl: string): Promise<{ statusCode: number, playlistObject: PlaylistObject }> {
   const lTags = loggerTagsFactory('ap', 'video-playlist', playlistUrl)
diff --git a/server/lib/activitypub/process/process-announce.ts b/server/lib/activitypub/process/process-announce.ts
index 200f8ce11..9cc87ee27 100644
--- a/server/lib/activitypub/process/process-announce.ts
+++ b/server/lib/activitypub/process/process-announce.ts
@@ -1,14 +1,14 @@
+import { getAPId } from '@server/lib/activitypub/activity'
 import { ActivityAnnounce } from '../../../../shared/models/activitypub'
 import { retryTransactionWrapper } from '../../../helpers/database-utils'
+import { logger } from '../../../helpers/logger'
 import { sequelizeTypescript } from '../../../initializers/database'
 import { VideoShareModel } from '../../../models/video/video-share'
-import { forwardVideoRelatedActivity } from '../send/shared/send-utils'
-import { getOrCreateAPVideo } from '../videos'
-import { Notifier } from '../../notifier'
-import { logger } from '../../../helpers/logger'
 import { APProcessorOptions } from '../../../types/activitypub-processor.model'
 import { MActorSignature, MVideoAccountLightBlacklistAllFiles } from '../../../types/models'
-import { getAPId } from '@server/helpers/activitypub'
+import { Notifier } from '../../notifier'
+import { forwardVideoRelatedActivity } from '../send/shared/send-utils'
+import { getOrCreateAPVideo } from '../videos'
 
 async function processAnnounceActivity (options: APProcessorOptions<ActivityAnnounce>) {
   const { activity, byActor: actorAnnouncer } = options
diff --git a/server/lib/activitypub/process/process-flag.ts b/server/lib/activitypub/process/process-flag.ts
index a15d07a62..10f58ef27 100644
--- a/server/lib/activitypub/process/process-flag.ts
+++ b/server/lib/activitypub/process/process-flag.ts
@@ -4,10 +4,10 @@ import { VideoModel } from '@server/models/video/video'
 import { VideoCommentModel } from '@server/models/video/video-comment'
 import { abusePredefinedReasonsMap } from '@shared/core-utils/abuse'
 import { AbuseObject, AbuseState, ActivityCreate, ActivityFlag } from '@shared/models'
-import { getAPId } from '../../../helpers/activitypub'
 import { retryTransactionWrapper } from '../../../helpers/database-utils'
 import { logger } from '../../../helpers/logger'
 import { sequelizeTypescript } from '../../../initializers/database'
+import { getAPId } from '../../../lib/activitypub/activity'
 import { APProcessorOptions } from '../../../types/activitypub-processor.model'
 import { MAccountDefault, MActorSignature, MCommentOwnerVideo } from '../../../types/models'
 
diff --git a/server/lib/activitypub/process/process-follow.ts b/server/lib/activitypub/process/process-follow.ts
index e44590ffc..93df7e191 100644
--- a/server/lib/activitypub/process/process-follow.ts
+++ b/server/lib/activitypub/process/process-follow.ts
@@ -1,10 +1,10 @@
 import { getServerActor } from '@server/models/application/application'
 import { ActivityFollow } from '../../../../shared/models/activitypub'
-import { getAPId } from '../../../helpers/activitypub'
 import { retryTransactionWrapper } from '../../../helpers/database-utils'
 import { logger } from '../../../helpers/logger'
 import { CONFIG } from '../../../initializers/config'
 import { sequelizeTypescript } from '../../../initializers/database'
+import { getAPId } from '../../../lib/activitypub/activity'
 import { ActorModel } from '../../../models/actor/actor'
 import { ActorFollowModel } from '../../../models/actor/actor-follow'
 import { APProcessorOptions } from '../../../types/activitypub-processor.model'
diff --git a/server/lib/activitypub/process/process-like.ts b/server/lib/activitypub/process/process-like.ts
index 93afb5edf..1aee756d8 100644
--- a/server/lib/activitypub/process/process-like.ts
+++ b/server/lib/activitypub/process/process-like.ts
@@ -1,8 +1,8 @@
 import { VideoModel } from '@server/models/video/video'
 import { ActivityLike } from '../../../../shared/models/activitypub'
-import { getAPId } from '../../../helpers/activitypub'
 import { retryTransactionWrapper } from '../../../helpers/database-utils'
 import { sequelizeTypescript } from '../../../initializers/database'
+import { getAPId } from '../../../lib/activitypub/activity'
 import { AccountVideoRateModel } from '../../../models/account/account-video-rate'
 import { APProcessorOptions } from '../../../types/activitypub-processor.model'
 import { MActorSignature } from '../../../types/models'
diff --git a/server/lib/activitypub/process/process.ts b/server/lib/activitypub/process/process.ts
index 02a23d098..2bc3dce03 100644
--- a/server/lib/activitypub/process/process.ts
+++ b/server/lib/activitypub/process/process.ts
@@ -1,10 +1,11 @@
 import { StatsManager } from '@server/lib/stat-manager'
 import { Activity, ActivityType } from '../../../../shared/models/activitypub'
-import { checkUrlsSameHost, getAPId } from '../../../helpers/activitypub'
 import { logger } from '../../../helpers/logger'
 import { APProcessorOptions } from '../../../types/activitypub-processor.model'
 import { MActorDefault, MActorSignature } from '../../../types/models'
+import { getAPId } from '../activity'
 import { getOrCreateAPActor } from '../actors'
+import { checkUrlsSameHost } from '../url'
 import { processAcceptActivity } from './process-accept'
 import { processAnnounceActivity } from './process-announce'
 import { processCreateActivity } from './process-create'
diff --git a/server/lib/activitypub/share.ts b/server/lib/activitypub/share.ts
index b18761174..0fefcbbc5 100644
--- a/server/lib/activitypub/share.ts
+++ b/server/lib/activitypub/share.ts
@@ -1,15 +1,15 @@
 import { map } from 'bluebird'
 import { Transaction } from 'sequelize'
 import { getServerActor } from '@server/models/application/application'
-import { checkUrlsSameHost, getAPId } from '../../helpers/activitypub'
 import { logger, loggerTagsFactory } from '../../helpers/logger'
 import { doJSONRequest } from '../../helpers/requests'
 import { CRAWL_REQUEST_CONCURRENCY } from '../../initializers/constants'
 import { VideoShareModel } from '../../models/video/video-share'
 import { MChannelActorLight, MVideo, MVideoAccountLight, MVideoId } from '../../types/models/video'
+import { getAPId } from './activity'
 import { getOrCreateAPActor } from './actors'
 import { sendUndoAnnounce, sendVideoAnnounce } from './send'
-import { getLocalVideoAnnounceActivityPubUrl } from './url'
+import { checkUrlsSameHost, getLocalVideoAnnounceActivityPubUrl } from './url'
 
 const lTags = loggerTagsFactory('share')
 
diff --git a/server/lib/activitypub/url.ts b/server/lib/activitypub/url.ts
index 338398f2b..50be4fac9 100644
--- a/server/lib/activitypub/url.ts
+++ b/server/lib/activitypub/url.ts
@@ -1,4 +1,4 @@
-import { WEBSERVER } from '../../initializers/constants'
+import { REMOTE_SCHEME, WEBSERVER } from '../../initializers/constants'
 import {
   MAbuseFull,
   MAbuseId,
@@ -10,7 +10,8 @@ import {
   MVideoId,
   MVideoPlaylistElement,
   MVideoUrl,
-  MVideoUUID
+  MVideoUUID,
+  MVideoWithHost
 } from '../../types/models'
 import { MVideoFileVideoUUID } from '../../types/models/video/video-file'
 import { MVideoPlaylist, MVideoPlaylistUUID } from '../../types/models/video/video-playlist'
@@ -121,6 +122,27 @@ function getAbuseTargetUrl (abuse: MAbuseFull) {
     abuse.FlaggedAccount.Actor.url
 }
 
+// ---------------------------------------------------------------------------
+
+function buildRemoteVideoBaseUrl (video: MVideoWithHost, path: string, scheme?: string) {
+  if (!scheme) scheme = REMOTE_SCHEME.HTTP
+
+  const host = video.VideoChannel.Actor.Server.host
+
+  return scheme + '://' + host + path
+}
+
+// ---------------------------------------------------------------------------
+
+function checkUrlsSameHost (url1: string, url2: string) {
+  const idHost = new URL(url1).host
+  const actorHost = new URL(url2).host
+
+  return idHost && actorHost && idHost.toLowerCase() === actorHost.toLowerCase()
+}
+
+// ---------------------------------------------------------------------------
+
 export {
   getLocalVideoActivityPubUrl,
   getLocalVideoPlaylistActivityPubUrl,
@@ -145,5 +167,8 @@ export {
   getLocalVideoCommentsActivityPubUrl,
   getLocalVideoLikesActivityPubUrl,
   getLocalVideoDislikesActivityPubUrl,
-  getAbuseTargetUrl
+
+  getAbuseTargetUrl,
+  checkUrlsSameHost,
+  buildRemoteVideoBaseUrl
 }
diff --git a/server/lib/activitypub/video-comments.ts b/server/lib/activitypub/video-comments.ts
index 2c7da3e00..911c7cd30 100644
--- a/server/lib/activitypub/video-comments.ts
+++ b/server/lib/activitypub/video-comments.ts
@@ -1,5 +1,4 @@
 import { map } from 'bluebird'
-import { checkUrlsSameHost } from '../../helpers/activitypub'
 import { sanitizeAndCheckVideoCommentObject } from '../../helpers/custom-validators/activitypub/video-comments'
 import { logger } from '../../helpers/logger'
 import { doJSONRequest } from '../../helpers/requests'
@@ -7,6 +6,7 @@ import { ACTIVITY_PUB, CRAWL_REQUEST_CONCURRENCY } from '../../initializers/cons
 import { VideoCommentModel } from '../../models/video/video-comment'
 import { MCommentOwner, MCommentOwnerVideo, MVideoAccountLightBlacklistAllFiles } from '../../types/models/video'
 import { getOrCreateAPActor } from './actors'
+import { checkUrlsSameHost } from './url'
 import { getOrCreateAPVideo } from './videos'
 
 type ResolveThreadParams = {
diff --git a/server/lib/activitypub/videos/get.ts b/server/lib/activitypub/videos/get.ts
index b13c6ceeb..d7500c71a 100644
--- a/server/lib/activitypub/videos/get.ts
+++ b/server/lib/activitypub/videos/get.ts
@@ -1,9 +1,9 @@
-import { getAPId } from '@server/helpers/activitypub'
 import { retryTransactionWrapper } from '@server/helpers/database-utils'
 import { JobQueue } from '@server/lib/job-queue'
 import { loadVideoByUrl, VideoLoadByUrlType } from '@server/lib/model-loaders'
 import { MVideoAccountLightBlacklistAllFiles, MVideoImmutable, MVideoThumbnail } from '@server/types/models'
 import { APObject } from '@shared/models'
+import { getAPId } from '../activity'
 import { refreshVideoIfNeeded } from './refresh'
 import { APVideoCreator, fetchRemoteVideo, SyncParam, syncVideoExternalAttributes } from './shared'
 
diff --git a/server/lib/activitypub/videos/shared/abstract-builder.ts b/server/lib/activitypub/videos/shared/abstract-builder.ts
index 788223b48..f299ba4fd 100644
--- a/server/lib/activitypub/videos/shared/abstract-builder.ts
+++ b/server/lib/activitypub/videos/shared/abstract-builder.ts
@@ -1,5 +1,4 @@
 import { Transaction } from 'sequelize/types'
-import { checkUrlsSameHost } from '@server/helpers/activitypub'
 import { deleteAllModels, filterNonExistingModels } from '@server/helpers/database-utils'
 import { logger, LoggerTagsFn } from '@server/helpers/logger'
 import { updatePlaceholderThumbnail, updateVideoMiniatureFromUrl } from '@server/lib/thumbnail'
@@ -11,6 +10,7 @@ import { VideoStreamingPlaylistModel } from '@server/models/video/video-streamin
 import { MStreamingPlaylistFilesVideo, MThumbnail, MVideoCaption, MVideoFile, MVideoFullLight, MVideoThumbnail } from '@server/types/models'
 import { ActivityTagObject, ThumbnailType, VideoObject, VideoStreamingPlaylistType } from '@shared/models'
 import { getOrCreateAPActor } from '../../actors'
+import { checkUrlsSameHost } from '../../url'
 import {
   getCaptionAttributesFromObject,
   getFileAttributesFromUrl,
diff --git a/server/lib/activitypub/videos/shared/trackers.ts b/server/lib/activitypub/videos/shared/trackers.ts
index 1c5fc4f84..2418f45c2 100644
--- a/server/lib/activitypub/videos/shared/trackers.ts
+++ b/server/lib/activitypub/videos/shared/trackers.ts
@@ -1,11 +1,11 @@
 import { Transaction } from 'sequelize/types'
-import { buildRemoteVideoBaseUrl } from '@server/helpers/activitypub'
 import { isAPVideoTrackerUrlObject } from '@server/helpers/custom-validators/activitypub/videos'
 import { isArray } from '@server/helpers/custom-validators/misc'
 import { REMOTE_SCHEME } from '@server/initializers/constants'
 import { TrackerModel } from '@server/models/server/tracker'
 import { MVideo, MVideoWithHost } from '@server/types/models'
 import { ActivityTrackerUrlObject, VideoObject } from '@shared/models'
+import { buildRemoteVideoBaseUrl } from '../../url'
 
 function getTrackerUrls (object: VideoObject, video: MVideoWithHost) {
   let wsFound = false
diff --git a/server/lib/activitypub/videos/shared/url-to-object.ts b/server/lib/activitypub/videos/shared/url-to-object.ts
index dba3e9480..5b7007530 100644
--- a/server/lib/activitypub/videos/shared/url-to-object.ts
+++ b/server/lib/activitypub/videos/shared/url-to-object.ts
@@ -1,8 +1,8 @@
-import { checkUrlsSameHost } from '@server/helpers/activitypub'
 import { sanitizeAndCheckVideoTorrentObject } from '@server/helpers/custom-validators/activitypub/videos'
 import { logger, loggerTagsFactory } from '@server/helpers/logger'
 import { doJSONRequest } from '@server/helpers/requests'
 import { VideoObject } from '@shared/models'
+import { checkUrlsSameHost } from '../../url'
 
 const lTags = loggerTagsFactory('ap', 'video')
 
diff --git a/server/lib/job-queue/handlers/activitypub-cleaner.ts b/server/lib/job-queue/handlers/activitypub-cleaner.ts
index 07dd908cd..123aeac03 100644
--- a/server/lib/job-queue/handlers/activitypub-cleaner.ts
+++ b/server/lib/job-queue/handlers/activitypub-cleaner.ts
@@ -1,6 +1,5 @@
 import { map } from 'bluebird'
 import { Job } from 'bull'
-import { checkUrlsSameHost } from '@server/helpers/activitypub'
 import {
   isAnnounceActivityValid,
   isDislikeActivityValid,
@@ -9,6 +8,7 @@ import {
 import { sanitizeAndCheckVideoCommentObject } from '@server/helpers/custom-validators/activitypub/video-comments'
 import { doJSONRequest, PeerTubeRequestError } from '@server/helpers/requests'
 import { AP_CLEANER } from '@server/initializers/constants'
+import { checkUrlsSameHost } from '@server/lib/activitypub/url'
 import { Redis } from '@server/lib/redis'
 import { VideoModel } from '@server/models/video/video'
 import { VideoCommentModel } from '@server/models/video/video-comment'
diff --git a/server/lib/job-queue/handlers/utils/activitypub-http-utils.ts b/server/lib/job-queue/handlers/utils/activitypub-http-utils.ts
index 37e7c1fad..2a03325b7 100644
--- a/server/lib/job-queue/handlers/utils/activitypub-http-utils.ts
+++ b/server/lib/job-queue/handlers/utils/activitypub-http-utils.ts
@@ -1,7 +1,7 @@
 import { buildDigest } from '@server/helpers/peertube-crypto'
+import { buildSignedActivity } from '@server/lib/activitypub/activity'
 import { getServerActor } from '@server/models/application/application'
 import { ContextType } from '@shared/models/activitypub/context'
-import { buildSignedActivity } from '../../../../helpers/activitypub'
 import { ACTIVITY_PUB, HTTP_SIGNATURE } from '../../../../initializers/constants'
 import { ActorModel } from '../../../../models/actor/actor'
 import { MActor } from '../../../../types/models'
diff --git a/server/middlewares/activitypub.ts b/server/middlewares/activitypub.ts
index 86d3c1d6c..2a2d86a24 100644
--- a/server/middlewares/activitypub.ts
+++ b/server/middlewares/activitypub.ts
@@ -1,6 +1,6 @@
 import { NextFunction, Request, Response } from 'express'
-import { getAPId } from '@server/helpers/activitypub'
 import { isActorDeleteActivityValid } from '@server/helpers/custom-validators/activitypub/actor'
+import { getAPId } from '@server/lib/activitypub/activity'
 import { ActivityDelete, ActivityPubSignature, HttpStatusCode } from '@shared/models'
 import { logger } from '../helpers/logger'
 import { isHTTPSignatureVerified, isJsonLDSignatureVerified, parseHTTPSignature } from '../helpers/peertube-crypto'
diff --git a/server/models/actor/actor.ts b/server/models/actor/actor.ts
index 08cb2fd24..fad2070ea 100644
--- a/server/models/actor/actor.ts
+++ b/server/models/actor/actor.ts
@@ -16,12 +16,12 @@ import {
   Table,
   UpdatedAt
 } from 'sequelize-typescript'
+import { activityPubContextify } from '@server/lib/activitypub/context'
 import { getBiggestActorImage } from '@server/lib/actor-image'
 import { ModelCache } from '@server/models/model-cache'
 import { getLowercaseExtension } from '@shared/core-utils'
 import { ActivityIconObject, ActivityPubActorType, ActorImageType } from '@shared/models'
 import { AttributesOnly } from '@shared/typescript-utils'
-import { activityPubContextify } from '../../helpers/activitypub'
 import {
   isActorFollowersCountValid,
   isActorFollowingCountValid,
diff --git a/server/models/video/video-file.ts b/server/models/video/video-file.ts
index fae76c6f2..4aaee1ffa 100644
--- a/server/models/video/video-file.ts
+++ b/server/models/video/video-file.ts
@@ -19,9 +19,9 @@ import {
   UpdatedAt
 } from 'sequelize-typescript'
 import validator from 'validator'
-import { buildRemoteVideoBaseUrl } from '@server/helpers/activitypub'
 import { logger } from '@server/helpers/logger'
 import { extractVideo } from '@server/helpers/video'
+import { buildRemoteVideoBaseUrl } from '@server/lib/activitypub/url'
 import { getHLSPublicFileUrl, getWebTorrentPublicFileUrl } from '@server/lib/object-storage'
 import { getFSTorrentFilePath } from '@server/lib/paths'
 import { isStreamingPlaylist, MStreamingPlaylistVideo, MVideo, MVideoWithHost } from '@server/types/models'
diff --git a/server/models/video/video-playlist.ts b/server/models/video/video-playlist.ts
index ae5e237ec..8fb3d5f15 100644
--- a/server/models/video/video-playlist.ts
+++ b/server/models/video/video-playlist.ts
@@ -17,6 +17,7 @@ import {
   Table,
   UpdatedAt
 } from 'sequelize-typescript'
+import { activityPubCollectionPagination } from '@server/lib/activitypub/collection'
 import { MAccountId, MChannelId } from '@server/types/models'
 import { buildPlaylistEmbedPath, buildPlaylistWatchPath, pick } from '@shared/core-utils'
 import { buildUUID, uuidToShort } from '@shared/extra-utils'
@@ -26,7 +27,6 @@ import { PlaylistObject } from '../../../shared/models/activitypub/objects/playl
 import { VideoPlaylistPrivacy } from '../../../shared/models/videos/playlist/video-playlist-privacy.model'
 import { VideoPlaylistType } from '../../../shared/models/videos/playlist/video-playlist-type.model'
 import { VideoPlaylist } from '../../../shared/models/videos/playlist/video-playlist.model'
-import { activityPubCollectionPagination } from '../../helpers/activitypub'
 import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc'
 import {
   isVideoPlaylistDescriptionValid,
diff --git a/server/tests/api/activitypub/helpers.ts b/server/tests/api/activitypub/helpers.ts
index 25e1d9823..e516cf49e 100644
--- a/server/tests/api/activitypub/helpers.ts
+++ b/server/tests/api/activitypub/helpers.ts
@@ -5,8 +5,8 @@ import { expect } from 'chai'
 import { cloneDeep } from 'lodash'
 import { buildRequestStub } from '@server/tests/shared'
 import { buildAbsoluteFixturePath } from '@shared/core-utils'
-import { buildSignedActivity } from '../../../helpers/activitypub'
 import { isHTTPSignatureVerified, isJsonLDSignatureVerified, parseHTTPSignature } from '../../../helpers/peertube-crypto'
+import { buildSignedActivity } from '../../../lib/activitypub/activity'
 
 describe('Test activity pub helpers', function () {
   describe('When checking the Linked Signature', function () {
diff --git a/server/tests/api/activitypub/security.ts b/server/tests/api/activitypub/security.ts
index c4cb5ea0d..da9880d7d 100644
--- a/server/tests/api/activitypub/security.ts
+++ b/server/tests/api/activitypub/security.ts
@@ -2,9 +2,10 @@
 
 import 'mocha'
 import * as chai from 'chai'
-import { activityPubContextify, buildSignedActivity } from '@server/helpers/activitypub'
 import { buildDigest } from '@server/helpers/peertube-crypto'
 import { HTTP_SIGNATURE } from '@server/initializers/constants'
+import { buildSignedActivity } from '@server/lib/activitypub/activity'
+import { activityPubContextify } from '@server/lib/activitypub/context'
 import { buildGlobalHeaders } from '@server/lib/job-queue/handlers/utils/activitypub-http-utils'
 import { makeFollowRequest, makePOSTAPRequest } from '@server/tests/shared'
 import { buildAbsoluteFixturePath, wait } from '@shared/core-utils'
diff --git a/server/tests/shared/requests.ts b/server/tests/shared/requests.ts
index 7f1acc0e1..d7aedf82f 100644
--- a/server/tests/shared/requests.ts
+++ b/server/tests/shared/requests.ts
@@ -1,7 +1,7 @@
-import { activityPubContextify } from '@server/helpers/activitypub'
 import { buildDigest } from '@server/helpers/peertube-crypto'
 import { doRequest } from '@server/helpers/requests'
 import { ACTIVITY_PUB, HTTP_SIGNATURE } from '@server/initializers/constants'
+import { activityPubContextify } from '@server/lib/activitypub/context'
 
 export function makePOSTAPRequest (url: string, body: any, httpSignature: any, headers: any) {
   const options = {