aboutsummaryrefslogtreecommitdiffhomepage
path: root/server/lib
diff options
context:
space:
mode:
authormira.bat <97340105+irafire@users.noreply.github.com>2023-07-27 17:01:15 +0200
committerGitHub <noreply@github.com>2023-07-27 17:01:15 +0200
commitf862be2749b4f2d8dee99128d7e3064a69920e11 (patch)
tree20643166e07cb31ca3e0d05b4490000150b2f1c4 /server/lib
parent787d822cd471d1e4bd5a37c687c78cd5f69d8645 (diff)
downloadPeerTube-f862be2749b4f2d8dee99128d7e3064a69920e11.tar.gz
PeerTube-f862be2749b4f2d8dee99128d7e3064a69920e11.tar.zst
PeerTube-f862be2749b4f2d8dee99128d7e3064a69920e11.zip
Add an option to sign federated fetches for mastodon compatibility (#5898)
* Fix player error modal Not hidden when we change the video * Correctly dispose player components * Sign cross-server fetch requests for mastodon AUTHORIZED_FETCH compatibilty * Add a remote fetch sign configuration knob * Federated fetches refactoring --------- Co-authored-by: Chocobozzz <me@florianbigard.com> Co-authored-by: ira <ira@foxgirl.space>
Diffstat (limited to 'server/lib')
-rw-r--r--server/lib/activitypub/activity.ts50
-rw-r--r--server/lib/activitypub/actors/get.ts4
-rw-r--r--server/lib/activitypub/actors/shared/url-to-object.ts6
-rw-r--r--server/lib/activitypub/crawl.ts8
-rw-r--r--server/lib/activitypub/playlists/shared/url-to-object.ts6
-rw-r--r--server/lib/activitypub/process/process-create.ts4
-rw-r--r--server/lib/activitypub/process/process-undo.ts4
-rw-r--r--server/lib/activitypub/process/process-update.ts4
-rw-r--r--server/lib/activitypub/send/http.ts13
-rw-r--r--server/lib/activitypub/share.ts5
-rw-r--r--server/lib/activitypub/video-comments.ts5
-rw-r--r--server/lib/activitypub/videos/shared/url-to-object.ts4
-rw-r--r--server/lib/activitypub/videos/shared/video-sync-attributes.ts12
-rw-r--r--server/lib/job-queue/handlers/activitypub-cleaner.ts5
-rw-r--r--server/lib/job-queue/handlers/activitypub-http-broadcast.ts2
-rw-r--r--server/lib/job-queue/handlers/activitypub-http-unicast.ts2
16 files changed, 79 insertions, 55 deletions
diff --git a/server/lib/activitypub/activity.ts b/server/lib/activitypub/activity.ts
index 0fed3e8fd..391bcd9c6 100644
--- a/server/lib/activitypub/activity.ts
+++ b/server/lib/activitypub/activity.ts
@@ -1,22 +1,26 @@
1import { doJSONRequest } from '@server/helpers/requests' 1import { doJSONRequest, PeerTubeRequestOptions } from '@server/helpers/requests'
2import { APObjectId, ActivityObject, ActivityPubActor, ActivityType } from '@shared/models' 2import { CONFIG } from '@server/initializers/config'
3import { ActivityObject, ActivityPubActor, ActivityType, APObjectId } from '@shared/models'
4import { buildSignedRequestOptions } from './send'
3 5
4function getAPId (object: string | { id: string }) { 6export function getAPId (object: string | { id: string }) {
5 if (typeof object === 'string') return object 7 if (typeof object === 'string') return object
6 8
7 return object.id 9 return object.id
8} 10}
9 11
10function getActivityStreamDuration (duration: number) { 12export function getActivityStreamDuration (duration: number) {
11 // https://www.w3.org/TR/activitystreams-vocabulary/#dfn-duration 13 // https://www.w3.org/TR/activitystreams-vocabulary/#dfn-duration
12 return 'PT' + duration + 'S' 14 return 'PT' + duration + 'S'
13} 15}
14 16
15function getDurationFromActivityStream (duration: string) { 17export function getDurationFromActivityStream (duration: string) {
16 return parseInt(duration.replace(/[^\d]+/, '')) 18 return parseInt(duration.replace(/[^\d]+/, ''))
17} 19}
18 20
19function buildAvailableActivities (): ActivityType[] { 21// ---------------------------------------------------------------------------
22
23export function buildAvailableActivities (): ActivityType[] {
20 return [ 24 return [
21 'Create', 25 'Create',
22 'Update', 26 'Update',
@@ -33,9 +37,25 @@ function buildAvailableActivities (): ActivityType[] {
33 ] 37 ]
34} 38}
35 39
36async function fetchAPObject <T extends (ActivityObject | ActivityPubActor)> (object: APObjectId) { 40// ---------------------------------------------------------------------------
41
42export async function fetchAP <T> (url: string, moreOptions: PeerTubeRequestOptions = {}) {
43 const options = {
44 activityPub: true,
45
46 httpSignature: CONFIG.FEDERATION.SIGN_FEDERATED_FETCHES
47 ? await buildSignedRequestOptions({ hasPayload: false })
48 : undefined,
49
50 ...moreOptions
51 }
52
53 return doJSONRequest<T>(url, options)
54}
55
56export async function fetchAPObjectIfNeeded <T extends (ActivityObject | ActivityPubActor)> (object: APObjectId) {
37 if (typeof object === 'string') { 57 if (typeof object === 'string') {
38 const { body } = await doJSONRequest<Exclude<T, string>>(object, { activityPub: true }) 58 const { body } = await fetchAP<Exclude<T, string>>(object)
39 59
40 return body 60 return body
41 } 61 }
@@ -43,10 +63,12 @@ async function fetchAPObject <T extends (ActivityObject | ActivityPubActor)> (ob
43 return object as Exclude<T, string> 63 return object as Exclude<T, string>
44} 64}
45 65
46export { 66export async function findLatestAPRedirection (url: string, iteration = 1) {
47 getAPId, 67 if (iteration > 10) throw new Error('Too much iterations to find final URL ' + url)
48 fetchAPObject, 68
49 getActivityStreamDuration, 69 const { headers } = await fetchAP(url, { followRedirect: false })
50 buildAvailableActivities, 70
51 getDurationFromActivityStream 71 if (headers.location) return findLatestAPRedirection(headers.location, iteration + 1)
72
73 return url
52} 74}
diff --git a/server/lib/activitypub/actors/get.ts b/server/lib/activitypub/actors/get.ts
index b2be3f5fb..dd2bc9f03 100644
--- a/server/lib/activitypub/actors/get.ts
+++ b/server/lib/activitypub/actors/get.ts
@@ -5,7 +5,7 @@ import { ActorLoadByUrlType, loadActorByUrl } from '@server/lib/model-loaders'
5import { MActor, MActorAccountChannelId, MActorAccountChannelIdActor, MActorAccountId, MActorFullActor } from '@server/types/models' 5import { MActor, MActorAccountChannelId, MActorAccountChannelIdActor, MActorAccountId, MActorFullActor } from '@server/types/models'
6import { arrayify } from '@shared/core-utils' 6import { arrayify } from '@shared/core-utils'
7import { ActivityPubActor, APObjectId } from '@shared/models' 7import { ActivityPubActor, APObjectId } from '@shared/models'
8import { fetchAPObject, getAPId } from '../activity' 8import { fetchAPObjectIfNeeded, getAPId } from '../activity'
9import { checkUrlsSameHost } from '../url' 9import { checkUrlsSameHost } from '../url'
10import { refreshActorIfNeeded } from './refresh' 10import { refreshActorIfNeeded } from './refresh'
11import { APActorCreator, fetchRemoteActor } from './shared' 11import { APActorCreator, fetchRemoteActor } from './shared'
@@ -87,7 +87,7 @@ async function getOrCreateAPOwner (actorObject: ActivityPubActor, actorUrl: stri
87 87
88async function findOwner (rootUrl: string, attributedTo: APObjectId[] | APObjectId, type: 'Person' | 'Group') { 88async function findOwner (rootUrl: string, attributedTo: APObjectId[] | APObjectId, type: 'Person' | 'Group') {
89 for (const actorToCheck of arrayify(attributedTo)) { 89 for (const actorToCheck of arrayify(attributedTo)) {
90 const actorObject = await fetchAPObject<ActivityPubActor>(getAPId(actorToCheck)) 90 const actorObject = await fetchAPObjectIfNeeded<ActivityPubActor>(getAPId(actorToCheck))
91 91
92 if (!actorObject) { 92 if (!actorObject) {
93 logger.warn('Unknown attributed to actor %s for owner %s', actorToCheck, rootUrl) 93 logger.warn('Unknown attributed to actor %s for owner %s', actorToCheck, rootUrl)
diff --git a/server/lib/activitypub/actors/shared/url-to-object.ts b/server/lib/activitypub/actors/shared/url-to-object.ts
index 208d108ee..73766bd50 100644
--- a/server/lib/activitypub/actors/shared/url-to-object.ts
+++ b/server/lib/activitypub/actors/shared/url-to-object.ts
@@ -1,13 +1,13 @@
1import { sanitizeAndCheckActorObject } from '@server/helpers/custom-validators/activitypub/actor' 1import { sanitizeAndCheckActorObject } from '@server/helpers/custom-validators/activitypub/actor'
2import { logger } from '@server/helpers/logger' 2import { logger } from '@server/helpers/logger'
3import { doJSONRequest } from '@server/helpers/requests'
4import { ActivityPubActor, ActivityPubOrderedCollection } from '@shared/models' 3import { ActivityPubActor, ActivityPubOrderedCollection } from '@shared/models'
4import { fetchAP } from '../../activity'
5import { checkUrlsSameHost } from '../../url' 5import { checkUrlsSameHost } from '../../url'
6 6
7async function fetchRemoteActor (actorUrl: string): Promise<{ statusCode: number, actorObject: ActivityPubActor }> { 7async function fetchRemoteActor (actorUrl: string): Promise<{ statusCode: number, actorObject: ActivityPubActor }> {
8 logger.info('Fetching remote actor %s.', actorUrl) 8 logger.info('Fetching remote actor %s.', actorUrl)
9 9
10 const { body, statusCode } = await doJSONRequest<ActivityPubActor>(actorUrl, { activityPub: true }) 10 const { body, statusCode } = await fetchAP<ActivityPubActor>(actorUrl)
11 11
12 if (sanitizeAndCheckActorObject(body) === false) { 12 if (sanitizeAndCheckActorObject(body) === false) {
13 logger.debug('Remote actor JSON is not valid.', { actorJSON: body }) 13 logger.debug('Remote actor JSON is not valid.', { actorJSON: body })
@@ -46,7 +46,7 @@ export {
46 46
47async function fetchActorTotalItems (url: string) { 47async function fetchActorTotalItems (url: string) {
48 try { 48 try {
49 const { body } = await doJSONRequest<ActivityPubOrderedCollection<unknown>>(url, { activityPub: true }) 49 const { body } = await fetchAP<ActivityPubOrderedCollection<unknown>>(url)
50 50
51 return body.totalItems || 0 51 return body.totalItems || 0
52 } catch (err) { 52 } catch (err) {
diff --git a/server/lib/activitypub/crawl.ts b/server/lib/activitypub/crawl.ts
index 336129b82..b8348e8cf 100644
--- a/server/lib/activitypub/crawl.ts
+++ b/server/lib/activitypub/crawl.ts
@@ -3,8 +3,8 @@ import { URL } from 'url'
3import { retryTransactionWrapper } from '@server/helpers/database-utils' 3import { retryTransactionWrapper } from '@server/helpers/database-utils'
4import { ActivityPubOrderedCollection } from '../../../shared/models/activitypub' 4import { ActivityPubOrderedCollection } from '../../../shared/models/activitypub'
5import { logger } from '../../helpers/logger' 5import { logger } from '../../helpers/logger'
6import { doJSONRequest } from '../../helpers/requests'
7import { ACTIVITY_PUB, WEBSERVER } from '../../initializers/constants' 6import { ACTIVITY_PUB, WEBSERVER } from '../../initializers/constants'
7import { fetchAP } from './activity'
8 8
9type HandlerFunction<T> = (items: T[]) => (Promise<any> | Bluebird<any>) 9type HandlerFunction<T> = (items: T[]) => (Promise<any> | Bluebird<any>)
10type CleanerFunction = (startedDate: Date) => Promise<any> 10type CleanerFunction = (startedDate: Date) => Promise<any>
@@ -14,11 +14,9 @@ async function crawlCollectionPage <T> (argUrl: string, handler: HandlerFunction
14 14
15 logger.info('Crawling ActivityPub data on %s.', url) 15 logger.info('Crawling ActivityPub data on %s.', url)
16 16
17 const options = { activityPub: true }
18
19 const startDate = new Date() 17 const startDate = new Date()
20 18
21 const response = await doJSONRequest<ActivityPubOrderedCollection<T>>(url, options) 19 const response = await fetchAP<ActivityPubOrderedCollection<T>>(url)
22 const firstBody = response.body 20 const firstBody = response.body
23 21
24 const limit = ACTIVITY_PUB.FETCH_PAGE_LIMIT 22 const limit = ACTIVITY_PUB.FETCH_PAGE_LIMIT
@@ -34,7 +32,7 @@ async function crawlCollectionPage <T> (argUrl: string, handler: HandlerFunction
34 32
35 url = nextLink 33 url = nextLink
36 34
37 const res = await doJSONRequest<ActivityPubOrderedCollection<T>>(url, options) 35 const res = await fetchAP<ActivityPubOrderedCollection<T>>(url)
38 body = res.body 36 body = res.body
39 } else { 37 } else {
40 // nextLink is already the object we want 38 // nextLink is already the object we want
diff --git a/server/lib/activitypub/playlists/shared/url-to-object.ts b/server/lib/activitypub/playlists/shared/url-to-object.ts
index 41bee3752..fd9fe5558 100644
--- a/server/lib/activitypub/playlists/shared/url-to-object.ts
+++ b/server/lib/activitypub/playlists/shared/url-to-object.ts
@@ -1,8 +1,8 @@
1import { isPlaylistElementObjectValid, isPlaylistObjectValid } from '@server/helpers/custom-validators/activitypub/playlist' 1import { isPlaylistElementObjectValid, isPlaylistObjectValid } from '@server/helpers/custom-validators/activitypub/playlist'
2import { isArray } from '@server/helpers/custom-validators/misc' 2import { isArray } from '@server/helpers/custom-validators/misc'
3import { logger, loggerTagsFactory } from '@server/helpers/logger' 3import { logger, loggerTagsFactory } from '@server/helpers/logger'
4import { doJSONRequest } from '@server/helpers/requests'
5import { PlaylistElementObject, PlaylistObject } from '@shared/models' 4import { PlaylistElementObject, PlaylistObject } from '@shared/models'
5import { fetchAP } from '../../activity'
6import { checkUrlsSameHost } from '../../url' 6import { checkUrlsSameHost } from '../../url'
7 7
8async function fetchRemoteVideoPlaylist (playlistUrl: string): Promise<{ statusCode: number, playlistObject: PlaylistObject }> { 8async function fetchRemoteVideoPlaylist (playlistUrl: string): Promise<{ statusCode: number, playlistObject: PlaylistObject }> {
@@ -10,7 +10,7 @@ async function fetchRemoteVideoPlaylist (playlistUrl: string): Promise<{ statusC
10 10
11 logger.info('Fetching remote playlist %s.', playlistUrl, lTags()) 11 logger.info('Fetching remote playlist %s.', playlistUrl, lTags())
12 12
13 const { body, statusCode } = await doJSONRequest<any>(playlistUrl, { activityPub: true }) 13 const { body, statusCode } = await fetchAP<any>(playlistUrl)
14 14
15 if (isPlaylistObjectValid(body) === false || checkUrlsSameHost(body.id, playlistUrl) !== true) { 15 if (isPlaylistObjectValid(body) === false || checkUrlsSameHost(body.id, playlistUrl) !== true) {
16 logger.debug('Remote video playlist JSON is not valid.', { body, ...lTags() }) 16 logger.debug('Remote video playlist JSON is not valid.', { body, ...lTags() })
@@ -30,7 +30,7 @@ async function fetchRemotePlaylistElement (elementUrl: string): Promise<{ status
30 30
31 logger.debug('Fetching remote playlist element %s.', elementUrl, lTags()) 31 logger.debug('Fetching remote playlist element %s.', elementUrl, lTags())
32 32
33 const { body, statusCode } = await doJSONRequest<PlaylistElementObject>(elementUrl, { activityPub: true }) 33 const { body, statusCode } = await fetchAP<PlaylistElementObject>(elementUrl)
34 34
35 if (!isPlaylistElementObjectValid(body)) throw new Error(`Invalid body in fetch playlist element ${elementUrl}`) 35 if (!isPlaylistElementObjectValid(body)) throw new Error(`Invalid body in fetch playlist element ${elementUrl}`)
36 36
diff --git a/server/lib/activitypub/process/process-create.ts b/server/lib/activitypub/process/process-create.ts
index 2e64d981e..5f980de65 100644
--- a/server/lib/activitypub/process/process-create.ts
+++ b/server/lib/activitypub/process/process-create.ts
@@ -18,7 +18,7 @@ import { sequelizeTypescript } from '../../../initializers/database'
18import { APProcessorOptions } from '../../../types/activitypub-processor.model' 18import { APProcessorOptions } from '../../../types/activitypub-processor.model'
19import { MActorSignature, MCommentOwnerVideo, MVideoAccountLightBlacklistAllFiles } from '../../../types/models' 19import { MActorSignature, MCommentOwnerVideo, MVideoAccountLightBlacklistAllFiles } from '../../../types/models'
20import { Notifier } from '../../notifier' 20import { Notifier } from '../../notifier'
21import { fetchAPObject } from '../activity' 21import { fetchAPObjectIfNeeded } from '../activity'
22import { createOrUpdateCacheFile } from '../cache-file' 22import { createOrUpdateCacheFile } from '../cache-file'
23import { createOrUpdateLocalVideoViewer } from '../local-video-viewer' 23import { createOrUpdateLocalVideoViewer } from '../local-video-viewer'
24import { createOrUpdateVideoPlaylist } from '../playlists' 24import { createOrUpdateVideoPlaylist } from '../playlists'
@@ -31,7 +31,7 @@ async function processCreateActivity (options: APProcessorOptions<ActivityCreate
31 31
32 // Only notify if it is not from a fetcher job 32 // Only notify if it is not from a fetcher job
33 const notify = options.fromFetch !== true 33 const notify = options.fromFetch !== true
34 const activityObject = await fetchAPObject<Exclude<ActivityObject, AbuseObject>>(activity.object) 34 const activityObject = await fetchAPObjectIfNeeded<Exclude<ActivityObject, AbuseObject>>(activity.object)
35 const activityType = activityObject.type 35 const activityType = activityObject.type
36 36
37 if (activityType === 'Video') { 37 if (activityType === 'Video') {
diff --git a/server/lib/activitypub/process/process-undo.ts b/server/lib/activitypub/process/process-undo.ts
index 25f68724d..a9d8199de 100644
--- a/server/lib/activitypub/process/process-undo.ts
+++ b/server/lib/activitypub/process/process-undo.ts
@@ -19,7 +19,7 @@ import { VideoRedundancyModel } from '../../../models/redundancy/video-redundanc
19import { VideoShareModel } from '../../../models/video/video-share' 19import { VideoShareModel } from '../../../models/video/video-share'
20import { APProcessorOptions } from '../../../types/activitypub-processor.model' 20import { APProcessorOptions } from '../../../types/activitypub-processor.model'
21import { MActorSignature } from '../../../types/models' 21import { MActorSignature } from '../../../types/models'
22import { fetchAPObject } from '../activity' 22import { fetchAPObjectIfNeeded } from '../activity'
23import { forwardVideoRelatedActivity } from '../send/shared/send-utils' 23import { forwardVideoRelatedActivity } from '../send/shared/send-utils'
24import { federateVideoIfNeeded, getOrCreateAPVideo } from '../videos' 24import { federateVideoIfNeeded, getOrCreateAPVideo } from '../videos'
25 25
@@ -32,7 +32,7 @@ async function processUndoActivity (options: APProcessorOptions<ActivityUndo<Act
32 } 32 }
33 33
34 if (activityToUndo.type === 'Create') { 34 if (activityToUndo.type === 'Create') {
35 const objectToUndo = await fetchAPObject<CacheFileObject>(activityToUndo.object) 35 const objectToUndo = await fetchAPObjectIfNeeded<CacheFileObject>(activityToUndo.object)
36 36
37 if (objectToUndo.type === 'CacheFile') { 37 if (objectToUndo.type === 'CacheFile') {
38 return retryTransactionWrapper(processUndoCacheFile, byActor, activity, objectToUndo) 38 return retryTransactionWrapper(processUndoCacheFile, byActor, activity, objectToUndo)
diff --git a/server/lib/activitypub/process/process-update.ts b/server/lib/activitypub/process/process-update.ts
index 9caa74e04..304ed9de6 100644
--- a/server/lib/activitypub/process/process-update.ts
+++ b/server/lib/activitypub/process/process-update.ts
@@ -10,7 +10,7 @@ import { sequelizeTypescript } from '../../../initializers/database'
10import { ActorModel } from '../../../models/actor/actor' 10import { ActorModel } from '../../../models/actor/actor'
11import { APProcessorOptions } from '../../../types/activitypub-processor.model' 11import { APProcessorOptions } from '../../../types/activitypub-processor.model'
12import { MActorFull, MActorSignature } from '../../../types/models' 12import { MActorFull, MActorSignature } from '../../../types/models'
13import { fetchAPObject } from '../activity' 13import { fetchAPObjectIfNeeded } from '../activity'
14import { APActorUpdater } from '../actors/updater' 14import { APActorUpdater } from '../actors/updater'
15import { createOrUpdateCacheFile } from '../cache-file' 15import { createOrUpdateCacheFile } from '../cache-file'
16import { createOrUpdateVideoPlaylist } from '../playlists' 16import { createOrUpdateVideoPlaylist } from '../playlists'
@@ -20,7 +20,7 @@ import { APVideoUpdater, getOrCreateAPVideo } from '../videos'
20async function processUpdateActivity (options: APProcessorOptions<ActivityUpdate<ActivityUpdateObject>>) { 20async function processUpdateActivity (options: APProcessorOptions<ActivityUpdate<ActivityUpdateObject>>) {
21 const { activity, byActor } = options 21 const { activity, byActor } = options
22 22
23 const object = await fetchAPObject(activity.object) 23 const object = await fetchAPObjectIfNeeded(activity.object)
24 const objectType = object.type 24 const objectType = object.type
25 25
26 if (objectType === 'Video') { 26 if (objectType === 'Video') {
diff --git a/server/lib/activitypub/send/http.ts b/server/lib/activitypub/send/http.ts
index ad7869853..b461aa55d 100644
--- a/server/lib/activitypub/send/http.ts
+++ b/server/lib/activitypub/send/http.ts
@@ -23,11 +23,14 @@ async function computeBody <T> (
23 return body 23 return body
24} 24}
25 25
26async function buildSignedRequestOptions (payload: Payload<any>) { 26async function buildSignedRequestOptions (options: {
27 signatureActorId?: number
28 hasPayload: boolean
29}) {
27 let actor: MActor | null 30 let actor: MActor | null
28 31
29 if (payload.signatureActorId) { 32 if (options.signatureActorId) {
30 actor = await ActorModel.load(payload.signatureActorId) 33 actor = await ActorModel.load(options.signatureActorId)
31 if (!actor) throw new Error('Unknown signature actor id.') 34 if (!actor) throw new Error('Unknown signature actor id.')
32 } else { 35 } else {
33 // We need to sign the request, so use the server 36 // We need to sign the request, so use the server
@@ -40,7 +43,9 @@ async function buildSignedRequestOptions (payload: Payload<any>) {
40 authorizationHeaderName: HTTP_SIGNATURE.HEADER_NAME, 43 authorizationHeaderName: HTTP_SIGNATURE.HEADER_NAME,
41 keyId, 44 keyId,
42 key: actor.privateKey, 45 key: actor.privateKey,
43 headers: HTTP_SIGNATURE.HEADERS_TO_SIGN 46 headers: options.hasPayload
47 ? HTTP_SIGNATURE.HEADERS_TO_SIGN_WITH_PAYLOAD
48 : HTTP_SIGNATURE.HEADERS_TO_SIGN_WITHOUT_PAYLOAD
44 } 49 }
45} 50}
46 51
diff --git a/server/lib/activitypub/share.ts b/server/lib/activitypub/share.ts
index af0dd510a..792a73f2a 100644
--- a/server/lib/activitypub/share.ts
+++ b/server/lib/activitypub/share.ts
@@ -2,11 +2,10 @@ import { map } from 'bluebird'
2import { Transaction } from 'sequelize' 2import { Transaction } from 'sequelize'
3import { getServerActor } from '@server/models/application/application' 3import { getServerActor } from '@server/models/application/application'
4import { logger, loggerTagsFactory } from '../../helpers/logger' 4import { logger, loggerTagsFactory } from '../../helpers/logger'
5import { doJSONRequest } from '../../helpers/requests'
6import { CRAWL_REQUEST_CONCURRENCY } from '../../initializers/constants' 5import { CRAWL_REQUEST_CONCURRENCY } from '../../initializers/constants'
7import { VideoShareModel } from '../../models/video/video-share' 6import { VideoShareModel } from '../../models/video/video-share'
8import { MChannelActorLight, MVideo, MVideoAccountLight, MVideoId } from '../../types/models/video' 7import { MChannelActorLight, MVideo, MVideoAccountLight, MVideoId } from '../../types/models/video'
9import { getAPId } from './activity' 8import { fetchAP, getAPId } from './activity'
10import { getOrCreateAPActor } from './actors' 9import { getOrCreateAPActor } from './actors'
11import { sendUndoAnnounce, sendVideoAnnounce } from './send' 10import { sendUndoAnnounce, sendVideoAnnounce } from './send'
12import { checkUrlsSameHost, getLocalVideoAnnounceActivityPubUrl } from './url' 11import { checkUrlsSameHost, getLocalVideoAnnounceActivityPubUrl } from './url'
@@ -56,7 +55,7 @@ export {
56// --------------------------------------------------------------------------- 55// ---------------------------------------------------------------------------
57 56
58async function addVideoShare (shareUrl: string, video: MVideoId) { 57async function addVideoShare (shareUrl: string, video: MVideoId) {
59 const { body } = await doJSONRequest<any>(shareUrl, { activityPub: true }) 58 const { body } = await fetchAP<any>(shareUrl)
60 if (!body?.actor) throw new Error('Body or body actor is invalid') 59 if (!body?.actor) throw new Error('Body or body actor is invalid')
61 60
62 const actorUrl = getAPId(body.actor) 61 const actorUrl = getAPId(body.actor)
diff --git a/server/lib/activitypub/video-comments.ts b/server/lib/activitypub/video-comments.ts
index 4fdb4e0b7..b861be5bd 100644
--- a/server/lib/activitypub/video-comments.ts
+++ b/server/lib/activitypub/video-comments.ts
@@ -1,12 +1,13 @@
1import { map } from 'bluebird' 1import { map } from 'bluebird'
2
2import { sanitizeAndCheckVideoCommentObject } from '../../helpers/custom-validators/activitypub/video-comments' 3import { sanitizeAndCheckVideoCommentObject } from '../../helpers/custom-validators/activitypub/video-comments'
3import { logger } from '../../helpers/logger' 4import { logger } from '../../helpers/logger'
4import { doJSONRequest } from '../../helpers/requests'
5import { ACTIVITY_PUB, CRAWL_REQUEST_CONCURRENCY } from '../../initializers/constants' 5import { ACTIVITY_PUB, CRAWL_REQUEST_CONCURRENCY } from '../../initializers/constants'
6import { VideoCommentModel } from '../../models/video/video-comment' 6import { VideoCommentModel } from '../../models/video/video-comment'
7import { MComment, MCommentOwner, MCommentOwnerVideo, MVideoAccountLightBlacklistAllFiles } from '../../types/models/video' 7import { MComment, MCommentOwner, MCommentOwnerVideo, MVideoAccountLightBlacklistAllFiles } from '../../types/models/video'
8import { isRemoteVideoCommentAccepted } from '../moderation' 8import { isRemoteVideoCommentAccepted } from '../moderation'
9import { Hooks } from '../plugins/hooks' 9import { Hooks } from '../plugins/hooks'
10import { fetchAP } from './activity'
10import { getOrCreateAPActor } from './actors' 11import { getOrCreateAPActor } from './actors'
11import { checkUrlsSameHost } from './url' 12import { checkUrlsSameHost } from './url'
12import { getOrCreateAPVideo } from './videos' 13import { getOrCreateAPVideo } from './videos'
@@ -139,7 +140,7 @@ async function resolveRemoteParentComment (params: ResolveThreadParams) {
139 throw new Error('Recursion limit reached when resolving a thread') 140 throw new Error('Recursion limit reached when resolving a thread')
140 } 141 }
141 142
142 const { body } = await doJSONRequest<any>(url, { activityPub: true }) 143 const { body } = await fetchAP<any>(url)
143 144
144 if (sanitizeAndCheckVideoCommentObject(body) === false) { 145 if (sanitizeAndCheckVideoCommentObject(body) === false) {
145 throw new Error(`Remote video comment JSON ${url} is not valid:` + JSON.stringify(body)) 146 throw new Error(`Remote video comment JSON ${url} is not valid:` + JSON.stringify(body))
diff --git a/server/lib/activitypub/videos/shared/url-to-object.ts b/server/lib/activitypub/videos/shared/url-to-object.ts
index 5b7007530..7fe008419 100644
--- a/server/lib/activitypub/videos/shared/url-to-object.ts
+++ b/server/lib/activitypub/videos/shared/url-to-object.ts
@@ -1,7 +1,7 @@
1import { sanitizeAndCheckVideoTorrentObject } from '@server/helpers/custom-validators/activitypub/videos' 1import { sanitizeAndCheckVideoTorrentObject } from '@server/helpers/custom-validators/activitypub/videos'
2import { logger, loggerTagsFactory } from '@server/helpers/logger' 2import { logger, loggerTagsFactory } from '@server/helpers/logger'
3import { doJSONRequest } from '@server/helpers/requests'
4import { VideoObject } from '@shared/models' 3import { VideoObject } from '@shared/models'
4import { fetchAP } from '../../activity'
5import { checkUrlsSameHost } from '../../url' 5import { checkUrlsSameHost } from '../../url'
6 6
7const lTags = loggerTagsFactory('ap', 'video') 7const lTags = loggerTagsFactory('ap', 'video')
@@ -9,7 +9,7 @@ const lTags = loggerTagsFactory('ap', 'video')
9async function fetchRemoteVideo (videoUrl: string): Promise<{ statusCode: number, videoObject: VideoObject }> { 9async function fetchRemoteVideo (videoUrl: string): Promise<{ statusCode: number, videoObject: VideoObject }> {
10 logger.info('Fetching remote video %s.', videoUrl, lTags(videoUrl)) 10 logger.info('Fetching remote video %s.', videoUrl, lTags(videoUrl))
11 11
12 const { statusCode, body } = await doJSONRequest<any>(videoUrl, { activityPub: true }) 12 const { statusCode, body } = await fetchAP<any>(videoUrl)
13 13
14 if (sanitizeAndCheckVideoTorrentObject(body) === false || checkUrlsSameHost(body.id, videoUrl) !== true) { 14 if (sanitizeAndCheckVideoTorrentObject(body) === false || checkUrlsSameHost(body.id, videoUrl) !== true) {
15 logger.debug('Remote video JSON is not valid.', { body, ...lTags(videoUrl) }) 15 logger.debug('Remote video JSON is not valid.', { body, ...lTags(videoUrl) })
diff --git a/server/lib/activitypub/videos/shared/video-sync-attributes.ts b/server/lib/activitypub/videos/shared/video-sync-attributes.ts
index aa37f3d34..7fb933559 100644
--- a/server/lib/activitypub/videos/shared/video-sync-attributes.ts
+++ b/server/lib/activitypub/videos/shared/video-sync-attributes.ts
@@ -1,12 +1,12 @@
1import { runInReadCommittedTransaction } from '@server/helpers/database-utils' 1import { runInReadCommittedTransaction } from '@server/helpers/database-utils'
2import { logger, loggerTagsFactory } from '@server/helpers/logger' 2import { logger, loggerTagsFactory } from '@server/helpers/logger'
3import { doJSONRequest } from '@server/helpers/requests'
4import { JobQueue } from '@server/lib/job-queue' 3import { JobQueue } from '@server/lib/job-queue'
5import { VideoModel } from '@server/models/video/video' 4import { VideoModel } from '@server/models/video/video'
6import { VideoCommentModel } from '@server/models/video/video-comment' 5import { VideoCommentModel } from '@server/models/video/video-comment'
7import { VideoShareModel } from '@server/models/video/video-share' 6import { VideoShareModel } from '@server/models/video/video-share'
8import { MVideo } from '@server/types/models' 7import { MVideo } from '@server/types/models'
9import { ActivitypubHttpFetcherPayload, ActivityPubOrderedCollection, VideoObject } from '@shared/models' 8import { ActivitypubHttpFetcherPayload, ActivityPubOrderedCollection, VideoObject } from '@shared/models'
9import { fetchAP } from '../../activity'
10import { crawlCollectionPage } from '../../crawl' 10import { crawlCollectionPage } from '../../crawl'
11import { addVideoShares } from '../../share' 11import { addVideoShares } from '../../share'
12import { addVideoComments } from '../../video-comments' 12import { addVideoComments } from '../../video-comments'
@@ -63,17 +63,15 @@ async function getRatesCount (type: 'like' | 'dislike', video: MVideo, fetchedVi
63 : fetchedVideo.dislikes 63 : fetchedVideo.dislikes
64 64
65 logger.info('Sync %s of video %s', type, video.url) 65 logger.info('Sync %s of video %s', type, video.url)
66 const options = { activityPub: true }
67 66
68 const response = await doJSONRequest<ActivityPubOrderedCollection<any>>(uri, options) 67 const { body } = await fetchAP<ActivityPubOrderedCollection<any>>(uri)
69 const totalItems = response.body.totalItems
70 68
71 if (isNaN(totalItems)) { 69 if (isNaN(body.totalItems)) {
72 logger.error('Cannot sync %s of video %s, totalItems is not a number', type, video.url, { body: response.body }) 70 logger.error('Cannot sync %s of video %s, totalItems is not a number', type, video.url, { body })
73 return 71 return
74 } 72 }
75 73
76 return totalItems 74 return body.totalItems
77} 75}
78 76
79function syncShares (video: MVideo, fetchedVideo: VideoObject, isSync: boolean) { 77function syncShares (video: MVideo, fetchedVideo: VideoObject, isSync: boolean) {
diff --git a/server/lib/job-queue/handlers/activitypub-cleaner.ts b/server/lib/job-queue/handlers/activitypub-cleaner.ts
index a25f00b0a..6ee9e2429 100644
--- a/server/lib/job-queue/handlers/activitypub-cleaner.ts
+++ b/server/lib/job-queue/handlers/activitypub-cleaner.ts
@@ -6,8 +6,9 @@ import {
6 isLikeActivityValid 6 isLikeActivityValid
7} from '@server/helpers/custom-validators/activitypub/activity' 7} from '@server/helpers/custom-validators/activitypub/activity'
8import { sanitizeAndCheckVideoCommentObject } from '@server/helpers/custom-validators/activitypub/video-comments' 8import { sanitizeAndCheckVideoCommentObject } from '@server/helpers/custom-validators/activitypub/video-comments'
9import { doJSONRequest, PeerTubeRequestError } from '@server/helpers/requests' 9import { PeerTubeRequestError } from '@server/helpers/requests'
10import { AP_CLEANER } from '@server/initializers/constants' 10import { AP_CLEANER } from '@server/initializers/constants'
11import { fetchAP } from '@server/lib/activitypub/activity'
11import { checkUrlsSameHost } from '@server/lib/activitypub/url' 12import { checkUrlsSameHost } from '@server/lib/activitypub/url'
12import { Redis } from '@server/lib/redis' 13import { Redis } from '@server/lib/redis'
13import { VideoModel } from '@server/models/video/video' 14import { VideoModel } from '@server/models/video/video'
@@ -85,7 +86,7 @@ async function updateObjectIfNeeded <T> (options: {
85 } 86 }
86 87
87 try { 88 try {
88 const { body } = await doJSONRequest<any>(url, { activityPub: true }) 89 const { body } = await fetchAP<any>(url)
89 90
90 // If not same id, check same host and update 91 // If not same id, check same host and update
91 if (!body?.id || !bodyValidator(body)) throw new Error(`Body or body id of ${url} is invalid`) 92 if (!body?.id || !bodyValidator(body)) throw new Error(`Body or body id of ${url} is invalid`)
diff --git a/server/lib/job-queue/handlers/activitypub-http-broadcast.ts b/server/lib/job-queue/handlers/activitypub-http-broadcast.ts
index 57ecf0acc..8904d086f 100644
--- a/server/lib/job-queue/handlers/activitypub-http-broadcast.ts
+++ b/server/lib/job-queue/handlers/activitypub-http-broadcast.ts
@@ -38,7 +38,7 @@ export {
38 38
39async function buildRequestOptions (payload: ActivitypubHttpBroadcastPayload) { 39async function buildRequestOptions (payload: ActivitypubHttpBroadcastPayload) {
40 const body = await computeBody(payload) 40 const body = await computeBody(payload)
41 const httpSignatureOptions = await buildSignedRequestOptions(payload) 41 const httpSignatureOptions = await buildSignedRequestOptions({ signatureActorId: payload.signatureActorId, hasPayload: true })
42 42
43 return { 43 return {
44 method: 'POST' as 'POST', 44 method: 'POST' as 'POST',
diff --git a/server/lib/job-queue/handlers/activitypub-http-unicast.ts b/server/lib/job-queue/handlers/activitypub-http-unicast.ts
index 9e4e84002..50fca3f94 100644
--- a/server/lib/job-queue/handlers/activitypub-http-unicast.ts
+++ b/server/lib/job-queue/handlers/activitypub-http-unicast.ts
@@ -12,7 +12,7 @@ async function processActivityPubHttpUnicast (job: Job) {
12 const uri = payload.uri 12 const uri = payload.uri
13 13
14 const body = await computeBody(payload) 14 const body = await computeBody(payload)
15 const httpSignatureOptions = await buildSignedRequestOptions(payload) 15 const httpSignatureOptions = await buildSignedRequestOptions({ signatureActorId: payload.signatureActorId, hasPayload: true })
16 16
17 const options = { 17 const options = {
18 method: 'POST' as 'POST', 18 method: 'POST' as 'POST',