Split files in activitypub server
authorChocobozzz <me@florianbigard.com>
Fri, 25 May 2018 09:32:36 +0000 (11:32 +0200)
committerChocobozzz <me@florianbigard.com>
Fri, 25 May 2018 09:33:04 +0000 (11:33 +0200)
20 files changed:
server/controllers/activitypub/client.ts
server/controllers/activitypub/outbox.ts
server/lib/activitypub/audience.ts [new file with mode: 0644]
server/lib/activitypub/index.ts
server/lib/activitypub/process/process-announce.ts
server/lib/activitypub/process/process-create.ts
server/lib/activitypub/process/process-delete.ts
server/lib/activitypub/process/process-like.ts
server/lib/activitypub/process/process-undo.ts
server/lib/activitypub/process/process-update.ts
server/lib/activitypub/send/misc.ts [deleted file]
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-follow.ts
server/lib/activitypub/send/send-like.ts
server/lib/activitypub/send/send-undo.ts
server/lib/activitypub/send/send-update.ts
server/lib/activitypub/send/utils.ts [new file with mode: 0644]

index 5199b3f8107990352e4710bfff65db49b79c3e8a..767fde5d943db7b6d920c311903edfdaa00c7976 100644 (file)
@@ -5,7 +5,7 @@ import { activityPubCollectionPagination, activityPubContextify } from '../../he
 import { pageToStartAndCount } from '../../helpers/core-utils'
 import { ACTIVITY_PUB, CONFIG, ROUTE_CACHE_LIFETIME } from '../../initializers'
 import { buildVideoAnnounce } from '../../lib/activitypub/send'
-import { audiencify, getAudience } from '../../lib/activitypub/send/misc'
+import { audiencify, getAudience } from '../../lib/activitypub/audience'
 import { createActivityData } from '../../lib/activitypub/send/send-create'
 import { asyncMiddleware, executeIfActivityPub, localAccountValidator } from '../../middlewares'
 import { videoChannelsGetValidator, videosGetValidator, videosShareValidator } from '../../middlewares/validators'
index 0077e1d0b0e6429a2a8114606421babefd5b5995..c9e087a136472045ac56b3f323cf5c354268b417 100644 (file)
@@ -6,7 +6,7 @@ import { pageToStartAndCount } from '../../helpers/core-utils'
 import { logger } from '../../helpers/logger'
 import { ACTIVITY_PUB } from '../../initializers/constants'
 import { announceActivityData, createActivityData } from '../../lib/activitypub/send'
-import { buildAudience } from '../../lib/activitypub/send/misc'
+import { buildAudience } from '../../lib/activitypub/audience'
 import { asyncMiddleware, localAccountValidator } from '../../middlewares'
 import { AccountModel } from '../../models/account/account'
 import { ActorModel } from '../../models/activitypub/actor'
