]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/commitdiff
Refactor AP context builder
authorChocobozzz <me@florianbigard.com>
Wed, 23 Mar 2022 15:14:33 +0000 (16:14 +0100)
committerChocobozzz <me@florianbigard.com>
Thu, 24 Mar 2022 08:40:46 +0000 (09:40 +0100)
28 files changed:
server/controllers/activitypub/client.ts
server/controllers/activitypub/outbox.ts
server/lib/activitypub/activity.ts
server/lib/activitypub/audience.ts
server/lib/activitypub/context.ts
server/lib/activitypub/send/http.ts [moved from server/lib/job-queue/handlers/utils/activitypub-http-utils.ts with 63% similarity]
server/lib/activitypub/send/index.ts
server/lib/activitypub/send/send-accept.ts
server/lib/activitypub/send/send-announce.ts
server/lib/activitypub/send/send-create.ts
server/lib/activitypub/send/send-delete.ts
server/lib/activitypub/send/send-dislike.ts
server/lib/activitypub/send/send-flag.ts
server/lib/activitypub/send/send-follow.ts
server/lib/activitypub/send/send-like.ts
server/lib/activitypub/send/send-reject.ts
server/lib/activitypub/send/send-undo.ts
server/lib/activitypub/send/send-update.ts
server/lib/activitypub/send/shared/send-utils.ts
server/lib/job-queue/handlers/activitypub-http-broadcast.ts
server/lib/job-queue/handlers/activitypub-http-unicast.ts
server/models/actor/actor.ts
server/tests/api/activitypub/helpers.ts
server/tests/api/activitypub/security.ts
server/tests/shared/requests.ts
shared/models/activitypub/context.ts
shared/models/activitypub/objects/common-objects.ts
shared/models/server/job.model.ts

