Optimize activity actor load in AP processors
authorChocobozzz <me@florianbigard.com>
Wed, 19 Sep 2018 12:44:20 +0000 (14:44 +0200)
committerChocobozzz <me@florianbigard.com>
Wed, 19 Sep 2018 13:22:55 +0000 (15:22 +0200)
16 files changed:
server/controllers/api/search.ts
server/helpers/actor.ts [new file with mode: 0644]
server/lib/activitypub/actor.ts
server/lib/activitypub/cache-file.ts
server/lib/activitypub/process/process-accept.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-follow.ts
server/lib/activitypub/process/process-like.ts
server/lib/activitypub/process/process-reject.ts
server/lib/activitypub/process/process-undo.ts
server/lib/activitypub/process/process-update.ts
server/lib/activitypub/process/process.ts
server/lib/activitypub/videos.ts
server/models/activitypub/actor.ts

index ea3166f5fdcf1b34247d73e91535ef2489893014..fd4db7a543869f187a445c42ed6760b2985559cb 100644 (file)
@@ -89,7 +89,7 @@ async function searchVideoChannelURI (search: string, isWebfingerSearch: boolean
 
   if (isUserAbleToSearchRemoteURI(res)) {
     try {
-      const actor = await getOrCreateActorAndServerAndModel(uri, true, true)
+      const actor = await getOrCreateActorAndServerAndModel(uri, 'all', true, true)
       videoChannel = actor.VideoChannel
     } catch (err) {
       logger.info('Cannot search remote video channel %s.', uri, { err })
diff --git a/server/helpers/actor.ts b/server/helpers/actor.ts
new file mode 100644 (file)
index 0000000..12a7ace
--- /dev/null
@@ -0,0 +1,13 @@
+import { ActorModel } from '../models/activitypub/actor'
+
+type ActorFetchByUrlType = 'all' | 'actor-and-association-ids'
+function fetchActorByUrl (url: string, fetchType: ActorFetchByUrlType) {
+  if (fetchType === 'all') return ActorModel.loadByUrlAndPopulateAccountAndChannel(url)
+
+  if (fetchType === 'actor-and-association-ids') return ActorModel.loadByUrl(url)
+}
+
+export {
+  ActorFetchByUrlType,
+  fetchActorByUrl
+}
index 3464add0311e2c6410d22f45a9ac1e66a50ac150..0bdb7d12eab16bd1ce40bd005fec9261c11d6925 100644 (file)
@@ -21,6 +21,7 @@ import { ServerModel } from '../../models/server/server'
 import { VideoChannelModel } from '../../models/video/video-channel'
 import { JobQueue } from '../job-queue'
 import { getServerActor } from '../../helpers/utils'
+import { ActorFetchByUrlType, fetchActorByUrl } from '../../helpers/actor'
 
 // Set account keys, this could be long so process after the account creation and do not block the client
 function setAsyncActorKeys (actor: ActorModel) {
@@ -38,13 +39,14 @@ function setAsyncActorKeys (actor: ActorModel) {
 
 async function getOrCreateActorAndServerAndModel (
   activityActor: string | ActivityPubActor,
+  fetchType: ActorFetchByUrlType = 'actor-and-association-ids',
   recurseIfNeeded = true,
   updateCollections = false
 ) {
   const actorUrl = getActorUrl(activityActor)
   let created = false
 
-  let actor = await ActorModel.loadByUrl(actorUrl)
+  let actor = await fetchActorByUrl(actorUrl, fetchType)
   // Orphan actor (not associated to an account of channel) so recreate it
   if (actor && (!actor.Account && !actor.VideoChannel)) {
     await actor.destroy()
@@ -65,7 +67,7 @@ async function getOrCreateActorAndServerAndModel (
 
       try {
         // Assert we don't recurse another time
-        ownerActor = await getOrCreateActorAndServerAndModel(accountAttributedTo.id, false)
+        ownerActor = await getOrCreateActorAndServerAndModel(accountAttributedTo.id, 'all', false)
       } catch (err) {
         logger.error('Cannot get or create account attributed to video channel ' + actor.url)
         throw new Error(err)
@@ -76,10 +78,7 @@ async function getOrCreateActorAndServerAndModel (
     created = true
   }
 
-  if (actor.Account) actor.Account.Actor = actor
-  if (actor.VideoChannel) actor.VideoChannel.Actor = actor
-
-  const { actor: actorRefreshed, refreshed } = await retryTransactionWrapper(refreshActorIfNeeded, actor)
+  const { actor: actorRefreshed, refreshed } = await retryTransactionWrapper(refreshActorIfNeeded, actor, fetchType)
   if (!actorRefreshed) throw new Error('Actor ' + actorRefreshed.url + ' does not exist anymore.')
 
   if ((created === true || refreshed === true) && updateCollections === true) {
@@ -370,8 +369,14 @@ async function saveVideoChannel (actor: ActorModel, result: FetchRemoteActorResu
   return videoChannelCreated
 }
 
-async function refreshActorIfNeeded (actor: ActorModel): Promise<{ actor: ActorModel, refreshed: boolean }> {
-  if (!actor.isOutdated()) return { actor, refreshed: false }
+async function refreshActorIfNeeded (
+  actorArg: ActorModel,
+  fetchedType: ActorFetchByUrlType
+): Promise<{ actor: ActorModel, refreshed: boolean }> {
+  if (!actorArg.isOutdated()) return { actor: actorArg, refreshed: false }
+
+  // We need more attributes
+  const actor = fetchedType === 'all' ? actorArg : await ActorModel.loadByUrlAndPopulateAccountAndChannel(actorArg.url)
 
   try {
     const actorUrl = await getUrlFromWebfinger(actor.preferredUsername + '@' + actor.getHost())
index 7325ddcb66f626849545e6a8a6dc5666bce47e1d..20558daf9bbca290c3b4cc9fa6018911d82a4f2c 100644 (file)
@@ -1,10 +1,9 @@
 import { CacheFileObject } from '../../../shared/index'
 import { VideoModel } from '../../models/video/video'
-import { ActorModel } from '../../models/activitypub/actor'
 import { sequelizeTypescript } from '../../initializers'
 import { VideoRedundancyModel } from '../../models/redundancy/video-redundancy'
 
-function cacheFileActivityObjectToDBAttributes (cacheFileObject: CacheFileObject, video: VideoModel, byActor: ActorModel) {
+function cacheFileActivityObjectToDBAttributes (cacheFileObject: CacheFileObject, video: VideoModel, byActor: { id?: number }) {
   const url = cacheFileObject.url
 
   const videoFile = video.VideoFiles.find(f => {
@@ -23,7 +22,7 @@ function cacheFileActivityObjectToDBAttributes (cacheFileObject: CacheFileObject
   }
 }
 
-function createCacheFile (cacheFileObject: CacheFileObject, video: VideoModel, byActor: ActorModel) {
+function createCacheFile (cacheFileObject: CacheFileObject, video: VideoModel, byActor: { id?: number }) {
   return sequelizeTypescript.transaction(async t => {
     const attributes = cacheFileActivityObjectToDBAttributes(cacheFileObject, video, byActor)
 
@@ -31,7 +30,7 @@ function createCacheFile (cacheFileObject: CacheFileObject, video: VideoModel, b
   })
 }
 
-function updateCacheFile (cacheFileObject: CacheFileObject, redundancyModel: VideoRedundancyModel, byActor: ActorModel) {
+function updateCacheFile (cacheFileObject: CacheFileObject, redundancyModel: VideoRedundancyModel, byActor: { id?: number }) {
   const attributes = cacheFileActivityObjectToDBAttributes(cacheFileObject, redundancyModel.VideoFile.Video, byActor)
 
   redundancyModel.set('expires', attributes.expiresOn)
index 046370b79dc0124366276903110d43c7657ff995..89bda9c32a531c4d49472702e59801ccae403582 100644 (file)
@@ -1,15 +1,11 @@
 import { ActivityAccept } from '../../../../shared/models/activitypub'
-import { getActorUrl } from '../../../helpers/activitypub'
 import { ActorModel } from '../../../models/activitypub/actor'
 import { ActorFollowModel } from '../../../models/activitypub/actor-follow'
 import { addFetchOutboxJob } from '../actor'
 
-async function processAcceptActivity (activity: ActivityAccept, inboxActor?: ActorModel) {
+async function processAcceptActivity (activity: ActivityAccept, targetActor: ActorModel, inboxActor?: ActorModel) {
   if (inboxActor === undefined) throw new Error('Need to accept on explicit inbox.')
 
-  const actorUrl = getActorUrl(activity.actor)
-  const targetActor = await ActorModel.loadByUrl(actorUrl)
-
   return processAccept(inboxActor, targetActor)
 }
 
index b968389b342e58ef620509013dea70c2f9d54ee6..cc88b5423702f0be03dfd8d563b054e3b07c95e2 100644 (file)
@@ -2,15 +2,11 @@ import { ActivityAnnounce } from '../../../../shared/models/activitypub'
 import { retryTransactionWrapper } from '../../../helpers/database-utils'
 import { sequelizeTypescript } from '../../../initializers'
 import { ActorModel } from '../../../models/activitypub/actor'
-import { VideoModel } from '../../../models/video/video'
 import { VideoShareModel } from '../../../models/video/video-share'
-import { getOrCreateActorAndServerAndModel } from '../actor'
 import { forwardVideoRelatedActivity } from '../send/utils'
 import { getOrCreateVideoAndAccountAndChannel } from '../videos'
 
-async function processAnnounceActivity (activity: ActivityAnnounce) {
-  const actorAnnouncer = await getOrCreateActorAndServerAndModel(activity.actor)
-
+async function processAnnounceActivity (activity: ActivityAnnounce, actorAnnouncer: ActorModel) {
   return retryTransactionWrapper(processVideoShare, actorAnnouncer, activity)
 }
 
index 559a0c23c8fd4623a423ab9c9d3574fae161821b..5197dac73455bef70c8d2cff984775c88404e713 100644 (file)
@@ -7,30 +7,28 @@ import { sequelizeTypescript } from '../../../initializers'
 import { AccountVideoRateModel } from '../../../models/account/account-video-rate'
 import { ActorModel } from '../../../models/activitypub/actor'
 import { VideoAbuseModel } from '../../../models/video/video-abuse'
-import { getOrCreateActorAndServerAndModel } from '../actor'
 import { addVideoComment, resolveThread } from '../video-comments'
 import { getOrCreateVideoAndAccountAndChannel } from '../videos'
 import { forwardActivity, forwardVideoRelatedActivity } from '../send/utils'
 import { Redis } from '../../redis'
 import { createCacheFile } from '../cache-file'
 
-async function processCreateActivity (activity: ActivityCreate) {
+async function processCreateActivity (activity: ActivityCreate, byActor: ActorModel) {
   const activityObject = activity.object
   const activityType = activityObject.type
-  const actor = await getOrCreateActorAndServerAndModel(activity.actor)
 
   if (activityType === 'View') {
-    return processCreateView(actor, activity)
+    return processCreateView(byActor, activity)
   } else if (activityType === 'Dislike') {
-    return retryTransactionWrapper(processCreateDislike, actor, activity)
+    return retryTransactionWrapper(processCreateDislike, byActor, activity)
   } else if (activityType === 'Video') {
     return processCreateVideo(activity)
   } else if (activityType === 'Flag') {
-    return retryTransactionWrapper(processCreateVideoAbuse, actor, activityObject as VideoAbuseObject)
+    return retryTransactionWrapper(processCreateVideoAbuse, byActor, activityObject as VideoAbuseObject)
   } else if (activityType === 'Note') {
-    return retryTransactionWrapper(processCreateVideoComment, actor, activity)
+    return retryTransactionWrapper(processCreateVideoComment, byActor, activity)
   } else if (activityType === 'CacheFile') {
-    return retryTransactionWrapper(processCacheFile, actor, activity)
+    return retryTransactionWrapper(processCacheFile, byActor, activity)
   }
 
   logger.warn('Unknown activity object type %s when creating activity.', activityType, { activity: activity.id })
@@ -118,11 +116,11 @@ async function processCacheFile (byActor: ActorModel, activity: ActivityCreate)
   }
 }
 
-async function processCreateVideoAbuse (actor: ActorModel, videoAbuseToCreateData: VideoAbuseObject) {
+async function processCreateVideoAbuse (byActor: ActorModel, videoAbuseToCreateData: VideoAbuseObject) {
   logger.debug('Reporting remote abuse for video %s.', videoAbuseToCreateData.object)
 
-  const account = actor.Account
-  if (!account) throw new Error('Cannot create dislike with the non account actor ' + actor.url)
+  const account = byActor.Account
+  if (!account) throw new Error('Cannot create dislike with the non account actor ' + byActor.url)
 
   const { video } = await getOrCreateVideoAndAccountAndChannel({ videoObject: videoAbuseToCreateData.object })
 
index 4c034a81c82e2e145acdfb4a90af493e0183e190..bf2a4d114c0b6dae801a843a52693413be7af172 100644 (file)
@@ -7,34 +7,32 @@ import { ActorModel } from '../../../models/activitypub/actor'
 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/utils'
 
-async function processDeleteActivity (activity: ActivityDelete) {
+async function processDeleteActivity (activity: ActivityDelete, byActor: ActorModel) {
   const objectUrl = typeof activity.object === 'string' ? activity.object : activity.object.id
 
   if (activity.actor === objectUrl) {
-    let actor = await ActorModel.loadByUrl(activity.actor)
-    if (!actor) return undefined
+    // We need more attributes (all the account and channel)
+    const byActorFull = await ActorModel.loadByUrlAndPopulateAccountAndChannel(byActor.url)
 
-    if (actor.type === 'Person') {
-      if (!actor.Account) throw new Error('Actor ' + actor.url + ' is a person but we cannot find it in database.')
+    if (byActorFull.type === 'Person') {
+      if (!byActorFull.Account) throw new Error('Actor ' + byActorFull.url + ' is a person but we cannot find it in database.')
 
-      actor.Account.Actor = await actor.Account.$get('Actor') as ActorModel
-      return retryTransactionWrapper(processDeleteAccount, actor.Account)
-    } else if (actor.type === 'Group') {
-      if (!actor.VideoChannel) throw new Error('Actor ' + actor.url + ' is a group but we cannot find it in database.')
+      byActorFull.Account.Actor = await byActorFull.Account.$get('Actor') as ActorModel
+      return retryTransactionWrapper(processDeleteAccount, byActorFull.Account)
+    } else if (byActorFull.type === 'Group') {
+      if (!byActorFull.VideoChannel) throw new Error('Actor ' + byActorFull.url + ' is a group but we cannot find it in database.')
 
-      actor.VideoChannel.Actor = await actor.VideoChannel.$get('Actor') as ActorModel
-      return retryTransactionWrapper(processDeleteVideoChannel, actor.VideoChannel)
+      byActorFull.VideoChannel.Actor = await byActorFull.VideoChannel.$get('Actor') as ActorModel
+      return retryTransactionWrapper(processDeleteVideoChannel, byActorFull.VideoChannel)
     }
   }
 
-  const actor = await getOrCreateActorAndServerAndModel(activity.actor)
   {
     const videoCommentInstance = await VideoCommentModel.loadByUrlAndPopulateAccount(objectUrl)
     if (videoCommentInstance) {
-      return retryTransactionWrapper(processDeleteVideoComment, actor, videoCommentInstance, activity)
+      return retryTransactionWrapper(processDeleteVideoComment, byActor, videoCommentInstance, activity)
     }
   }
 
@@ -43,7 +41,7 @@ async function processDeleteActivity (activity: ActivityDelete) {
     if (videoInstance) {
       if (videoInstance.isOwned()) throw new Error(`Remote instance cannot delete owned video ${videoInstance.url}.`)
 
-      return retryTransactionWrapper(processDeleteVideo, actor, videoInstance)
+      return retryTransactionWrapper(processDeleteVideo, byActor, videoInstance)
     }
   }
 
index f34fd66cc24e953882dfb0d94d76391db3b3b1af..24c9085f7ac77520a537e713e8c09a336518341c 100644 (file)
@@ -4,14 +4,12 @@ import { logger } from '../../../helpers/logger'
 import { sequelizeTypescript } from '../../../initializers'
 import { ActorModel } from '../../../models/activitypub/actor'
 import { ActorFollowModel } from '../../../models/activitypub/actor-follow'
-import { getOrCreateActorAndServerAndModel } from '../actor'
 import { sendAccept } from '../send'
 
-async function processFollowActivity (activity: ActivityFollow) {
+async function processFollowActivity (activity: ActivityFollow, byActor: ActorModel) {
   const activityObject = activity.object
-  const actor = await getOrCreateActorAndServerAndModel(activity.actor)
 
-  return retryTransactionWrapper(processFollow, actor, activityObject)
+  return retryTransactionWrapper(processFollow, byActor, activityObject)
 }
 
 // ---------------------------------------------------------------------------
@@ -24,7 +22,7 @@ export {
 
 async function processFollow (actor: ActorModel, targetActorURL: string) {
   await sequelizeTypescript.transaction(async t => {
-    const targetActor = await ActorModel.loadByUrl(targetActorURL, t)
+    const targetActor = await ActorModel.loadByUrlAndPopulateAccountAndChannel(targetActorURL, t)
 
     if (!targetActor) throw new Error('Unknown actor')
     if (targetActor.isOwned() === false) throw new Error('This is not a local actor.')
index 631a9dde7b07fc527dee43ecd2d2329ce151e7e8..f7200db6187be4efb649b9ee25d4c0256eecf499 100644 (file)
@@ -3,14 +3,11 @@ import { retryTransactionWrapper } from '../../../helpers/database-utils'
 import { sequelizeTypescript } from '../../../initializers'
 import { AccountVideoRateModel } from '../../../models/account/account-video-rate'
 import { ActorModel } from '../../../models/activitypub/actor'
-import { getOrCreateActorAndServerAndModel } from '../actor'
 import { forwardVideoRelatedActivity } from '../send/utils'
 import { getOrCreateVideoAndAccountAndChannel } from '../videos'
 
-async function processLikeActivity (activity: ActivityLike) {
-  const actor = await getOrCreateActorAndServerAndModel(activity.actor)
-
-  return retryTransactionWrapper(processLikeVideo, actor, activity)
+async function processLikeActivity (activity: ActivityLike, byActor: ActorModel) {
+  return retryTransactionWrapper(processLikeVideo, byActor, activity)
 }
 
 // ---------------------------------------------------------------------------
index f06b03772d08c6135d9d6dc90ce42b209a54eb2c..b0e6783166ac3c4b6d739e70b8ca3f6022e97be4 100644 (file)
@@ -1,15 +1,11 @@
 import { ActivityReject } from '../../../../shared/models/activitypub/activity'
-import { getActorUrl } from '../../../helpers/activitypub'
 import { sequelizeTypescript } from '../../../initializers'
 import { ActorModel } from '../../../models/activitypub/actor'
 import { ActorFollowModel } from '../../../models/activitypub/actor-follow'
 
-async function processRejectActivity (activity: ActivityReject, inboxActor?: ActorModel) {
+async function processRejectActivity (activity: ActivityReject, targetActor: ActorModel, inboxActor?: ActorModel) {
   if (inboxActor === undefined) throw new Error('Need to reject on explicit inbox.')
 
-  const actorUrl = getActorUrl(activity.actor)
-  const targetActor = await ActorModel.loadByUrl(actorUrl)
-
   return processReject(inboxActor, targetActor)
 }
 
index b78de66973142e601ff1ee215261ea5b6005e1c4..c091d96788e8de7084c481c8821f4b96f5e922fb 100644 (file)
@@ -13,7 +13,7 @@ import { getOrCreateVideoAndAccountAndChannel } from '../videos'
 import { VideoShareModel } from '../../../models/video/video-share'
 import { VideoRedundancyModel } from '../../../models/redundancy/video-redundancy'
 
-async function processUndoActivity (activity: ActivityUndo) {
+async function processUndoActivity (activity: ActivityUndo, byActor: ActorModel) {
   const activityToUndo = activity.object
 
   const actorUrl = getActorUrl(activity.actor)
@@ -26,16 +26,16 @@ async function processUndoActivity (activity: ActivityUndo) {
     if (activityToUndo.object.type === 'Dislike') {
       return retryTransactionWrapper(processUndoDislike, actorUrl, activity)
     } else if (activityToUndo.object.type === 'CacheFile') {
-      return retryTransactionWrapper(processUndoCacheFile, actorUrl, activity)
+      return retryTransactionWrapper(processUndoCacheFile, byActor, activity)
     }
   }
 
   if (activityToUndo.type === 'Follow') {
-    return retryTransactionWrapper(processUndoFollow, actorUrl, activityToUndo)
+    return retryTransactionWrapper(processUndoFollow, byActor, activityToUndo)
   }
 
   if (activityToUndo.type === 'Announce') {
-    return retryTransactionWrapper(processUndoAnnounce, actorUrl, activityToUndo)
+    return retryTransactionWrapper(processUndoAnnounce, byActor, activityToUndo)
   }
 
   logger.warn('Unknown activity object type %s -> %s when undo activity.', activityToUndo.type, { activity: activity.id })
@@ -99,15 +99,12 @@ async function processUndoDislike (actorUrl: string, activity: ActivityUndo) {
   })
 }
 
-async function processUndoCacheFile (actorUrl: string, activity: ActivityUndo) {
+async function processUndoCacheFile (byActor: ActorModel, activity: ActivityUndo) {
   const cacheFileObject = activity.object.object as CacheFileObject
 
   const { video } = await getOrCreateVideoAndAccountAndChannel({ videoObject: cacheFileObject.object })
 
   return sequelizeTypescript.transaction(async t => {
-    const byActor = await ActorModel.loadByUrl(actorUrl)
-    if (!byActor) throw new Error('Unknown actor ' + actorUrl)
-
     const cacheFile = await VideoRedundancyModel.loadByUrl(cacheFileObject.id)
     if (!cacheFile) throw new Error('Unknown video cache ' + cacheFile.url)
 
@@ -122,10 +119,9 @@ async function processUndoCacheFile (actorUrl: string, activity: ActivityUndo) {
   })
 }
 
-function processUndoFollow (actorUrl: string, followActivity: ActivityFollow) {
+function processUndoFollow (follower: ActorModel, followActivity: ActivityFollow) {
   return sequelizeTypescript.transaction(async t => {
-    const follower = await ActorModel.loadByUrl(actorUrl, t)
-    const following = await ActorModel.loadByUrl(followActivity.object, t)
+    const following = await ActorModel.loadByUrlAndPopulateAccountAndChannel(followActivity.object, t)
     const actorFollow = await ActorFollowModel.loadByActorAndTarget(follower.id, following.id, t)
 
     if (!actorFollow) throw new Error(`'Unknown actor follow ${follower.id} -> ${following.id}.`)
@@ -136,11 +132,8 @@ function processUndoFollow (actorUrl: string, followActivity: ActivityFollow) {
   })
 }
 
-function processUndoAnnounce (actorUrl: string, announceActivity: ActivityAnnounce) {
+function processUndoAnnounce (byActor: ActorModel, announceActivity: ActivityAnnounce) {
   return sequelizeTypescript.transaction(async t => {
-    const byActor = await ActorModel.loadByUrl(actorUrl, t)
-    if (!byActor) throw new Error('Unknown actor ' + actorUrl)
-
     const share = await VideoShareModel.loadByUrl(announceActivity.id, t)
     if (!share) throw new Error(`Unknown video share ${announceActivity.id}.`)
 
index 0bceb370e12f2d6d6489ffd3380f8877dec0b027..ed3489ebfe0ab52b25f9ce6bdc9a2c0a2853b2af 100644 (file)
@@ -6,27 +6,30 @@ import { sequelizeTypescript } from '../../../initializers'
 import { AccountModel } from '../../../models/account/account'
 import { ActorModel } from '../../../models/activitypub/actor'
 import { VideoChannelModel } from '../../../models/video/video-channel'
-import { fetchAvatarIfExists, getOrCreateActorAndServerAndModel, updateActorAvatarInstance, updateActorInstance } from '../actor'
-import { getOrCreateVideoAndAccountAndChannel, updateVideoFromAP, getOrCreateVideoChannelFromVideoObject } from '../videos'
+import { fetchAvatarIfExists, updateActorAvatarInstance, updateActorInstance } from '../actor'
+import { getOrCreateVideoAndAccountAndChannel, getOrCreateVideoChannelFromVideoObject, updateVideoFromAP } from '../videos'
 import { sanitizeAndCheckVideoTorrentObject } from '../../../helpers/custom-validators/activitypub/videos'
 import { isCacheFileObjectValid } from '../../../helpers/custom-validators/activitypub/cache-file'
 import { VideoRedundancyModel } from '../../../models/redundancy/video-redundancy'
 import { createCacheFile, updateCacheFile } from '../cache-file'
 
-async function processUpdateActivity (activity: ActivityUpdate) {
-  const actor = await getOrCreateActorAndServerAndModel(activity.actor)
+async function processUpdateActivity (activity: ActivityUpdate, byActor: ActorModel) {
   const objectType = activity.object.type
 
   if (objectType === 'Video') {
-    return retryTransactionWrapper(processUpdateVideo, actor, activity)
+    return retryTransactionWrapper(processUpdateVideo, byActor, activity)
   }
 
   if (objectType === 'Person' || objectType === 'Application' || objectType === 'Group') {
-    return retryTransactionWrapper(processUpdateActor, actor, activity)
+    // We need more attributes
+    const byActorFull = await ActorModel.loadByUrlAndPopulateAccountAndChannel(byActor.url)
+    return retryTransactionWrapper(processUpdateActor, byActorFull, activity)
   }
 
   if (objectType === 'CacheFile') {
-    return retryTransactionWrapper(processUpdateCacheFile, actor, activity)
+    // We need more attributes
+    const byActorFull = await ActorModel.loadByUrlAndPopulateAccountAndChannel(byActor.url)
+    return retryTransactionWrapper(processUpdateCacheFile, byActorFull, activity)
   }
 
   return undefined
index da91675ced3d7622f5fdb7505837a775aa043b4a..35ad1696ac19d1dfe40a56432e4c5cf3b1dd8047 100644 (file)
@@ -11,8 +11,9 @@ import { processLikeActivity } from './process-like'
 import { processRejectActivity } from './process-reject'
 import { processUndoActivity } from './process-undo'
 import { processUpdateActivity } from './process-update'
+import { getOrCreateActorAndServerAndModel } from '../actor'
 
-const processActivity: { [ P in ActivityType ]: (activity: Activity, inboxActor?: ActorModel) => Promise<any> } = {
+const processActivity: { [ P in ActivityType ]: (activity: Activity, byActor: ActorModel, inboxActor?: ActorModel) => Promise<any> } = {
   Create: processCreateActivity,
   Update: processUpdateActivity,
   Delete: processDeleteActivity,
@@ -25,6 +26,8 @@ const processActivity: { [ P in ActivityType ]: (activity: Activity, inboxActor?
 }
 
 async function processActivities (activities: Activity[], signatureActor?: ActorModel, inboxActor?: ActorModel) {
+  const actorsCache: { [ url: string ]: ActorModel } = {}
+
   for (const activity of activities) {
     const actorUrl = getActorUrl(activity.actor)
 
@@ -34,6 +37,9 @@ async function processActivities (activities: Activity[], signatureActor?: Actor
       continue
     }
 
+    const byActor = signatureActor || actorsCache[actorUrl] || await getOrCreateActorAndServerAndModel(actorUrl)
+    actorsCache[actorUrl] = byActor
+
     const activityProcessor = processActivity[activity.type]
     if (activityProcessor === undefined) {
       logger.warn('Unknown activity type %s.', activity.type, { activityId: activity.id })
@@ -41,7 +47,7 @@ async function processActivities (activities: Activity[], signatureActor?: Actor
     }
 
     try {
-      await activityProcessor(activity, inboxActor)
+      await activityProcessor(activity, byActor, inboxActor)
     } catch (err) {
       logger.warn('Cannot process activity %s.', activity.type, { err })
     }
index de22e358442f2c07e96748fb19e0c69d89db0b50..91231a187f386e6a04e7e429ecca2ba4d99340bb 100644 (file)
@@ -107,7 +107,7 @@ function getOrCreateVideoChannelFromVideoObject (videoObject: VideoTorrentObject
   const channel = videoObject.attributedTo.find(a => a.type === 'Group')
   if (!channel) throw new Error('Cannot find associated video channel to video ' + videoObject.url)
 
-  return getOrCreateActorAndServerAndModel(channel.id)
+  return getOrCreateActorAndServerAndModel(channel.id, 'all')
 }
 
 type SyncParam = {
index 69c2eca57ed703598a775c2e84e7a6e5b9ed4b37..f8bb593239dbbe47792f193ad66766d394f9f945 100644 (file)
@@ -323,6 +323,29 @@ export class ActorModel extends Model<ActorModel> {
   }
 
   static loadByUrl (url: string, transaction?: Sequelize.Transaction) {
+    const query = {
+      where: {
+        url
+      },
+      transaction,
+      include: [
+        {
+          attributes: [ 'id' ],
+          model: AccountModel.unscoped(),
+          required: false
+        },
+        {
+          attributes: [ 'id' ],
+          model: VideoChannelModel.unscoped(),
+          required: false
+        }
+      ]
+    }
+
+    return ActorModel.unscoped().findOne(query)
+  }
+
+  static loadByUrlAndPopulateAccountAndChannel (url: string, transaction?: Sequelize.Transaction) {
     const query = {
       where: {
         url