diff --git a/server/lib/activitypub/audience.ts b/server/lib/activitypub/audience.ts
new file mode 100644 (file)
index 0000000..916358f
--- /dev/null
@@ -0,0 +1,92 @@
+import { Transaction } from 'sequelize'
+import { ActivityAudience } from '../../../shared/models/activitypub'
+import { ACTIVITY_PUB } from '../../initializers'
+import { ActorModel } from '../../models/activitypub/actor'
+import { VideoModel } from '../../models/video/video'
+import { VideoCommentModel } from '../../models/video/video-comment'
+import { VideoShareModel } from '../../models/video/video-share'
+
+function getVideoAudience (video: VideoModel, actorsInvolvedInVideo: ActorModel[]) {
+  return {
+    to: [ video.VideoChannel.Account.Actor.url ],
+    cc: actorsInvolvedInVideo.map(a => a.followersUrl)
+  }
+}
+
+function getVideoCommentAudience (
+  videoComment: VideoCommentModel,
+  threadParentComments: VideoCommentModel[],
+  actorsInvolvedInVideo: ActorModel[],
+  isOrigin = false
+) {
+  const to = [ ACTIVITY_PUB.PUBLIC ]
+  const cc = [ ]
+
+  // Owner of the video we comment
+  if (isOrigin === false) {
+    cc.push(videoComment.Video.VideoChannel.Account.Actor.url)
+  }
+
+  // Followers of the poster
+  cc.push(videoComment.Account.Actor.followersUrl)
+
+  // Send to actors we reply to
+  for (const parentComment of threadParentComments) {
+    cc.push(parentComment.Account.Actor.url)
+  }
+
+  return {
+    to,
+    cc: cc.concat(actorsInvolvedInVideo.map(a => a.followersUrl))
+  }
+}
+
+function getObjectFollowersAudience (actorsInvolvedInObject: ActorModel[]) {
+  return {
+    to: [ ACTIVITY_PUB.PUBLIC ].concat(actorsInvolvedInObject.map(a => a.followersUrl)),
+    cc: []
+  }
+}
+
+async function getActorsInvolvedInVideo (video: VideoModel, t: Transaction) {
+  const actors = await VideoShareModel.loadActorsByShare(video.id, t)
+  actors.push(video.VideoChannel.Account.Actor)
+
+  return actors
+}
+
+async function getAudience (actorSender: ActorModel, t: Transaction, isPublic = true) {
+  return buildAudience([ actorSender.followersUrl ], isPublic)
+}
+
+function buildAudience (followerInboxUrls: string[], isPublic = true) {
+  // Thanks Mastodon: https://github.com/tootsuite/mastodon/blob/master/app/lib/activitypub/tag_manager.rb#L47
+  let to = []
+  let cc = []
+
+  if (isPublic) {
+    to = [ ACTIVITY_PUB.PUBLIC ]
+    cc = followerInboxUrls
+  } else { // Unlisted
+    to = [ ]
+    cc = [ ]
+  }
+
+  return { to, cc }
+}
+
+function audiencify <T> (object: T, audience: ActivityAudience) {
+  return Object.assign(object, audience)
+}
+
+// ---------------------------------------------------------------------------
+
+export {
+  buildAudience,
+  getAudience,
+  getVideoAudience,
+  getActorsInvolvedInVideo,
+  getObjectFollowersAudience,
+  audiencify,
+  getVideoCommentAudience
+}
index 88064c6b6d7fd0b40154805e0dab4485b0de158f..6906bf9d3d86302277d506738a7a0cf8f8f8be19 100644 (file)
@@ -1,7 +1,6 @@
 export * from './process'
 export * from './send'
 export * from './actor'
-export * from './fetch'
 export * from './share'
 export * from './videos'
 export * from './video-comments'
index 09f2e80f34fab1ba53c902f454e5774458c006b6..a6e1e2d470506237d9a0156333a63191b96a2de4 100644 (file)
@@ -5,7 +5,7 @@ import { ActorModel } from '../../../models/activitypub/actor'
 import { VideoModel } from '../../../models/video/video'
 import { VideoShareModel } from '../../../models/video/video-share'
 import { getOrCreateActorAndServerAndModel } from '../actor'