index 99637dbab1eccf73fcc0edb2a584926e0fcc76fd..d0f76100950dc9ae9bec4677b5678795344d3937 100644 (file)
@@ -186,35 +186,35 @@ export {
 function accountController (req: express.Request, res: express.Response) {
   const account = res.locals.account
 
-  return activityPubResponse(activityPubContextify(account.toActivityPubObject()), res)
+  return activityPubResponse(activityPubContextify(account.toActivityPubObject(), 'Actor'), res)
 }
 
 async function accountFollowersController (req: express.Request, res: express.Response) {
   const account = res.locals.account
   const activityPubResult = await actorFollowers(req, account.Actor)
 
-  return activityPubResponse(activityPubContextify(activityPubResult), res)
+  return activityPubResponse(activityPubContextify(activityPubResult, 'Collection'), res)
 }
 
 async function accountFollowingController (req: express.Request, res: express.Response) {
   const account = res.locals.account
   const activityPubResult = await actorFollowing(req, account.Actor)
 
-  return activityPubResponse(activityPubContextify(activityPubResult), res)
+  return activityPubResponse(activityPubContextify(activityPubResult, 'Collection'), res)
 }
 
 async function accountPlaylistsController (req: express.Request, res: express.Response) {
   const account = res.locals.account
   const activityPubResult = await actorPlaylists(req, { account })
 
-  return activityPubResponse(activityPubContextify(activityPubResult), res)
+  return activityPubResponse(activityPubContextify(activityPubResult, 'Collection'), res)
 }
 
 async function videoChannelPlaylistsController (req: express.Request, res: express.Response) {
   const channel = res.locals.videoChannel
   const activityPubResult = await actorPlaylists(req, { channel })
 
-  return activityPubResponse(activityPubContextify(activityPubResult), res)
+  return activityPubResponse(activityPubContextify(activityPubResult, 'Collection'), res)
 }
 
 function getAccountVideoRateFactory (rateType: VideoRateType) {
@@ -226,7 +226,7 @@ function getAccountVideoRateFactory (rateType: VideoRateType) {
       ? buildLikeActivity(accountVideoRate.url, byActor, accountVideoRate.Video)
       : buildDislikeActivity(accountVideoRate.url, byActor, accountVideoRate.Video)
 
-    return activityPubResponse(activityPubContextify(APObject), res)
+    return activityPubResponse(activityPubContextify(APObject, 'Rate'), res)
   }
 }
 
@@ -244,10 +244,10 @@ async function videoController (req: express.Request, res: express.Response) {
 
   if (req.path.endsWith('/activity')) {
     const data = buildCreateActivity(videoWithCaptions.url, video.VideoChannel.Account.Actor, videoObject, audience)
-    return activityPubResponse(activityPubContextify(data), res)
+    return activityPubResponse(activityPubContextify(data, 'Video'), res)
   }
 
-  return activityPubResponse(activityPubContextify(videoObject), res)
+  return activityPubResponse(activityPubContextify(videoObject, 'Video'), res)
 }
 
 async function videoAnnounceController (req: express.Request, res: express.Response) {
@@ -274,7 +274,7 @@ async function videoAnnouncesController (req: express.Request, res: express.Resp
   }
   const json = await activityPubCollectionPagination(getLocalVideoSharesActivityPubUrl(video), handler, req.query.page)
 
-  return activityPubResponse(activityPubContextify(json), res)
+  return activityPubResponse(activityPubContextify(json, 'Collection'), res)
 }
 
 async function videoLikesController (req: express.Request, res: express.Response) {
@@ -284,7 +284,7 @@ async function videoLikesController (req: express.Request, res: express.Response
 
   const json = await videoRates(req, 'like', video, getLocalVideoLikesActivityPubUrl(video))
 
-  return activityPubResponse(activityPubContextify(json), res)
+  return activityPubResponse(activityPubContextify(json, 'Collection'), res)
 }
 
 async function videoDislikesController (req: express.Request, res: express.Response) {
@@ -294,7 +294,7 @@ async function videoDislikesController (req: express.Request, res: express.Respo
 
   const json = await videoRates(req, 'dislike', video, getLocalVideoDislikesActivityPubUrl(video))
 
-  return activityPubResponse(activityPubContextify(json), res)
+  return activityPubResponse(activityPubContextify(json, 'Collection'), res)
 }
 
 async function videoCommentsController (req: express.Request, res: express.Response) {
@@ -312,27 +312,27 @@ async function videoCommentsController (req: express.Request, res: express.Respo
   }
   const json = await activityPubCollectionPagination(getLocalVideoCommentsActivityPubUrl(video), handler, req.query.page)
 
-  return activityPubResponse(activityPubContextify(json), res)
+  return activityPubResponse(activityPubContextify(json, 'Collection'), res)
 }
 
 function videoChannelController (req: express.Request, res: express.Response) {
   const videoChannel = res.locals.videoChannel
 
-  return activityPubResponse(activityPubContextify(videoChannel.toActivityPubObject()), res)
+  return activityPubResponse(activityPubContextify(videoChannel.toActivityPubObject(), 'Actor'), res)
 }
 
 async function videoChannelFollowersController (req: express.Request, res: express.Response) {
   const videoChannel = res.locals.videoChannel
   const activityPubResult = await actorFollowers(req, videoChannel.Actor)
 
-  return activityPubResponse(activityPubContextify(activityPubResult), res)
+  return activityPubResponse(activityPubContextify(activityPubResult, 'Collection'), res)
 }
 
 async function videoChannelFollowingController (req: express.Request, res: express.Response) {
   const videoChannel = res.locals.videoChannel
   const activityPubResult = await actorFollowing(req, videoChannel.Actor)
 
-  return activityPubResponse(activityPubContextify(activityPubResult), res)
+  return activityPubResponse(activityPubContextify(activityPubResult, 'Collection'), res)
 }
 
 async function videoCommentController (req: express.Request, res: express.Response) {
@@ -350,11 +350,11 @@ async function videoCommentController (req: express.Request, res: express.Respon
 
     if (req.path.endsWith('/activity')) {
       const data = buildCreateActivity(videoComment.url, videoComment.Account.Actor, videoCommentObject, audience)
-      return activityPubResponse(activityPubContextify(data), res)
+      return activityPubResponse(activityPubContextify(data, 'Comment'), res)
     }
   }
 
-  return activityPubResponse(activityPubContextify(videoCommentObject), res)
+  return activityPubResponse(activityPubContextify(videoCommentObject, 'Comment'), res)
 }
 
 async function videoRedundancyController (req: express.Request, res: express.Response) {
@@ -387,7 +387,7 @@ async function videoPlaylistController (req: express.Request, res: express.Respo
   const audience = getAudience(playlist.OwnerAccount.Actor, playlist.privacy === VideoPlaylistPrivacy.PUBLIC)
   const object = audiencify(json, audience)
 
-  return activityPubResponse(activityPubContextify(object), res)
+  return activityPubResponse(activityPubContextify(object, 'Playlist'), res)
 }
 
 function videoPlaylistElementController (req: express.Request, res: express.Response) {
@@ -396,7 +396,7 @@ function videoPlaylistElementController (req: express.Request, res: express.Resp
   if (redirectIfNotOwned(videoPlaylistElement.url, res)) return
 
   const json = videoPlaylistElement.toActivityPubObject()
-  return activityPubResponse(activityPubContextify(json), res)
+  return activityPubResponse(activityPubContextify(json, 'Playlist'), res)
 }
 
 // ---------------------------------------------------------------------------
index 4e7a3afeb746e4984ad8a81b11ca7845eaec13ec..f385c9927ee5967683a01d289c590f5cf3be5dab 100644 (file)
@@ -45,7 +45,7 @@ async function outboxController (req: express.Request, res: express.Response) {
   const handler = (start: number, count: number) => buildActivities(actor, start, count)
   const json = await activityPubCollectionPagination(actorOutboxUrl, handler, req.query.page, req.query.size)
 
-  return activityPubResponse(activityPubContextify(json), res)
+  return activityPubResponse(activityPubContextify(json, 'Collection'), res)
 }
 
 async function buildActivities (actor: MActorLight, start: number, count: number) {
index 215b50b6995305aa7f3a712c96dd11f6ea8d0f55..cccb7b1c1d4a081dc90e89dec2faee5dba0ecf00 100644 (file)
@@ -1,14 +1,3 @@
-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
 
@@ -16,6 +5,5 @@ function getAPId (object: string | { id: string }) {
 }
 
 export {
-  buildSignedActivity,
   getAPId
 }
index 2bd5bb06649fc178b5e5080f8b8b371da740eb6c..6f54913875858aff5b10f5131282c2137248a34d 100644 (file)
@@ -22,7 +22,7 @@ function buildAudience (followerUrls: string[], isPublic = true) {
 }
 
 function audiencify<T> (object: T, audience: ActivityAudience) {
-  return Object.assign(object, audience)
+  return { ...audience, ...object }
 }
 
 // ---------------------------------------------------------------------------
index 71f08da80052953c1a0b1fd74be1cd1c107c895f..3bc40e2aa2749114f9786d0b40a76948776bfd03 100644 (file)
 import { ContextType } from '@shared/models'
 
-function getContextData (type: ContextType) {
-  const context: any[] = [
-    'https://www.w3.org/ns/activitystreams',
-    'https://w3id.org/security/v1',
-    {
-      RsaSignature2017: 'https://w3id.org/security#RsaSignature2017'
+function activityPubContextify <T> (data: T, type: ContextType) {
+  return { ...getContextData(type), ...data }
+}
+
+// ---------------------------------------------------------------------------
+
+export {
+  getContextData,
+  activityPubContextify
+}
+
+// ---------------------------------------------------------------------------
+
+type ContextValue = { [ id: string ]: (string | { '@type': string, '@id': string }) }
+
+const contextStore = {
+  Video: buildContext({
+    Hashtag: 'as:Hashtag',
+    uuid: 'sc:identifier',
+    category: 'sc:category',
+    licence: 'sc:license',
+    subtitleLanguage: 'sc:subtitleLanguage',
+    sensitive: 'as:sensitive',
+    language: 'sc:inLanguage',
+
+    // TODO: remove in a few versions, introduced in 4.2
+    icons: 'as:icon',
+
+    isLiveBroadcast: 'sc:isLiveBroadcast',
+    liveSaveReplay: {
+      '@type': 'sc:Boolean',
+      '@id': 'pt:liveSaveReplay'
+    },
+    permanentLive: {
+      '@type': 'sc:Boolean',
+      '@id': 'pt:permanentLive'
+    },
+    latencyMode: {
+      '@type': 'sc:Number',
+      '@id': 'pt:latencyMode'
+    },
+
+    Infohash: 'pt:Infohash',
+
+    originallyPublishedAt: 'sc:datePublished',
+    views: {
+      '@type': 'sc:Number',
+      '@id': 'pt:views'
+    },
+    state: {
+      '@type': 'sc:Number',
+      '@id': 'pt:state'
+    },
+    size: {
+      '@type': 'sc:Number',
+      '@id': 'pt:size'
+    },
+    fps: {
+      '@type': 'sc:Number',
+      '@id': 'pt:fps'
+    },
+    commentsEnabled: {
+      '@type': 'sc:Boolean',
+      '@id': 'pt:commentsEnabled'
+    },
+    downloadEnabled: {
+      '@type': 'sc:Boolean',
+      '@id': 'pt:downloadEnabled'
+    },
+    waitTranscoding: {
+      '@type': 'sc:Boolean',
+      '@id': 'pt:waitTranscoding'
+    },
+    support: {
+      '@type': 'sc:Text',
+      '@id': 'pt:support'
+    },
+    likes: {
+      '@id': 'as:likes',
+      '@type': '@id'
+    },
+    dislikes: {
+      '@id': 'as:dislikes',
+      '@type': '@id'
+    },
+    shares: {
+      '@id': 'as:shares',
+      '@type': '@id'
+    },
+    comments: {
+      '@id': 'as:comments',
+      '@type': '@id'
     }
-  ]
+  }),
 
-  if (type !== 'View' && type !== 'Announce') {
-    const additional = {
-      pt: 'https://joinpeertube.org/ns#',
-      sc: 'http://schema.org#'
+  Playlist: buildContext({
+    Playlist: 'pt:Playlist',
+    PlaylistElement: 'pt:PlaylistElement',
+    position: {
+      '@type': 'sc:Number',
+      '@id': 'pt:position'
+    },
+    startTimestamp: {
+      '@type': 'sc:Number',
+      '@id': 'pt:startTimestamp'
+    },
+    stopTimestamp: {
+      '@type': 'sc:Number',
+      '@id': 'pt:stopTimestamp'
     }
+  }),
+
+  CacheFile: buildContext({
+    expires: 'sc:expires',
+    CacheFile: 'pt:CacheFile'
+  }),
 
-    if (type === 'CacheFile') {
-      Object.assign(additional, {
-        expires: 'sc:expires',
-        CacheFile: 'pt:CacheFile'
-      })
-    } else {
-      Object.assign(additional, {
-        Hashtag: 'as:Hashtag',
-        uuid: 'sc:identifier',
-        category: 'sc:category',
-        licence: 'sc:license',
-        subtitleLanguage: 'sc:subtitleLanguage',
-        sensitive: 'as:sensitive',
-        language: 'sc:inLanguage',
-
-        // TODO: remove in a few versions, introduced in 4.2
-        icons: 'as:icon',
-
-        isLiveBroadcast: 'sc:isLiveBroadcast',
-        liveSaveReplay: {
-          '@type': 'sc:Boolean',
-          '@id': 'pt:liveSaveReplay'
-        },
-        permanentLive: {
-          '@type': 'sc:Boolean',
-          '@id': 'pt:permanentLive'
-        },
-        latencyMode: {
-          '@type': 'sc:Number',
-          '@id': 'pt:latencyMode'
-        },
-
-        Infohash: 'pt:Infohash',
-        Playlist: 'pt:Playlist',
-        PlaylistElement: 'pt:PlaylistElement',
-
-        originallyPublishedAt: 'sc:datePublished',
-        views: {
-          '@type': 'sc:Number',
-          '@id': 'pt:views'
-        },
-        state: {
-          '@type': 'sc:Number',
-          '@id': 'pt:state'
-        },
-        size: {
-          '@type': 'sc:Number',
-          '@id': 'pt:size'
-        },
-        fps: {
-          '@type': 'sc:Number',
-          '@id': 'pt:fps'
-        },
-        startTimestamp: {
-          '@type': 'sc:Number',
-          '@id': 'pt:startTimestamp'
-        },
-        stopTimestamp: {
-          '@type': 'sc:Number',
-          '@id': 'pt:stopTimestamp'
-        },
-        position: {
-          '@type': 'sc:Number',
-          '@id': 'pt:position'
-        },
-        commentsEnabled: {
-          '@type': 'sc:Boolean',
-          '@id': 'pt:commentsEnabled'
-        },
-        downloadEnabled: {
-          '@type': 'sc:Boolean',
-          '@id': 'pt:downloadEnabled'
-        },
-        waitTranscoding: {
-          '@type': 'sc:Boolean',
-          '@id': 'pt:waitTranscoding'
-        },
-        support: {
-          '@type': 'sc:Text',
-          '@id': 'pt:support'
-        },
-        likes: {
-          '@id': 'as:likes',
-          '@type': '@id'
-        },
-        dislikes: {
-          '@id': 'as:dislikes',
-          '@type': '@id'
-        },
-        playlists: {
-          '@id': 'pt:playlists',
-          '@type': '@id'
-        },
-        shares: {
-          '@id': 'as:shares',
-          '@type': '@id'
-        },
-        comments: {
-          '@id': 'as:comments',
-          '@type': '@id'
-        }
-      })
+  Flag: buildContext({
+    Hashtag: 'as:Hashtag'
+  }),
+
+  Actor: buildContext({
+    playlists: {
+      '@id': 'pt:playlists',
+      '@type': '@id'
     }
+  }),
 
-    context.push(additional)
-  }
+  Follow: buildContext(),
+  Reject: buildContext(),
+  Accept: buildContext(),
+  View: buildContext(),
+  Announce: buildContext(),
+  Comment: buildContext(),
+  Delete: buildContext(),
+  Rate: buildContext()
+}
 
+function getContextData (type: ContextType) {
   return {
-    '@context': context
+    '@context': contextStore[type]
   }
 }
 
-function activityPubContextify <T> (data: T, type: ContextType = 'All') {
-  return Object.assign({}, data, getContextData(type))
-}
+function buildContext (contextValue?: ContextValue) {
+  const baseContext = [
+    'https://www.w3.org/ns/activitystreams',
+    'https://w3id.org/security/v1',
+    {
+      RsaSignature2017: 'https://w3id.org/security#RsaSignature2017'
+    }
+  ]
 
-export {
-  getContextData,
-  activityPubContextify
+  if (!contextValue) return baseContext
+
+  return [
+    ...baseContext,
+
+    {
+      pt: 'https://joinpeertube.org/ns#',
+      sc: 'http://schema.org#',
+
+      ...contextValue
+    }
+  ]
 }
similarity index 63%
rename from server/lib/job-queue/handlers/utils/activitypub-http-utils.ts
rename to server/lib/activitypub/send/http.ts
index 2a03325b7f8d5a9fcfd845b84385e1248603a6fa..d8d0b85422f38fcd56f755c943ec4bb6182ef874 100644 (file)
@@ -1,12 +1,12 @@
-import { buildDigest } from '@server/helpers/peertube-crypto'
-import { buildSignedActivity } from '@server/lib/activitypub/activity'
+import { buildDigest, signJsonLDObject } from '@server/helpers/peertube-crypto'
+import { ACTIVITY_PUB, HTTP_SIGNATURE } from '@server/initializers/constants'
+import { ActorModel } from '@server/models/actor/actor'
 import { getServerActor } from '@server/models/application/application'
+import { MActor } from '@server/types/models'
 import { ContextType } from '@shared/models/activitypub/context'
-import { ACTIVITY_PUB, HTTP_SIGNATURE } from '../../../../initializers/constants'
-import { ActorModel } from '../../../../models/actor/actor'
-import { MActor } from '../../../../types/models'
+import { activityPubContextify } from '../context'
 
-type Payload <T> = { body: T, contextType?: ContextType, signatureActorId?: number }
+type Payload <T> = { body: T, contextType: ContextType, signatureActorId?: number }
 
 async function computeBody <T> (
   payload: Payload<T>
@@ -17,7 +17,7 @@ async function computeBody <T> (
     const actorSignature = await ActorModel.load(payload.signatureActorId)
     if (!actorSignature) throw new Error('Unknown signature actor id.')
 
-    body = await buildSignedActivity(actorSignature, payload.body, payload.contextType)
+    body = await signAndContextify(actorSignature, payload.body, payload.contextType)
   }
 
   return body
@@ -52,8 +52,17 @@ function buildGlobalHeaders (body: any) {
   }
 }
 
+function signAndContextify <T> (byActor: MActor, data: T, contextType: ContextType | null) {
+  const activity = contextType
+    ? activityPubContextify(data, contextType)
+    : data
+
+  return signJsonLDObject(byActor, activity)
+}
+
 export {
   buildGlobalHeaders,
   computeBody,
-  buildSignedRequestOptions
+  buildSignedRequestOptions,
+  signAndContextify
 }
index 0289368100044f8dd0afee9586f4daba610d529b..852ea2e741c8f4c1b714822537d2598e87b25180 100644 (file)
@@ -1,4 +1,4 @@
-export * from './send-accept'
+export * from './http'
 export * from './send-accept'
 export * from './send-announce'
 export * from './send-create'
index 939f06d9e8f93576f85799789a0830e578671eb3..4c9bcbb0b3f99086733be1e4d908f412fee85773 100644 (file)
@@ -21,7 +21,12 @@ function sendAccept (actorFollow: MActorFollowActors) {
   const url = getLocalActorFollowAcceptActivityPubUrl(actorFollow)
   const data = buildAcceptActivity(url, me, followData)
 
-  return unicastTo(data, me, follower.inboxUrl)
+  return unicastTo({
+    data,
+    byActor: me,
+    toActorUrl: follower.inboxUrl,
+    contextType: 'Accept'
+  })
 }
 
 // ---------------------------------------------------------------------------
index 7897beb75147875c9c7005b838ab589f45b73b6e..6c078b0475531ce34e41b324a467f0b07dd4a91e 100644 (file)
@@ -23,13 +23,19 @@ async function buildAnnounceWithVideoAudience (
   return { activity, actorsInvolvedInVideo }
 }
 
-async function sendVideoAnnounce (byActor: MActorLight, videoShare: MVideoShare, video: MVideo, t: Transaction) {
-  const { activity, actorsInvolvedInVideo } = await buildAnnounceWithVideoAudience(byActor, videoShare, video, t)
+async function sendVideoAnnounce (byActor: MActorLight, videoShare: MVideoShare, video: MVideo, transaction: Transaction) {
+  const { activity, actorsInvolvedInVideo } = await buildAnnounceWithVideoAudience(byActor, videoShare, video, transaction)
 
   logger.info('Creating job to send announce %s.', videoShare.url)
 
-  const followersException = [ byActor ]
-  return broadcastToFollowers(activity, byActor, actorsInvolvedInVideo, t, followersException, 'Announce')
+  return broadcastToFollowers({
+    data: activity,
+    byActor,
+    toFollowersOf: actorsInvolvedInVideo,
+    transaction,
+    actorsException: [ byActor ],
+    contextType: 'Announce'
+  })
 }
 
 function buildAnnounceActivity (url: string, byActor: MActorLight, object: string, audience?: ActivityAudience): ActivityAnnounce {
index f6d897220d9b05cbb4e2f030114efa93c9fe4641..5d87634952cc638c824efd462cfe37d3474b51bd 100644 (file)
@@ -25,7 +25,7 @@ import {
 
 const lTags = loggerTagsFactory('ap', 'create')
 
-async function sendCreateVideo (video: MVideoAP, t: Transaction) {
+async function sendCreateVideo (video: MVideoAP, transaction: Transaction) {
   if (!video.hasPrivacyForFederation()) return undefined
 
   logger.info('Creating job to send video creation of %s.', video.url, lTags(video.uuid))
@@ -36,7 +36,13 @@ async function sendCreateVideo (video: MVideoAP, t: Transaction) {
   const audience = getAudience(byActor, video.privacy === VideoPrivacy.PUBLIC)
   const createActivity = buildCreateActivity(video.url, byActor, videoObject, audience)
 
-  return broadcastToFollowers(createActivity, byActor, [ byActor ], t)
+  return broadcastToFollowers({
+    data: createActivity,
+    byActor,
+    toFollowersOf: [ byActor ],
+    transaction,
+    contextType: 'Video'
+  })
 }
 
 async function sendCreateCacheFile (
@@ -55,7 +61,7 @@ async function sendCreateCacheFile (
   })
 }
 
-async function sendCreateVideoPlaylist (playlist: MVideoPlaylistFull, t: Transaction) {
+async function sendCreateVideoPlaylist (playlist: MVideoPlaylistFull, transaction: Transaction) {
   if (playlist.privacy === VideoPlaylistPrivacy.PRIVATE) return undefined
 
   logger.info('Creating job to send create video playlist of %s.', playlist.url, lTags(playlist.uuid))
@@ -63,7 +69,7 @@ async function sendCreateVideoPlaylist (playlist: MVideoPlaylistFull, t: Transac
   const byActor = playlist.OwnerAccount.Actor
   const audience = getAudience(byActor, playlist.privacy === VideoPlaylistPrivacy.PUBLIC)
 
-  const object = await playlist.toActivityPubObject(null, t)
+  const object = await playlist.toActivityPubObject(null, transaction)
   const createActivity = buildCreateActivity(playlist.url, byActor, object, audience)
 
   const serverActor = await getServerActor()
@@ -71,19 +77,25 @@ async function sendCreateVideoPlaylist (playlist: MVideoPlaylistFull, t: Transac
 
   if (playlist.VideoChannel) toFollowersOf.push(playlist.VideoChannel.Actor)
 
-  return broadcastToFollowers(createActivity, byActor, toFollowersOf, t)
+  return broadcastToFollowers({
+    data: createActivity,
+    byActor,
+    toFollowersOf,
+    transaction,
+    contextType: 'Playlist'
+  })
 }
 
-async function sendCreateVideoComment (comment: MCommentOwnerVideo, t: Transaction) {
+async function sendCreateVideoComment (comment: MCommentOwnerVideo, transaction: Transaction) {
   logger.info('Creating job to send comment %s.', comment.url)
 
   const isOrigin = comment.Video.isOwned()
 
   const byActor = comment.Account.Actor
-  const threadParentComments = await VideoCommentModel.listThreadParentComments(comment, t)
+  const threadParentComments = await VideoCommentModel.listThreadParentComments(comment, transaction)
   const commentObject = comment.toActivityPubObject(threadParentComments)
 
-  const actorsInvolvedInComment = await getActorsInvolvedInVideo(comment.Video, t)
+  const actorsInvolvedInComment = await getActorsInvolvedInVideo(comment.Video, transaction)
   // Add the actor that commented too
   actorsInvolvedInComment.push(byActor)
 
@@ -101,16 +113,45 @@ async function sendCreateVideoComment (comment: MCommentOwnerVideo, t: Transacti
 
   // This was a reply, send it to the parent actors
   const actorsException = [ byActor ]
-  await broadcastToActors(createActivity, byActor, parentsCommentActors, t, actorsException)
+  await broadcastToActors({
+    data: createActivity,
+    byActor,
+    toActors: parentsCommentActors,
+    transaction,
+    actorsException,
+    contextType: 'Comment'
+  })
 
   // Broadcast to our followers
-  await broadcastToFollowers(createActivity, byActor, [ byActor ], t)
+  await broadcastToFollowers({
+    data: createActivity,
+    byActor,
+    toFollowersOf: [ byActor ],
+    transaction,
+    contextType: 'Comment'
+  })
 
   // Send to actors involved in the comment
-  if (isOrigin) return broadcastToFollowers(createActivity, byActor, actorsInvolvedInComment, t, actorsException)
+  if (isOrigin) {
+    return broadcastToFollowers({
+      data: createActivity,
+      byActor,
+      toFollowersOf: actorsInvolvedInComment,
+      transaction,
+      actorsException,
+      contextType: 'Comment'
+    })
+  }
 
   // Send to origin
-  t.afterCommit(() => unicastTo(createActivity, byActor, comment.Video.VideoChannel.Account.Actor.getSharedInbox()))
+  return transaction.afterCommit(() => {
+    return unicastTo({
+      data: createActivity,
+      byActor,
+      toActorUrl: comment.Video.VideoChannel.Account.Actor.getSharedInbox(),
+      contextType: 'Comment'
+    })
+  })
 }
 
 function buildCreateActivity (url: string, byActor: MActorLight, object: any, audience?: ActivityAudience): ActivityCreate {
@@ -144,8 +185,8 @@ async function sendVideoRelatedCreateActivity (options: {
   video: MVideoAccountLight
   url: string
   object: any
+  contextType: ContextType
   transaction?: Transaction
-  contextType?: ContextType
 }) {
   const activityBuilder = (audience: ActivityAudience) => {
     return buildCreateActivity(options.url, options.byActor, options.object, audience)
index 39216cdebedbf762c931da6ab8f653915ce3bb7b..0d85d90012d1a0081dc3463167a12f938dc7ec83 100644 (file)
@@ -23,16 +23,16 @@ async function sendDeleteVideo (video: MVideoAccountLight, transaction: Transact
     return buildDeleteActivity(url, video.url, byActor, audience)
   }
 
-  return sendVideoRelatedActivity(activityBuilder, { byActor, video, transaction })
+  return sendVideoRelatedActivity(activityBuilder, { byActor, video, contextType: 'Delete', transaction })
 }
 
-async function sendDeleteActor (byActor: ActorModel, t: Transaction) {
+async function sendDeleteActor (byActor: ActorModel, transaction: Transaction) {
   logger.info('Creating job to broadcast delete of actor %s.', byActor.url)
 
   const url = getDeleteActivityPubUrl(byActor.url)
   const activity = buildDeleteActivity(url, byActor.url, byActor)
 
-  const actorsInvolved = await VideoShareModel.loadActorsWhoSharedVideosOf(byActor.id, t)
+  const actorsInvolved = await VideoShareModel.loadActorsWhoSharedVideosOf(byActor.id, transaction)
 
   // In case the actor did not have any videos
   const serverActor = await getServerActor()
@@ -40,10 +40,16 @@ async function sendDeleteActor (byActor: ActorModel, t: Transaction) {
 
   actorsInvolved.push(byActor)
 
-  return broadcastToFollowers(activity, byActor, actorsInvolved, t)
+  return broadcastToFollowers({
+    data: activity,
+    byActor,
+    toFollowersOf: actorsInvolved,
+    contextType: 'Delete',
+    transaction
+  })
 }
 
-async function sendDeleteVideoComment (videoComment: MCommentOwnerVideo, t: Transaction) {
+async function sendDeleteVideoComment (videoComment: MCommentOwnerVideo, transaction: Transaction) {
   logger.info('Creating job to send delete of comment %s.', videoComment.url)
 
   const isVideoOrigin = videoComment.Video.isOwned()
@@ -53,10 +59,10 @@ async function sendDeleteVideoComment (videoComment: MCommentOwnerVideo, t: Tran
     ? videoComment.Account.Actor
     : videoComment.Video.VideoChannel.Account.Actor
 
-  const threadParentComments = await VideoCommentModel.listThreadParentComments(videoComment, t)
+  const threadParentComments = await VideoCommentModel.listThreadParentComments(videoComment, transaction)
   const threadParentCommentsFiltered = threadParentComments.filter(c => !c.isDeleted())
 
-  const actorsInvolvedInComment = await getActorsInvolvedInVideo(videoComment.Video, t)
+  const actorsInvolvedInComment = await getActorsInvolvedInVideo(videoComment.Video, transaction)
   actorsInvolvedInComment.push(byActor) // Add the actor that commented the video
 
   const audience = getVideoCommentAudience(videoComment, threadParentCommentsFiltered, actorsInvolvedInComment, isVideoOrigin)
@@ -64,19 +70,48 @@ async function sendDeleteVideoComment (videoComment: MCommentOwnerVideo, t: Tran
 
   // This was a reply, send it to the parent actors
   const actorsException = [ byActor ]
-  await broadcastToActors(activity, byActor, threadParentCommentsFiltered.map(c => c.Account.Actor), t, actorsException)
+  await broadcastToActors({
+    data: activity,
+    byActor,
+    toActors: threadParentCommentsFiltered.map(c => c.Account.Actor),
+    transaction,
+    contextType: 'Delete',
+    actorsException
+  })
 
   // Broadcast to our followers
-  await broadcastToFollowers(activity, byActor, [ byActor ], t)
+  await broadcastToFollowers({
+    data: activity,
+    byActor,
+    toFollowersOf: [ byActor ],
+    contextType: 'Delete',
+    transaction
+  })
 
   // Send to actors involved in the comment
-  if (isVideoOrigin) return broadcastToFollowers(activity, byActor, actorsInvolvedInComment, t, actorsException)
+  if (isVideoOrigin) {
+    return broadcastToFollowers({
+      data: activity,
+      byActor,
+      toFollowersOf: actorsInvolvedInComment,
+      transaction,
+      contextType: 'Delete',
+      actorsException
+    })
+  }
 
   // Send to origin
-  t.afterCommit(() => unicastTo(activity, byActor, videoComment.Video.VideoChannel.Account.Actor.getSharedInbox()))
+  return transaction.afterCommit(() => {
+    return unicastTo({
+      data: activity,
+      byActor,
+      toActorUrl: videoComment.Video.VideoChannel.Account.Actor.getSharedInbox(),
+      contextType: 'Delete'
+    })
+  })
 }
 
-async function sendDeleteVideoPlaylist (videoPlaylist: MVideoPlaylistFullSummary, t: Transaction) {
+async function sendDeleteVideoPlaylist (videoPlaylist: MVideoPlaylistFullSummary, transaction: Transaction) {
   logger.info('Creating job to send delete of playlist %s.', videoPlaylist.url)
 
   const byActor = videoPlaylist.OwnerAccount.Actor
@@ -89,7 +124,13 @@ async function sendDeleteVideoPlaylist (videoPlaylist: MVideoPlaylistFullSummary
 
   if (videoPlaylist.VideoChannel) toFollowersOf.push(videoPlaylist.VideoChannel.Actor)
 
-  return broadcastToFollowers(activity, byActor, toFollowersOf, t)
+  return broadcastToFollowers({
+    data: activity,
+    byActor,
+    toFollowersOf,
+    contextType: 'Delete',
+    transaction
+  })
 }
 
 // ---------------------------------------------------------------------------
index ecb11e9bf9bf7f0d1eb2a007905f190e13333977..959e74823f928a085df818ac7030353465bab55c 100644 (file)
@@ -6,7 +6,7 @@ import { audiencify, getAudience } from '../audience'
 import { getVideoDislikeActivityPubUrlByLocalActor } from '../url'
 import { sendVideoActivityToOrigin } from './shared/send-utils'
 
-function sendDislike (byActor: MActor, video: MVideoAccountLight, t: Transaction) {
+function sendDislike (byActor: MActor, video: MVideoAccountLight, transaction: Transaction) {
   logger.info('Creating job to dislike %s.', video.url)
 
   const activityBuilder = (audience: ActivityAudience) => {
@@ -15,7 +15,7 @@ function sendDislike (byActor: MActor, video: MVideoAccountLight, t: Transaction
     return buildDislikeActivity(url, byActor, video, audience)
   }
 
-  return sendVideoActivityToOrigin(activityBuilder, { byActor, video, transaction: t })
+  return sendVideoActivityToOrigin(activityBuilder, { byActor, video, transaction, contextType: 'Rate' })
 }
 
 function buildDislikeActivity (url: string, byActor: MActorAudience, video: MVideoUrl, audience?: ActivityAudience): ActivityDislike {
index 6df4e7eb8b7127ecc6e04face4ee722ce98e4f50..138eb5adc8d1043142bd04af816eda8428401c86 100644 (file)
@@ -17,16 +17,20 @@ function sendAbuse (byActor: MActor, abuse: MAbuseAP, flaggedAccount: MAccountLi
   const audience = { to: [ flaggedAccount.Actor.url ], cc: [] }
   const flagActivity = buildFlagActivity(url, byActor, abuse, audience)
 
-  t.afterCommit(() => unicastTo(flagActivity, byActor, flaggedAccount.Actor.getSharedInbox()))
+  return t.afterCommit(() => {
+    return unicastTo({
+      data: flagActivity,
+      byActor,
+      toActorUrl: flaggedAccount.Actor.getSharedInbox(),
+      contextType: 'Flag'
+    })
+  })
 }
 
 function buildFlagActivity (url: string, byActor: MActor, abuse: MAbuseAP, audience: ActivityAudience): ActivityFlag {
   if (!audience) audience = getAudience(byActor)
 
-  const activity = Object.assign(
-    { id: url, actor: byActor.url },
-    abuse.toActivityPubObject()
-  )
+  const activity = { id: url, actor: byActor.url, ...abuse.toActivityPubObject() }
 
   return audiencify(activity, audience)
 }
index aeeb50a2a9410932e2e58a435feae1201d4e6f98..57501dadb0551eecf31342749be1bf9a4ca60fd9 100644 (file)
@@ -15,7 +15,9 @@ function sendFollow (actorFollow: MActorFollowActors, t: Transaction) {
 
   const data = buildFollowActivity(actorFollow.url, me, following)
 
-  t.afterCommit(() => unicastTo(data, me, following.inboxUrl))
+  return t.afterCommit(() => {
+    return unicastTo({ data, byActor: me, toActorUrl: following.inboxUrl, contextType: 'Follow' })
+  })
 }
 
 function buildFollowActivity (url: string, byActor: MActor, targetActor: MActor): ActivityFollow {
index a5fe95e0af4827d38952cb23feea500fc7fc7bb9..46c9fdec9d96e6f884c16484bbbef905e99e5624 100644 (file)
@@ -6,7 +6,7 @@ import { audiencify, getAudience } from '../audience'
 import { getVideoLikeActivityPubUrlByLocalActor } from '../url'
 import { sendVideoActivityToOrigin } from './shared/send-utils'
 
-function sendLike (byActor: MActor, video: MVideoAccountLight, t: Transaction) {
+function sendLike (byActor: MActor, video: MVideoAccountLight, transaction: Transaction) {
   logger.info('Creating job to like %s.', video.url)
 
   const activityBuilder = (audience: ActivityAudience) => {
@@ -15,7 +15,7 @@ function sendLike (byActor: MActor, video: MVideoAccountLight, t: Transaction) {
     return buildLikeActivity(url, byActor, video, audience)
   }
 
-  return sendVideoActivityToOrigin(activityBuilder, { byActor, video, transaction: t })
+  return sendVideoActivityToOrigin(activityBuilder, { byActor, video, transaction, contextType: 'Rate' })
 }
 
 function buildLikeActivity (url: string, byActor: MActorAudience, video: MVideoUrl, audience?: ActivityAudience): ActivityLike {
index 01b8f743b537e481e631a9032254d9054f6e521f..83d8dfba72366e78888216535a0aec006b2c32de 100644 (file)
@@ -18,7 +18,7 @@ function sendReject (followUrl: string, follower: MActor, following: MActor) {
   const url = getLocalActorFollowRejectActivityPubUrl(follower, following)
   const data = buildRejectActivity(url, following, followData)
 
-  return unicastTo(data, following, follower.inboxUrl)
+  return unicastTo({ data, byActor: following, toActorUrl: follower.inboxUrl, contextType: 'Reject' })
 }
 
 // ---------------------------------------------------------------------------
index 948ca0d7ab127883925c3a713ce183875119bc75..36d7ef99199e58a68b095f16da6331193343245c 100644 (file)
@@ -6,7 +6,8 @@ import {
   ActivityDislike,
   ActivityFollow,
   ActivityLike,
-  ActivityUndo
+  ActivityUndo,
+  ContextType
 } from '@shared/models'
 import { logger } from '../../../helpers/logger'
 import { VideoModel } from '../../../models/video/video'
@@ -43,24 +44,37 @@ function sendUndoFollow (actorFollow: MActorFollowActors, t: Transaction) {
   const followActivity = buildFollowActivity(actorFollow.url, me, following)
   const undoActivity = undoActivityData(undoUrl, me, followActivity)
 
-  t.afterCommit(() => unicastTo(undoActivity, me, following.inboxUrl))
+  return t.afterCommit(() => {
+    return unicastTo({
+      data: undoActivity,
+      byActor: me,
+      toActorUrl: following.inboxUrl,
+      contextType: 'Follow'
+    })
+  })
 }
 
 // ---------------------------------------------------------------------------
 
-async function sendUndoAnnounce (byActor: MActorLight, videoShare: MVideoShare, video: MVideo, t: Transaction) {
+async function sendUndoAnnounce (byActor: MActorLight, videoShare: MVideoShare, video: MVideo, transaction: Transaction) {
   logger.info('Creating job to undo announce %s.', videoShare.url)
 
   const undoUrl = getUndoActivityPubUrl(videoShare.url)
 
-  const { activity: announceActivity, actorsInvolvedInVideo } = await buildAnnounceWithVideoAudience(byActor, videoShare, video, t)
-  const undoActivity = undoActivityData(undoUrl, byActor, announceActivity)
-
-  const followersException = [ byActor ]
-  return broadcastToFollowers(undoActivity, byActor, actorsInvolvedInVideo, t, followersException)
+  const { activity: announce, actorsInvolvedInVideo } = await buildAnnounceWithVideoAudience(byActor, videoShare, video, transaction)
+  const undoActivity = undoActivityData(undoUrl, byActor, announce)
+
+  return broadcastToFollowers({
+    data: undoActivity,
+    byActor,
+    toFollowersOf: actorsInvolvedInVideo,
+    transaction,
+    actorsException: [ byActor ],
+    contextType: 'Announce'
+  })
 }
 
-async function sendUndoCacheFile (byActor: MActor, redundancyModel: MVideoRedundancyVideo, t: Transaction) {
+async function sendUndoCacheFile (byActor: MActor, redundancyModel: MVideoRedundancyVideo, transaction: Transaction) {
   logger.info('Creating job to undo cache file %s.', redundancyModel.url)
 
   const associatedVideo = redundancyModel.getVideo()
@@ -72,7 +86,14 @@ async function sendUndoCacheFile (byActor: MActor, redundancyModel: MVideoRedund
   const video = await VideoModel.loadAndPopulateAccountAndServerAndTags(associatedVideo.id)
   const createActivity = buildCreateActivity(redundancyModel.url, byActor, redundancyModel.toActivityPubObject())
 
-  return sendUndoVideoRelatedActivity({ byActor, video, url: redundancyModel.url, activity: createActivity, transaction: t })
+  return sendUndoVideoRelatedActivity({
+    byActor,
+    video,
+    url: redundancyModel.url,
+    activity: createActivity,
+    contextType: 'CacheFile',
+    transaction
+  })
 }
 
 // ---------------------------------------------------------------------------
@@ -83,7 +104,7 @@ async function sendUndoLike (byActor: MActor, video: MVideoAccountLight, t: Tran
   const likeUrl = getVideoLikeActivityPubUrlByLocalActor(byActor, video)
   const likeActivity = buildLikeActivity(likeUrl, byActor, video)
 
-  return sendUndoVideoToOriginActivity({ byActor, video, url: likeUrl, activity: likeActivity, transaction: t })
+  return sendUndoVideoRateToOriginActivity({ byActor, video, url: likeUrl, activity: likeActivity, transaction: t })
 }
 
 async function sendUndoDislike (byActor: MActor, video: MVideoAccountLight, t: Transaction) {
@@ -92,7 +113,7 @@ async function sendUndoDislike (byActor: MActor, video: MVideoAccountLight, t: T
   const dislikeUrl = getVideoDislikeActivityPubUrlByLocalActor(byActor, video)
   const dislikeActivity = buildDislikeActivity(dislikeUrl, byActor, video)
 
-  return sendUndoVideoToOriginActivity({ byActor, video, url: dislikeUrl, activity: dislikeActivity, transaction: t })
+  return sendUndoVideoRateToOriginActivity({ byActor, video, url: dislikeUrl, activity: dislikeActivity, transaction: t })
 }
 
 // ---------------------------------------------------------------------------
@@ -131,6 +152,7 @@ async function sendUndoVideoRelatedActivity (options: {
   video: MVideoAccountLight
   url: string
   activity: ActivityFollow | ActivityCreate | ActivityAnnounce
+  contextType: ContextType
   transaction: Transaction
 }) {
   const activityBuilder = (audience: ActivityAudience) => {
@@ -142,7 +164,7 @@ async function sendUndoVideoRelatedActivity (options: {
   return sendVideoRelatedActivity(activityBuilder, options)
 }
 
-async function sendUndoVideoToOriginActivity (options: {
+async function sendUndoVideoRateToOriginActivity (options: {
   byActor: MActor
   video: MVideoAccountLight
   url: string
@@ -155,5 +177,5 @@ async function sendUndoVideoToOriginActivity (options: {
     return undoActivityData(undoUrl, options.byActor, options.activity, audience)
   }
 
-  return sendVideoActivityToOrigin(activityBuilder, options)
+  return sendVideoActivityToOrigin(activityBuilder, { ...options, contextType: 'Rate' })
 }
index 7c9e72cbcec2b464879e0fd1c68e2de6c2a258ef..3577ece02c5710232bf352bab0ff10ec0842e722 100644 (file)
@@ -20,20 +20,20 @@ import { getUpdateActivityPubUrl } from '../url'
 import { getActorsInvolvedInVideo } from './shared'
 import { broadcastToFollowers, sendVideoRelatedActivity } from './shared/send-utils'
 
-async function sendUpdateVideo (videoArg: MVideoAPWithoutCaption, t: Transaction, overrodeByActor?: MActor) {
+async function sendUpdateVideo (videoArg: MVideoAPWithoutCaption, transaction: Transaction, overriddenByActor?: MActor) {
   const video = videoArg as MVideoAP
 
   if (!video.hasPrivacyForFederation()) return undefined
 
   logger.info('Creating job to update video %s.', video.url)
 
-  const byActor = overrodeByActor || video.VideoChannel.Account.Actor
+  const byActor = overriddenByActor || video.VideoChannel.Account.Actor
 
   const url = getUpdateActivityPubUrl(video.url, video.updatedAt.toISOString())
 
   // Needed to build the AP object
   if (!video.VideoCaptions) {
-    video.VideoCaptions = await video.$get('VideoCaptions', { transaction: t })
+    video.VideoCaptions = await video.$get('VideoCaptions', { transaction })
   }
 
   const videoObject = video.toActivityPubObject()
@@ -41,13 +41,19 @@ async function sendUpdateVideo (videoArg: MVideoAPWithoutCaption, t: Transaction
 
   const updateActivity = buildUpdateActivity(url, byActor, videoObject, audience)
 
-  const actorsInvolved = await getActorsInvolvedInVideo(video, t)
-  if (overrodeByActor) actorsInvolved.push(overrodeByActor)
+  const actorsInvolved = await getActorsInvolvedInVideo(video, transaction)
+  if (overriddenByActor) actorsInvolved.push(overriddenByActor)
 
-  return broadcastToFollowers(updateActivity, byActor, actorsInvolved, t)
+  return broadcastToFollowers({
+    data: updateActivity,
+    byActor,
+    toFollowersOf: actorsInvolved,
+    contextType: 'Video',
+    transaction
+  })
 }
 
-async function sendUpdateActor (accountOrChannel: MChannelDefault | MAccountDefault, t: Transaction) {
+async function sendUpdateActor (accountOrChannel: MChannelDefault | MAccountDefault, transaction: Transaction) {
   const byActor = accountOrChannel.Actor
 
   logger.info('Creating job to update actor %s.', byActor.url)
@@ -60,15 +66,21 @@ async function sendUpdateActor (accountOrChannel: MChannelDefault | MAccountDefa
   let actorsInvolved: MActor[]
   if (accountOrChannel instanceof AccountModel) {
     // Actors that shared my videos are involved too
-    actorsInvolved = await VideoShareModel.loadActorsWhoSharedVideosOf(byActor.id, t)
+    actorsInvolved = await VideoShareModel.loadActorsWhoSharedVideosOf(byActor.id, transaction)
   } else {
     // Actors that shared videos of my channel are involved too
-    actorsInvolved = await VideoShareModel.loadActorsByVideoChannel(accountOrChannel.id, t)
+    actorsInvolved = await VideoShareModel.loadActorsByVideoChannel(accountOrChannel.id, transaction)
   }
 
   actorsInvolved.push(byActor)
 
-  return broadcastToFollowers(updateActivity, byActor, actorsInvolved, t)
+  return broadcastToFollowers({
+    data: updateActivity,
+    byActor,
+    toFollowersOf: actorsInvolved,
+    transaction,
+    contextType: 'Actor'
+  })
 }
 
 async function sendUpdateCacheFile (byActor: MActorLight, redundancyModel: MVideoRedundancyVideo) {
@@ -92,7 +104,7 @@ async function sendUpdateCacheFile (byActor: MActorLight, redundancyModel: MVide
   return sendVideoRelatedActivity(activityBuilder, { byActor, video, contextType: 'CacheFile' })
 }
 
-async function sendUpdateVideoPlaylist (videoPlaylist: MVideoPlaylistFull, t: Transaction) {
+async function sendUpdateVideoPlaylist (videoPlaylist: MVideoPlaylistFull, transaction: Transaction) {
   if (videoPlaylist.privacy === VideoPlaylistPrivacy.PRIVATE) return undefined
 
   const byActor = videoPlaylist.OwnerAccount.Actor
@@ -101,7 +113,7 @@ async function sendUpdateVideoPlaylist (videoPlaylist: MVideoPlaylistFull, t: Tr
 
   const url = getUpdateActivityPubUrl(videoPlaylist.url, videoPlaylist.updatedAt.toISOString())
 
-  const object = await videoPlaylist.toActivityPubObject(null, t)
+  const object = await videoPlaylist.toActivityPubObject(null, transaction)
   const audience = getAudience(byActor, videoPlaylist.privacy === VideoPlaylistPrivacy.PUBLIC)
 
   const updateActivity = buildUpdateActivity(url, byActor, object, audience)
@@ -111,7 +123,13 @@ async function sendUpdateVideoPlaylist (videoPlaylist: MVideoPlaylistFull, t: Tr
 
   if (videoPlaylist.VideoChannel) toFollowersOf.push(videoPlaylist.VideoChannel.Actor)
 
-  return broadcastToFollowers(updateActivity, byActor, toFollowersOf, t)
+  return broadcastToFollowers({
+    data: updateActivity,
+    byActor,
+    toFollowersOf,
+    transaction,
+    contextType: 'Playlist'
+  })
 }
 
 // ---------------------------------------------------------------------------
index 9e8f12fa89c8c199dd502d73d0b4363581d722c5..dbcde91eedcaab23520a02ebc47cac965e97af9b 100644 (file)
@@ -1,7 +1,7 @@
 import { Transaction } from 'sequelize'
 import { ActorFollowHealthCache } from '@server/lib/actor-follow-health-cache'
 import { getServerActor } from '@server/models/application/application'
-import { Activity, ActivityAudience } from '@shared/models'
+import { Activity, ActivityAudience, ActivitypubHttpBroadcastPayload } from '@shared/models'
 import { ContextType } from '@shared/models/activitypub/context'
 import { afterCommitIfTransaction } from '../../../../helpers/database-utils'
 import { logger } from '../../../../helpers/logger'
@@ -14,8 +14,8 @@ import { getActorsInvolvedInVideo, getAudienceFromFollowersOf, getOriginVideoAud
 async function sendVideoRelatedActivity (activityBuilder: (audience: ActivityAudience) => Activity, options: {
   byActor: MActorLight
   video: MVideoImmutable | MVideoAccountLight
+  contextType: ContextType
   transaction?: Transaction
-  contextType?: ContextType
 }) {
   const { byActor, video, transaction, contextType } = options
 
@@ -32,15 +32,23 @@ async function sendVideoRelatedActivity (activityBuilder: (audience: ActivityAud
 
   const actorsException = [ byActor ]
 
-  return broadcastToFollowers(activity, byActor, actorsInvolvedInVideo, transaction, actorsException, contextType)
+  return broadcastToFollowers({
+    data: activity,
+    byActor,
+    toFollowersOf: actorsInvolvedInVideo,
+    transaction,
+    actorsException,
+    contextType
+  })
 }
 
 async function sendVideoActivityToOrigin (activityBuilder: (audience: ActivityAudience) => Activity, options: {
   byActor: MActorLight
   video: MVideoImmutable | MVideoAccountLight
+  contextType: ContextType
+
   actorsInvolvedInVideo?: MActorLight[]
   transaction?: Transaction
-  contextType?: ContextType
 }) {
   const { byActor, video, actorsInvolvedInVideo, transaction, contextType } = options
 
@@ -53,7 +61,12 @@ async function sendVideoActivityToOrigin (activityBuilder: (audience: ActivityAu
   const activity = activityBuilder(audience)
 
   return afterCommitIfTransaction(transaction, () => {
-    return unicastTo(activity, byActor, accountActor.getSharedInbox(), contextType)
+    return unicastTo({
+      data: activity,
+      byActor,
+      toActorUrl: accountActor.getSharedInbox(),
+      contextType
+    })
   })
 }
 
@@ -100,41 +113,69 @@ async function forwardActivity (
 
   logger.debug('Creating forwarding job.', { uris })
 
-  const payload = {
+  const payload: ActivitypubHttpBroadcastPayload = {
     uris,
-    body: activity
+    body: activity,
+    contextType: null
   }
   return afterCommitIfTransaction(t, () => JobQueue.Instance.createJob({ type: 'activitypub-http-broadcast', payload }))
 }
 
 // ---------------------------------------------------------------------------
 
-async function broadcastToFollowers (
-  data: any,
-  byActor: MActorId,
-  toFollowersOf: MActorId[],
-  t: Transaction,
-  actorsException: MActorWithInboxes[] = [],
-  contextType?: ContextType
-) {
-  const uris = await computeFollowerUris(toFollowersOf, actorsException, t)
+async function broadcastToFollowers (options: {
+  data: any
+  byActor: MActorId
+  toFollowersOf: MActorId[]
+  transaction: Transaction
+  contextType: ContextType
+
+  actorsException?: MActorWithInboxes[]
+}) {
+  const { data, byActor, toFollowersOf, transaction, contextType, actorsException = [] } = options
 
-  return afterCommitIfTransaction(t, () => broadcastTo(uris, data, byActor, contextType))
+  const uris = await computeFollowerUris(toFollowersOf, actorsException, transaction)
+
+  return afterCommitIfTransaction(transaction, () => {
+    return broadcastTo({
+      uris,
+      data,
+      byActor,
+      contextType
+    })
+  })
 }
 
-async function broadcastToActors (
-  data: any,
-  byActor: MActorId,
-  toActors: MActor[],
-  t?: Transaction,
-  actorsException: MActorWithInboxes[] = [],
-  contextType?: ContextType
-) {
+async function broadcastToActors (options: {
+  data: any
+  byActor: MActorId
+  toActors: MActor[]
+  transaction: Transaction
+  contextType: ContextType
+  actorsException?: MActorWithInboxes[]
+}) {
+  const { data, byActor, toActors, transaction, contextType, actorsException = [] } = options
+
   const uris = await computeUris(toActors, actorsException)
-  return afterCommitIfTransaction(t, () => broadcastTo(uris, data, byActor, contextType))
+
+  return afterCommitIfTransaction(transaction, () => {
+    return broadcastTo({
+      uris,
+      data,
+      byActor,
+      contextType
+    })
+  })
 }
 
-function broadcastTo (uris: string[], data: any, byActor: MActorId, contextType?: ContextType) {
+function broadcastTo (options: {
+  uris: string[]
+  data: any
+  byActor: MActorId
+  contextType: ContextType
+}) {
+  const { uris, data, byActor, contextType } = options
+
   if (uris.length === 0) return undefined
 
   const broadcastUris: string[] = []
@@ -174,7 +215,14 @@ function broadcastTo (uris: string[], data: any, byActor: MActorId, contextType?
   }
 }
 
-function unicastTo (data: any, byActor: MActorId, toActorUrl: string, contextType?: ContextType) {
+function unicastTo (options: {
+  data: any
+  byActor: MActorId
+  toActorUrl: string
+  contextType: ContextType
+}) {
+  const { data, byActor, toActorUrl, contextType } = options
+
   logger.debug('Creating unicast job.', { uri: toActorUrl })
 
   const payload = {
index fbf01d2768f92ccd2ce2e6d2754d448fbecc9203..709e8501f1f603d664b38e0ff9d20ff740b8f29c 100644 (file)
@@ -1,11 +1,11 @@
 import { map } from 'bluebird'
 import { Job } from 'bull'
+import { buildGlobalHeaders, buildSignedRequestOptions, computeBody } from '@server/lib/activitypub/send'
 import { ActorFollowHealthCache } from '@server/lib/actor-follow-health-cache'
 import { ActivitypubHttpBroadcastPayload } from '@shared/models'
 import { logger } from '../../../helpers/logger'
 import { doRequest } from '../../../helpers/requests'
 import { BROADCAST_CONCURRENCY } from '../../../initializers/constants'
-import { buildGlobalHeaders, buildSignedRequestOptions, computeBody } from './utils/activitypub-http-utils'
 
 async function processActivityPubHttpBroadcast (job: Job) {
   logger.info('Processing ActivityPub broadcast in job %d.', job.id)
index 673583d2b05d6c58626f9dcc1c2c343ede81d680..99bcd3e8d5e11ab31281cc3973e59b1ca2b5d804 100644 (file)
@@ -1,9 +1,9 @@
 import { Job } from 'bull'
+import { buildGlobalHeaders, buildSignedRequestOptions, computeBody } from '@server/lib/activitypub/send'
 import { ActivitypubHttpUnicastPayload } from '@shared/models'
 import { logger } from '../../../helpers/logger'
 import { doRequest } from '../../../helpers/requests'
 import { ActorFollowHealthCache } from '../../actor-follow-health-cache'
-import { buildGlobalHeaders, buildSignedRequestOptions, computeBody } from './utils/activitypub-http-utils'
 
 async function processActivityPubHttpUnicast (job: Job) {
   logger.info('Processing ActivityPub unicast in job %d.', job.id)
index fad2070ea345d91027f55843b583a65a540535d6..93145b8aeb0db6ab020c053b49ea53634db03da8 100644 (file)
@@ -605,7 +605,7 @@ export class ActorModel extends Model<Partial<AttributesOnly<ActorModel>>> {
       image
     }
 
-    return activityPubContextify(json)
+    return activityPubContextify(json, 'Actor')
   }
 
   getFollowerSharedInboxUrls (t: Transaction) {
index e516cf49e8ce03c1f21c5b849cf8e646c0f0b468..bc1de35d1f7f5da98e9e28e8f1bb2c01d91eb8f8 100644 (file)
@@ -3,10 +3,10 @@
 import 'mocha'
 import { expect } from 'chai'
 import { cloneDeep } from 'lodash'
+import { signAndContextify } from '@server/lib/activitypub/send'
 import { buildRequestStub } from '@server/tests/shared'
 import { buildAbsoluteFixturePath } from '@shared/core-utils'
 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 () {
@@ -46,7 +46,7 @@ describe('Test activity pub helpers', function () {
       const body = require(buildAbsoluteFixturePath('./ap-json/peertube/announce-without-context.json'))
 
       const actorSignature = { url: 'http://localhost:9002/accounts/peertube', privateKey: keys.privateKey }
-      const signedBody = await buildSignedActivity(actorSignature as any, body)
+      const signedBody = await signAndContextify(actorSignature as any, body, 'Announce')
 
       const fromActor = { publicKey: keys.publicKey, url: 'http://localhost:9002/accounts/peertube' }
       const result = await isJsonLDSignatureVerified(fromActor as any, signedBody)
@@ -59,7 +59,7 @@ describe('Test activity pub helpers', function () {
       const body = require(buildAbsoluteFixturePath('./ap-json/peertube/announce-without-context.json'))
 
       const actorSignature = { url: 'http://localhost:9002/accounts/peertube', privateKey: keys.privateKey }
-      const signedBody = await buildSignedActivity(actorSignature as any, body)
+      const signedBody = await signAndContextify(actorSignature as any, body, 'Announce')
 
       const fromActor = { publicKey: keys.publicKey, url: 'http://localhost:9002/accounts/peertube' }
       const result = await isJsonLDSignatureVerified(fromActor as any, signedBody)
index da9880d7d3237e96461bc137888f5b57c061396f..a070517b8c34d328a311c20251f17fa39671cbc4 100644 (file)
@@ -4,9 +4,8 @@ import 'mocha'
 import * as chai from 'chai'
 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 { buildGlobalHeaders, signAndContextify } from '@server/lib/activitypub/send'
 import { makeFollowRequest, makePOSTAPRequest } from '@server/tests/shared'
 import { buildAbsoluteFixturePath, wait } from '@shared/core-utils'
 import { HttpStatusCode } from '@shared/models'
@@ -81,7 +80,7 @@ describe('Test ActivityPub security', function () {
   describe('When checking HTTP signature', function () {
 
     it('Should fail with an invalid digest', async function () {
-      const body = activityPubContextify(getAnnounceWithoutContext(servers[1]))
+      const body = activityPubContextify(getAnnounceWithoutContext(servers[1]), 'Announce')
       const headers = {
         Digest: buildDigest({ hello: 'coucou' })
       }
@@ -95,7 +94,7 @@ describe('Test ActivityPub security', function () {
     })
 
     it('Should fail with an invalid date', async function () {
-      const body = activityPubContextify(getAnnounceWithoutContext(servers[1]))
+      const body = activityPubContextify(getAnnounceWithoutContext(servers[1]), 'Announce')
       const headers = buildGlobalHeaders(body)
       headers['date'] = 'Wed, 21 Oct 2015 07:28:00 GMT'
 
@@ -111,7 +110,7 @@ describe('Test ActivityPub security', function () {
       await setKeysOfServer(servers[0], servers[1], invalidKeys.publicKey, invalidKeys.privateKey)
       await setKeysOfServer(servers[1], servers[1], invalidKeys.publicKey, invalidKeys.privateKey)
 
-      const body = activityPubContextify(getAnnounceWithoutContext(servers[1]))
+      const body = activityPubContextify(getAnnounceWithoutContext(servers[1]), 'Announce')
       const headers = buildGlobalHeaders(body)
 
       try {
@@ -126,7 +125,7 @@ describe('Test ActivityPub security', function () {
       await setKeysOfServer(servers[0], servers[1], keys.publicKey, keys.privateKey)
       await setKeysOfServer(servers[1], servers[1], keys.publicKey, keys.privateKey)
 
-      const body = activityPubContextify(getAnnounceWithoutContext(servers[1]))
+      const body = activityPubContextify(getAnnounceWithoutContext(servers[1]), 'Announce')
       const headers = buildGlobalHeaders(body)
 
       const signatureOptions = baseHttpSignature()
@@ -149,7 +148,7 @@ describe('Test ActivityPub security', function () {
     })
 
     it('Should succeed with a valid HTTP signature', async function () {
-      const body = activityPubContextify(getAnnounceWithoutContext(servers[1]))
+      const body = activityPubContextify(getAnnounceWithoutContext(servers[1]), 'Announce')
       const headers = buildGlobalHeaders(body)
 
       const { statusCode } = await makePOSTAPRequest(url, body, baseHttpSignature(), headers)
@@ -168,7 +167,7 @@ describe('Test ActivityPub security', function () {
       await killallServers([ servers[1] ])
       await servers[1].run()
 
-      const body = activityPubContextify(getAnnounceWithoutContext(servers[1]))
+      const body = activityPubContextify(getAnnounceWithoutContext(servers[1]), 'Announce')
       const headers = buildGlobalHeaders(body)
 
       try {
@@ -204,7 +203,7 @@ describe('Test ActivityPub security', function () {
       body.actor = 'http://localhost:' + servers[2].port + '/accounts/peertube'
 
       const signer: any = { privateKey: invalidKeys.privateKey, url: 'http://localhost:' + servers[2].port + '/accounts/peertube' }
-      const signedBody = await buildSignedActivity(signer, body)
+      const signedBody = await signAndContextify(signer, body, 'Announce')
 
       const headers = buildGlobalHeaders(signedBody)
 
@@ -226,7 +225,7 @@ describe('Test ActivityPub security', function () {
       body.actor = 'http://localhost:' + servers[2].port + '/accounts/peertube'
 
       const signer: any = { privateKey: keys.privateKey, url: 'http://localhost:' + servers[2].port + '/accounts/peertube' }
-      const signedBody = await buildSignedActivity(signer, body)
+      const signedBody = await signAndContextify(signer, body, 'Announce')
 
       signedBody.actor = 'http://localhost:' + servers[2].port + '/account/peertube'
 
@@ -247,7 +246,7 @@ describe('Test ActivityPub security', function () {
       body.actor = 'http://localhost:' + servers[2].port + '/accounts/peertube'
 
       const signer: any = { privateKey: keys.privateKey, url: 'http://localhost:' + servers[2].port + '/accounts/peertube' }
-      const signedBody = await buildSignedActivity(signer, body)
+      const signedBody = await signAndContextify(signer, body, 'Announce')
 
       const headers = buildGlobalHeaders(signedBody)
 
@@ -269,7 +268,7 @@ describe('Test ActivityPub security', function () {
       body.actor = 'http://localhost:' + servers[2].port + '/accounts/peertube'
 
       const signer: any = { privateKey: keys.privateKey, url: 'http://localhost:' + servers[2].port + '/accounts/peertube' }
-      const signedBody = await buildSignedActivity(signer, body)
+      const signedBody = await signAndContextify(signer, body, 'Announce')
 
       const headers = buildGlobalHeaders(signedBody)
 
index d7aedf82f22b092c143af733d65fa000e8b116e4..57120cacacbaf84a785c513e8f95c2ed24238a57 100644 (file)
@@ -22,7 +22,7 @@ export async function makeFollowRequest (to: { url: string }, by: { url: string,
     object: to.url
   }
 
-  const body = activityPubContextify(follow)
+  const body = activityPubContextify(follow, 'Follow')
 
   const httpSignature = {
     algorithm: HTTP_SIGNATURE.ALGORITHM,
index bd795a2fde7864824fb11f438fc403c2bcd61bec..4ada3b083eab9858abf61faa344441dd903a27d7 100644 (file)
@@ -1 +1,15 @@
-export type ContextType = 'All' | 'View' | 'Announce' | 'CacheFile'
+export type ContextType =
+  'Video' |
+  'Comment' |
+  'Playlist' |
+  'Follow' |
+  'Reject' |
+  'Accept' |
+  'View' |
+  'Announce' |
+  'CacheFile' |
+  'Delete' |
+  'Rate' |
+  'Flag' |
+  'Actor' |
+  'Collection'
index 43d7f7f7480eab9e150a0dff3a17686505389f56..9bf9943799a36494e839da61ad5b2088bb2fd75e 100644 (file)
@@ -46,7 +46,7 @@ export type ActivityTrackerUrlObject = {
   href: string
 }
 
-export type ActivityPlaylistInfohashesObject = {
+export type ActivityStreamingPlaylistInfohashesObject = {
   type: 'Infohash'
   name: string
 }
@@ -97,7 +97,7 @@ export interface ActivityFlagReasonObject {
 
 export type ActivityTagObject =
   ActivityPlaylistSegmentHashesObject
-  | ActivityPlaylistInfohashesObject
+  | ActivityStreamingPlaylistInfohashesObject
   | ActivityVideoUrlObject
   | ActivityHashTagObject
   | ActivityMentionObject
index 91469d010a6a260015cb0ae28cb32a7ea6657d33..92d1b56982db9137bd254ebdd9761feb9926b62f 100644 (file)
@@ -40,9 +40,9 @@ export interface Job {
 
 export type ActivitypubHttpBroadcastPayload = {
   uris: string[]
-  signatureActorId?: number
+  contextType: ContextType
   body: any
-  contextType?: ContextType
+  signatureActorId?: number
 }
 
 export type ActivitypubFollowPayload = {
@@ -62,9 +62,9 @@ export type ActivitypubHttpFetcherPayload = {
 
 export type ActivitypubHttpUnicastPayload = {
   uri: string
+  contextType: ContextType
   signatureActorId?: number
   body: object
-  contextType?: ContextType
 }
 
 export type RefreshPayload = {