From: Chocobozzz Date: Wed, 19 Sep 2018 12:44:20 +0000 (+0200) Subject: Optimize activity actor load in AP processors X-Git-Tag: v1.0.0-beta.14~48 X-Git-Url: https://git.immae.eu/?a=commitdiff_plain;h=e587e0ecee5bec43a225995948faaa4bc97f080a;p=github%2FChocobozzz%2FPeerTube.git Optimize activity actor load in AP processors --- diff --git a/server/controllers/api/search.ts b/server/controllers/api/search.ts index ea3166f5f..fd4db7a54 100644 --- a/server/controllers/api/search.ts +++ b/server/controllers/api/search.ts @@ -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 index 000000000..12a7ace9f --- /dev/null +++ b/server/helpers/actor.ts @@ -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 +} diff --git a/server/lib/activitypub/actor.ts b/server/lib/activitypub/actor.ts index 3464add03..0bdb7d12e 100644 --- a/server/lib/activitypub/actor.ts +++ b/server/lib/activitypub/actor.ts @@ -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()) diff --git a/server/lib/activitypub/cache-file.ts b/server/lib/activitypub/cache-file.ts index 7325ddcb6..20558daf9 100644 --- a/server/lib/activitypub/cache-file.ts +++ b/server/lib/activitypub/cache-file.ts @@ -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) diff --git a/server/lib/activitypub/process/process-accept.ts b/server/lib/activitypub/process/process-accept.ts index 046370b79..89bda9c32 100644 --- a/server/lib/activitypub/process/process-accept.ts +++ b/server/lib/activitypub/process/process-accept.ts @@ -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) } diff --git a/server/lib/activitypub/process/process-announce.ts b/server/lib/activitypub/process/process-announce.ts index b968389b3..cc88b5423 100644 --- a/server/lib/activitypub/process/process-announce.ts +++ b/server/lib/activitypub/process/process-announce.ts @@ -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) } diff --git a/server/lib/activitypub/process/process-create.ts b/server/lib/activitypub/process/process-create.ts index 559a0c23c..5197dac73 100644 --- a/server/lib/activitypub/process/process-create.ts +++ b/server/lib/activitypub/process/process-create.ts @@ -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 }) diff --git a/server/lib/activitypub/process/process-delete.ts b/server/lib/activitypub/process/process-delete.ts index 4c034a81c..bf2a4d114 100644 --- a/server/lib/activitypub/process/process-delete.ts +++ b/server/lib/activitypub/process/process-delete.ts @@ -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) } } diff --git a/server/lib/activitypub/process/process-follow.ts b/server/lib/activitypub/process/process-follow.ts index f34fd66cc..24c9085f7 100644 --- a/server/lib/activitypub/process/process-follow.ts +++ b/server/lib/activitypub/process/process-follow.ts @@ -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.') diff --git a/server/lib/activitypub/process/process-like.ts b/server/lib/activitypub/process/process-like.ts index 631a9dde7..f7200db61 100644 --- a/server/lib/activitypub/process/process-like.ts +++ b/server/lib/activitypub/process/process-like.ts @@ -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) } // --------------------------------------------------------------------------- diff --git a/server/lib/activitypub/process/process-reject.ts b/server/lib/activitypub/process/process-reject.ts index f06b03772..b0e678316 100644 --- a/server/lib/activitypub/process/process-reject.ts +++ b/server/lib/activitypub/process/process-reject.ts @@ -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) } diff --git a/server/lib/activitypub/process/process-undo.ts b/server/lib/activitypub/process/process-undo.ts index b78de6697..c091d9678 100644 --- a/server/lib/activitypub/process/process-undo.ts +++ b/server/lib/activitypub/process/process-undo.ts @@ -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}.`) diff --git a/server/lib/activitypub/process/process-update.ts b/server/lib/activitypub/process/process-update.ts index 0bceb370e..ed3489ebf 100644 --- a/server/lib/activitypub/process/process-update.ts +++ b/server/lib/activitypub/process/process-update.ts @@ -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 diff --git a/server/lib/activitypub/process/process.ts b/server/lib/activitypub/process/process.ts index da91675ce..35ad1696a 100644 --- a/server/lib/activitypub/process/process.ts +++ b/server/lib/activitypub/process/process.ts @@ -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 } = { +const processActivity: { [ P in ActivityType ]: (activity: Activity, byActor: ActorModel, inboxActor?: ActorModel) => Promise } = { 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 }) } diff --git a/server/lib/activitypub/videos.ts b/server/lib/activitypub/videos.ts index de22e3584..91231a187 100644 --- a/server/lib/activitypub/videos.ts +++ b/server/lib/activitypub/videos.ts @@ -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 = { diff --git a/server/models/activitypub/actor.ts b/server/models/activitypub/actor.ts index 69c2eca57..f8bb59323 100644 --- a/server/models/activitypub/actor.ts +++ b/server/models/activitypub/actor.ts @@ -323,6 +323,29 @@ export class ActorModel extends Model { } 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