-import { forwardActivity } from '../send/misc'
+import { forwardActivity } from '../send/utils'
 import { getOrCreateAccountAndVideoAndChannel } from '../videos'
 
 async function processAnnounceActivity (activity: ActivityAnnounce) {
index ee180b765d8338e584a0d684bf583250014e7244..99a5f6f9c0b73b40a2d5d948f011357e0b732aa4 100644 (file)
@@ -9,9 +9,10 @@ import { ActorModel } from '../../../models/activitypub/actor'
 import { VideoAbuseModel } from '../../../models/video/video-abuse'
 import { VideoCommentModel } from '../../../models/video/video-comment'
 import { getOrCreateActorAndServerAndModel } from '../actor'
-import { forwardActivity, getActorsInvolvedInVideo } from '../send/misc'
+import { getActorsInvolvedInVideo } from '../audience'
 import { resolveThread } from '../video-comments'
 import { getOrCreateAccountAndVideoAndChannel } from '../videos'
+import { forwardActivity } from '../send/utils'
 
 async function processCreateActivity (activity: ActivityCreate) {
   const activityObject = activity.object
index b58f6c04a28a686f1fddb4ab365d16750e48cc6f..8310b70f08694cdc59343762ed991388e6d55751 100644 (file)
@@ -8,7 +8,7 @@ import { VideoModel } from '../../../models/video/video'
 import { VideoChannelModel } from '../../../models/video/video-channel'
 import { VideoCommentModel } from '../../../models/video/video-comment'
 import { getOrCreateActorAndServerAndModel } from '../actor'
-import { forwardActivity } from '../send/misc'
+import { forwardActivity } from '../send/utils'
 
 async function processDeleteActivity (activity: ActivityDelete) {
   const objectUrl = typeof activity.object === 'string' ? activity.object : activity.object.id
index 0d161b126d1389884b48cae346da36a4f5e9fee8..d219e76eb23d726f2bef9bcb42658cfb0ce7ec11 100644 (file)
@@ -4,7 +4,7 @@ import { sequelizeTypescript } from '../../../initializers'
 import { AccountVideoRateModel } from '../../../models/account/account-video-rate'
 import { ActorModel } from '../../../models/activitypub/actor'
 import { getOrCreateActorAndServerAndModel } from '../actor'
-import { forwardActivity } from '../send/misc'
+import { forwardActivity } from '../send/utils'
 import { getOrCreateAccountAndVideoAndChannel } from '../videos'
 
 async function processLikeActivity (activity: ActivityLike) {
index 9b024d15f5c2cd802eb463ebb3a9f1d731ef0a8a..d023f7029e2cc5c1f39758eb36fa33c94d28e704 100644 (file)
@@ -8,7 +8,7 @@ import { AccountModel } from '../../../models/account/account'
 import { AccountVideoRateModel } from '../../../models/account/account-video-rate'
 import { ActorModel } from '../../../models/activitypub/actor'
 import { ActorFollowModel } from '../../../models/activitypub/actor-follow'
-import { forwardActivity } from '../send/misc'
+import { forwardActivity } from '../send/utils'
 import { getOrCreateAccountAndVideoAndChannel } from '../videos'
 import { VideoShareModel } from '../../../models/video/video-share'
 
index 0dd657c2b2d26fee8478d7d4a839dc6fcca12438..2750f48c3692eeb75195fd9a7bf2359a9a2a6b7f 100644 (file)
@@ -14,7 +14,8 @@ import { VideoFileModel } from '../../../models/video/video-file'
 import { fetchAvatarIfExists, getOrCreateActorAndServerAndModel, updateActorAvatarInstance, updateActorInstance } from '../actor'
 import {
   generateThumbnailFromUrl,
-  getOrCreateAccountAndVideoAndChannel, getOrCreateVideoChannel,
+  getOrCreateAccountAndVideoAndChannel,
+  getOrCreateVideoChannel,
   videoActivityObjectToDBAttributes,
   videoFileActivityUrlToDBAttributes
 } from '../videos'
diff --git a/server/lib/activitypub/send/misc.ts b/server/lib/activitypub/send/misc.ts
deleted file mode 100644 (file)
index 646aa9f..0000000
+++ /dev/null
@@ -1,196 +0,0 @@
-import { Transaction } from 'sequelize'
-import { Activity, ActivityAudience } from '../../../../shared/models/activitypub'
-import { logger } from '../../../helpers/logger'
-import { ACTIVITY_PUB } from '../../../initializers'
-import { ActorModel } from '../../../models/activitypub/actor'
-import { ActorFollowModel } from '../../../models/activitypub/actor-follow'
-import { VideoModel } from '../../../models/video/video'
-import { VideoCommentModel } from '../../../models/video/video-comment'
-import { VideoShareModel } from '../../../models/video/video-share'
-import { JobQueue } from '../../job-queue'
-
-async function forwardActivity (
-  activity: Activity,
-  t: Transaction,
-  followersException: ActorModel[] = [],
-  additionalFollowerUrls: string[] = []
-) {
-  const to = activity.to || []
-  const cc = activity.cc || []
-
-  const followersUrls = additionalFollowerUrls
-  for (const dest of to.concat(cc)) {
-    if (dest.endsWith('/followers')) {
-      followersUrls.push(dest)
-    }
-  }
-
-  const toActorFollowers = await ActorModel.listByFollowersUrls(followersUrls, t)
-  const uris = await computeFollowerUris(toActorFollowers, followersException, t)
-
-  if (uris.length === 0) {
-    logger.info('0 followers for %s, no forwarding.', toActorFollowers.map(a => a.id).join(', '))
-    return undefined
-  }
-
-  logger.debug('Creating forwarding job.', { uris })
-
-  const payload = {
-    uris,
-    body: activity
-  }
-  return JobQueue.Instance.createJob({ type: 'activitypub-http-broadcast', payload })
-}
-
-async function broadcastToFollowers (
-  data: any,
-  byActor: ActorModel,
-  toActorFollowers: ActorModel[],
-  t: Transaction,
-  actorsException: ActorModel[] = []
-) {
-  const uris = await computeFollowerUris(toActorFollowers, actorsException, t)
-  return broadcastTo(uris, data, byActor)
-}
-
-async function broadcastToActors (
-  data: any,
-  byActor: ActorModel,
-  toActors: ActorModel[],
-  actorsException: ActorModel[] = []
-) {
-  const uris = await computeUris(toActors, actorsException)
-  return broadcastTo(uris, data, byActor)
-}
-
-async function broadcastTo (uris: string[], data: any, byActor: ActorModel) {
-  if (uris.length === 0) return undefined
-
-  logger.debug('Creating broadcast job.', { uris })
-
-  const payload = {
-    uris,
-    signatureActorId: byActor.id,
-    body: data
-  }
-
-  return JobQueue.Instance.createJob({ type: 'activitypub-http-broadcast', payload })
-}
-
-async function unicastTo (data: any, byActor: ActorModel, toActorUrl: string) {
-  logger.debug('Creating unicast job.', { uri: toActorUrl })
-
-  const payload = {
-    uri: toActorUrl,
-    signatureActorId: byActor.id,
-    body: data
-  }
-
-  return JobQueue.Instance.createJob({ type: 'activitypub-http-unicast', payload })
-}
-
-function getOriginVideoAudience (video: VideoModel, actorsInvolvedInVideo: ActorModel[]) {
-  return {
-    to: [ video.VideoChannel.Account.Actor.url ],
-    cc: actorsInvolvedInVideo.map(a => a.followersUrl)
-  }
-}
-
-function getVideoCommentAudience (
-  videoComment: VideoCommentModel,
-  threadParentComments: VideoCommentModel[],
-  actorsInvolvedInVideo: ActorModel[],
-  isOrigin = false
-) {
-  const to = [ ACTIVITY_PUB.PUBLIC ]
-  const cc = [ ]
-
-  // Owner of the video we comment
-  if (isOrigin === false) {
-    cc.push(videoComment.Video.VideoChannel.Account.Actor.url)
-  }
-
-  // Followers of the poster
-  cc.push(videoComment.Account.Actor.followersUrl)
-
-  // Send to actors we reply to
-  for (const parentComment of threadParentComments) {
-    cc.push(parentComment.Account.Actor.url)
-  }
-
-  return {
-    to,
-    cc: cc.concat(actorsInvolvedInVideo.map(a => a.followersUrl))
-  }
-}
-
-function getObjectFollowersAudience (actorsInvolvedInObject: ActorModel[]) {
-  return {
-    to: [ ACTIVITY_PUB.PUBLIC ].concat(actorsInvolvedInObject.map(a => a.followersUrl)),
-    cc: []
-  }
-}
-
-async function getActorsInvolvedInVideo (video: VideoModel, t: Transaction) {
-  const actors = await VideoShareModel.loadActorsByShare(video.id, t)
-  actors.push(video.VideoChannel.Account.Actor)
-
-  return actors
-}
-
-async function getAudience (actorSender: ActorModel, t: Transaction, isPublic = true) {
-  return buildAudience([ actorSender.followersUrl ], isPublic)
-}
-
-function buildAudience (followerInboxUrls: string[], isPublic = true) {
-  // Thanks Mastodon: https://github.com/tootsuite/mastodon/blob/master/app/lib/activitypub/tag_manager.rb#L47
-  let to = []
-  let cc = []
-
-  if (isPublic) {
-    to = [ ACTIVITY_PUB.PUBLIC ]
-    cc = followerInboxUrls
-  } else { // Unlisted
-    to = [ ]
-    cc = [ ]
-  }
-
-  return { to, cc }
-}
-
-function audiencify <T> (object: T, audience: ActivityAudience) {
-  return Object.assign(object, audience)
-}
-
-async function computeFollowerUris (toActorFollower: ActorModel[], actorsException: ActorModel[], t: Transaction) {
-  const toActorFollowerIds = toActorFollower.map(a => a.id)
-
-  const result = await ActorFollowModel.listAcceptedFollowerSharedInboxUrls(toActorFollowerIds, t)
-  const sharedInboxesException = actorsException.map(f => f.sharedInboxUrl || f.inboxUrl)
-  return result.data.filter(sharedInbox => sharedInboxesException.indexOf(sharedInbox) === -1)
-}
-
-async function computeUris (toActors: ActorModel[], actorsException: ActorModel[] = []) {
-  const toActorSharedInboxesSet = new Set(toActors.map(a => a.sharedInboxUrl || a.inboxUrl))
-
-  const sharedInboxesException = actorsException.map(f => f.sharedInboxUrl || f.inboxUrl)
-  return Array.from(toActorSharedInboxesSet)
-    .filter(sharedInbox => sharedInboxesException.indexOf(sharedInbox) === -1)
-}
-
-// ---------------------------------------------------------------------------
-
-export {
-  broadcastToFollowers,
-  unicastTo,
-  buildAudience,
-  getAudience,
-  getOriginVideoAudience,
-  getActorsInvolvedInVideo,
-  getObjectFollowersAudience,
-  forwardActivity,
-  audiencify,
-  getVideoCommentAudience,
-  computeUris,
-  broadcastToActors
-}
index 064fd88d201ee9b0e8a39b495adcee60d278a2c2..44644a22f3d57189e9c35b36c9cb750f345c2006 100644 (file)
@@ -2,7 +2,7 @@ import { ActivityAccept, ActivityFollow } from '../../../../shared/models/activi
 import { ActorModel } from '../../../models/activitypub/actor'
 import { ActorFollowModel } from '../../../models/activitypub/actor-follow'
 import { getActorFollowAcceptActivityPubUrl, getActorFollowActivityPubUrl } from '../url'
-import { unicastTo } from './misc'
+import { unicastTo } from './utils'
 import { followActivityData } from './send-follow'
 
 async function sendAccept (actorFollow: ActorFollowModel) {
index 4179c9d43ee4c4c1a42468adf6e0ca250b587ec1..fa1d47259a958af993b3bdae32eb3e0228e42cc8 100644 (file)
@@ -3,7 +3,8 @@ import { ActivityAnnounce, ActivityAudience } from '../../../../shared/models/ac
 import { ActorModel } from '../../../models/activitypub/actor'
 import { VideoModel } from '../../../models/video/video'
 import { VideoShareModel } from '../../../models/video/video-share'
-import { broadcastToFollowers, getActorsInvolvedInVideo, getAudience, getObjectFollowersAudience } from './misc'
+import { broadcastToFollowers } from './utils'
+import { getActorsInvolvedInVideo, getAudience, getObjectFollowersAudience } from '../audience'
 
 async function buildVideoAnnounce (byActor: ActorModel, videoShare: VideoShareModel, video: VideoModel, t: Transaction) {
   const announcedObject = video.url
index 4ff20b0334ad408aef6249b76c05938f07f1652e..3ef4fcd3b1a02801793731ec9fa1f7c18fc78dbe 100644 (file)
@@ -7,17 +7,15 @@ import { VideoModel } from '../../../models/video/video'
 import { VideoAbuseModel } from '../../../models/video/video-abuse'
 import { VideoCommentModel } from '../../../models/video/video-comment'
 import { getVideoAbuseActivityPubUrl, getVideoDislikeActivityPubUrl, getVideoViewActivityPubUrl } from '../url'
+import { broadcastToActors, broadcastToFollowers, unicastTo } from './utils'
 import {
   audiencify,
-  broadcastToActors,
-  broadcastToFollowers,
   getActorsInvolvedInVideo,
   getAudience,
   getObjectFollowersAudience,
-  getOriginVideoAudience,
-  getVideoCommentAudience,
-  unicastTo
-} from './misc'
+  getVideoAudience,
+  getVideoCommentAudience
+} from '../audience'
 
 async function sendCreateVideo (video: VideoModel, t: Transaction) {
   if (video.privacy === VideoPrivacy.PRIVATE) return undefined
@@ -83,7 +81,7 @@ async function sendCreateView (byActor: ActorModel, video: VideoModel, t: Transa
 
   // Send to origin
   if (video.isOwned() === false) {
-    const audience = getOriginVideoAudience(video, actorsInvolvedInVideo)
+    const audience = getVideoAudience(video, actorsInvolvedInVideo)
     const data = await createActivityData(url, byActor, viewActivityData, t, audience)
 
     return unicastTo(data, byActor, video.VideoChannel.Account.Actor.sharedInboxUrl)
@@ -107,7 +105,7 @@ async function sendCreateDislike (byActor: ActorModel, video: VideoModel, t: Tra
 
   // Send to origin
   if (video.isOwned() === false) {
-    const audience = getOriginVideoAudience(video, actorsInvolvedInVideo)
+    const audience = getVideoAudience(video, actorsInvolvedInVideo)
     const data = await createActivityData(url, byActor, dislikeActivityData, t, audience)
 
     return unicastTo(data, byActor, video.VideoChannel.Account.Actor.sharedInboxUrl)
index bb5d6913ccef10541800a2cefebceeef35cd65c9..e9a8951b5911f214fc90a4263ccbc4a7ca998ed4 100644 (file)
@@ -5,7 +5,8 @@ import { VideoModel } from '../../../models/video/video'
 import { VideoCommentModel } from '../../../models/video/video-comment'
 import { VideoShareModel } from '../../../models/video/video-share'
 import { getDeleteActivityPubUrl } from '../url'
-import { audiencify, broadcastToActors, broadcastToFollowers, getActorsInvolvedInVideo, getVideoCommentAudience, unicastTo } from './misc'
+import { broadcastToActors, broadcastToFollowers, unicastTo } from './utils'
+import { audiencify, getActorsInvolvedInVideo, getVideoCommentAudience } from '../audience'
 
 async function sendDeleteVideo (video: VideoModel, t: Transaction) {
   const url = getDeleteActivityPubUrl(video.url)
index 4e9865af48dd782a9ec9b808ec1c7a6daa348d6a..f81d9a1942ed2c57ccef3d84103a03407bad8982 100644 (file)
@@ -2,7 +2,7 @@ import { ActivityFollow } from '../../../../shared/models/activitypub'
 import { ActorModel } from '../../../models/activitypub/actor'
 import { ActorFollowModel } from '../../../models/activitypub/actor-follow'
 import { getActorFollowActivityPubUrl } from '../url'
-import { unicastTo } from './misc'
+import { unicastTo } from './utils'
 
 function sendFollow (actorFollow: ActorFollowModel) {
   const me = actorFollow.ActorFollower
index fb2b4aaf87132f356af319c6930a4887b2da0cf3..ddeb1fcd2e375bdc05fea4ebdaed0c6a3a3551cb 100644 (file)
@@ -3,15 +3,8 @@ import { ActivityAudience, ActivityLike } from '../../../../shared/models/activi
 import { ActorModel } from '../../../models/activitypub/actor'
 import { VideoModel } from '../../../models/video/video'
 import { getVideoLikeActivityPubUrl } from '../url'
-import {
-  audiencify,
-  broadcastToFollowers,
-  getActorsInvolvedInVideo,
-  getAudience,
-  getObjectFollowersAudience,
-  getOriginVideoAudience,
-  unicastTo
-} from './misc'
+import { broadcastToFollowers, unicastTo } from './utils'
+import { audiencify, getActorsInvolvedInVideo, getAudience, getObjectFollowersAudience, getVideoAudience } from '../audience'
 
 async function sendLike (byActor: ActorModel, video: VideoModel, t: Transaction) {
   const url = getVideoLikeActivityPubUrl(byActor, video)
@@ -20,7 +13,7 @@ async function sendLike (byActor: ActorModel, video: VideoModel, t: Transaction)
 
   // Send to origin
   if (video.isOwned() === false) {
-    const audience = getOriginVideoAudience(video, accountsInvolvedInVideo)
+    const audience = getVideoAudience(video, accountsInvolvedInVideo)
     const data = await likeActivityData(url, byActor, video, t, audience)
 
     return unicastTo(data, byActor, video.VideoChannel.Account.Actor.sharedInboxUrl)
index adee2192fad735092810f61778529c9c02b75112..9733e66dcbf449082508c4b9d586e2cfb4efacaf 100644 (file)
@@ -11,15 +11,8 @@ import { ActorModel } from '../../../models/activitypub/actor'
 import { ActorFollowModel } from '../../../models/activitypub/actor-follow'
 import { VideoModel } from '../../../models/video/video'
 import { getActorFollowActivityPubUrl, getUndoActivityPubUrl, getVideoDislikeActivityPubUrl, getVideoLikeActivityPubUrl } from '../url'
-import {
-  audiencify,
-  broadcastToFollowers,
-  getActorsInvolvedInVideo,
-  getAudience,
-  getObjectFollowersAudience,
-  getOriginVideoAudience,
-  unicastTo
-} from './misc'
+import { broadcastToFollowers, unicastTo } from './utils'
+import { audiencify, getActorsInvolvedInVideo, getAudience, getObjectFollowersAudience, getVideoAudience } from '../audience'
 import { createActivityData, createDislikeActivityData } from './send-create'
 import { followActivityData } from './send-follow'
 import { likeActivityData } from './send-like'
@@ -48,7 +41,7 @@ async function sendUndoLike (byActor: ActorModel, video: VideoModel, t: Transact
 
   // Send to origin
   if (video.isOwned() === false) {
-    const audience = getOriginVideoAudience(video, actorsInvolvedInVideo)
+    const audience = getVideoAudience(video, actorsInvolvedInVideo)
     const data = await undoActivityData(undoUrl, byActor, object, t, audience)
 
     return unicastTo(data, byActor, video.VideoChannel.Account.Actor.sharedInboxUrl)
@@ -70,7 +63,7 @@ async function sendUndoDislike (byActor: ActorModel, video: VideoModel, t: Trans
   const object = await createActivityData(dislikeUrl, byActor, dislikeActivity, t)
 
   if (video.isOwned() === false) {
-    const audience = getOriginVideoAudience(video, actorsInvolvedInVideo)
+    const audience = getVideoAudience(video, actorsInvolvedInVideo)
     const data = await undoActivityData(undoUrl, byActor, object, t, audience)
 
     return unicastTo(data, byActor, video.VideoChannel.Account.Actor.sharedInboxUrl)
index e15fecff656cde6d137dee1c12a36a8af4a52cc5..d64b88343b73c4bafc9cb03cd0e754dcc10adc90 100644 (file)
@@ -7,7 +7,8 @@ import { VideoModel } from '../../../models/video/video'
 import { VideoChannelModel } from '../../../models/video/video-channel'
 import { VideoShareModel } from '../../../models/video/video-share'
 import { getUpdateActivityPubUrl } from '../url'
-import { audiencify, broadcastToFollowers, getAudience } from './misc'
+import { broadcastToFollowers } from './utils'
+import { audiencify, getAudience } from '../audience'
 
 async function sendUpdateVideo (video: VideoModel, t: Transaction) {
   const byActor = video.VideoChannel.Account.Actor
diff --git a/server/lib/activitypub/send/utils.ts b/server/lib/activitypub/send/utils.ts
new file mode 100644 (file)
index 0000000..80d4463
--- /dev/null
@@ -0,0 +1,113 @@
+import { Transaction } from 'sequelize'
+import { Activity } from '../../../../shared/models/activitypub'
+import { logger } from '../../../helpers/logger'
+import { ActorModel } from '../../../models/activitypub/actor'
+import { ActorFollowModel } from '../../../models/activitypub/actor-follow'
+import { JobQueue } from '../../job-queue'
+
+async function forwardActivity (
+  activity: Activity,
+  t: Transaction,
+  followersException: ActorModel[] = [],
+  additionalFollowerUrls: string[] = []
+) {
+  const to = activity.to || []
+  const cc = activity.cc || []
+
+  const followersUrls = additionalFollowerUrls
+  for (const dest of to.concat(cc)) {
+    if (dest.endsWith('/followers')) {
+      followersUrls.push(dest)
+    }
+  }
+
+  const toActorFollowers = await ActorModel.listByFollowersUrls(followersUrls, t)
+  const uris = await computeFollowerUris(toActorFollowers, followersException, t)
+
+  if (uris.length === 0) {
+    logger.info('0 followers for %s, no forwarding.', toActorFollowers.map(a => a.id).join(', '))
+    return undefined
+  }
+
+  logger.debug('Creating forwarding job.', { uris })
+
+  const payload = {
+    uris,
+    body: activity
+  }
+  return JobQueue.Instance.createJob({ type: 'activitypub-http-broadcast', payload })
+}
+
+async function broadcastToFollowers (
+  data: any,
+  byActor: ActorModel,
+  toActorFollowers: ActorModel[],
+  t: Transaction,
+  actorsException: ActorModel[] = []
+) {
+  const uris = await computeFollowerUris(toActorFollowers, actorsException, t)
+  return broadcastTo(uris, data, byActor)
+}
+
+async function broadcastToActors (
+  data: any,
+  byActor: ActorModel,
+  toActors: ActorModel[],
+  actorsException: ActorModel[] = []
+) {
+  const uris = await computeUris(toActors, actorsException)
+  return broadcastTo(uris, data, byActor)
+}
+
+async function broadcastTo (uris: string[], data: any, byActor: ActorModel) {
+  if (uris.length === 0) return undefined
+
+  logger.debug('Creating broadcast job.', { uris })
+
+  const payload = {
+    uris,
+    signatureActorId: byActor.id,
+    body: data
+  }
+
+  return JobQueue.Instance.createJob({ type: 'activitypub-http-broadcast', payload })
+}
+
+async function unicastTo (data: any, byActor: ActorModel, toActorUrl: string) {
+  logger.debug('Creating unicast job.', { uri: toActorUrl })
+
+  const payload = {
+    uri: toActorUrl,
+    signatureActorId: byActor.id,
+    body: data
+  }
+
+  return JobQueue.Instance.createJob({ type: 'activitypub-http-unicast', payload })
+}
+
+// ---------------------------------------------------------------------------
+
+export {
+  broadcastToFollowers,
+  unicastTo,
+  forwardActivity,
+  broadcastToActors
+}
+
+// ---------------------------------------------------------------------------
+
+async function computeFollowerUris (toActorFollower: ActorModel[], actorsException: ActorModel[], t: Transaction) {
+  const toActorFollowerIds = toActorFollower.map(a => a.id)
+
+  const result = await ActorFollowModel.listAcceptedFollowerSharedInboxUrls(toActorFollowerIds, t)
+  const sharedInboxesException = actorsException.map(f => f.sharedInboxUrl || f.inboxUrl)
+  return result.data.filter(sharedInbox => sharedInboxesException.indexOf(sharedInbox) === -1)
+}
+
+async function computeUris (toActors: ActorModel[], actorsException: ActorModel[] = []) {
+  const toActorSharedInboxesSet = new Set(toActors.map(a => a.sharedInboxUrl || a.inboxUrl))
+
+  const sharedInboxesException = actorsException.map(f => f.sharedInboxUrl || f.inboxUrl)
+  return Array.from(toActorSharedInboxesSet)
+              .filter(sharedInbox => sharedInboxesException.indexOf(sharedInbox) === -1)
+}