aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorChocobozzz <me@florianbigard.com>2018-09-19 14:44:20 +0200
committerChocobozzz <me@florianbigard.com>2018-09-19 15:22:55 +0200
commite587e0ecee5bec43a225995948faaa4bc97f080a (patch)
tree6348e28eb06086d0c8586ceb91230b4a4af67053
parentd4defe07d26013a75577b30608841fe3f8334308 (diff)
downloadPeerTube-e587e0ecee5bec43a225995948faaa4bc97f080a.tar.gz
PeerTube-e587e0ecee5bec43a225995948faaa4bc97f080a.tar.zst
PeerTube-e587e0ecee5bec43a225995948faaa4bc97f080a.zip
Optimize activity actor load in AP processors
-rw-r--r--server/controllers/api/search.ts2
-rw-r--r--server/helpers/actor.ts13
-rw-r--r--server/lib/activitypub/actor.ts21
-rw-r--r--server/lib/activitypub/cache-file.ts7
-rw-r--r--server/lib/activitypub/process/process-accept.ts6
-rw-r--r--server/lib/activitypub/process/process-announce.ts6
-rw-r--r--server/lib/activitypub/process/process-create.ts20
-rw-r--r--server/lib/activitypub/process/process-delete.ts28
-rw-r--r--server/lib/activitypub/process/process-follow.ts8
-rw-r--r--server/lib/activitypub/process/process-like.ts7
-rw-r--r--server/lib/activitypub/process/process-reject.ts6
-rw-r--r--server/lib/activitypub/process/process-undo.ts23
-rw-r--r--server/lib/activitypub/process/process-update.ts17
-rw-r--r--server/lib/activitypub/process/process.ts10
-rw-r--r--server/lib/activitypub/videos.ts2
-rw-r--r--server/models/activitypub/actor.ts23
16 files changed, 110 insertions, 89 deletions
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
89 89
90 if (isUserAbleToSearchRemoteURI(res)) { 90 if (isUserAbleToSearchRemoteURI(res)) {
91 try { 91 try {
92 const actor = await getOrCreateActorAndServerAndModel(uri, true, true) 92 const actor = await getOrCreateActorAndServerAndModel(uri, 'all', true, true)
93 videoChannel = actor.VideoChannel 93 videoChannel = actor.VideoChannel
94 } catch (err) { 94 } catch (err) {
95 logger.info('Cannot search remote video channel %s.', uri, { err }) 95 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 @@
1import { ActorModel } from '../models/activitypub/actor'
2
3type ActorFetchByUrlType = 'all' | 'actor-and-association-ids'
4function fetchActorByUrl (url: string, fetchType: ActorFetchByUrlType) {
5 if (fetchType === 'all') return ActorModel.loadByUrlAndPopulateAccountAndChannel(url)
6
7 if (fetchType === 'actor-and-association-ids') return ActorModel.loadByUrl(url)
8}
9
10export {
11 ActorFetchByUrlType,
12 fetchActorByUrl
13}
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'
21import { VideoChannelModel } from '../../models/video/video-channel' 21import { VideoChannelModel } from '../../models/video/video-channel'
22import { JobQueue } from '../job-queue' 22import { JobQueue } from '../job-queue'
23import { getServerActor } from '../../helpers/utils' 23import { getServerActor } from '../../helpers/utils'
24import { ActorFetchByUrlType, fetchActorByUrl } from '../../helpers/actor'
24 25
25// Set account keys, this could be long so process after the account creation and do not block the client 26// Set account keys, this could be long so process after the account creation and do not block the client
26function setAsyncActorKeys (actor: ActorModel) { 27function setAsyncActorKeys (actor: ActorModel) {
@@ -38,13 +39,14 @@ function setAsyncActorKeys (actor: ActorModel) {
38 39
39async function getOrCreateActorAndServerAndModel ( 40async function getOrCreateActorAndServerAndModel (
40 activityActor: string | ActivityPubActor, 41 activityActor: string | ActivityPubActor,
42 fetchType: ActorFetchByUrlType = 'actor-and-association-ids',
41 recurseIfNeeded = true, 43 recurseIfNeeded = true,
42 updateCollections = false 44 updateCollections = false
43) { 45) {
44 const actorUrl = getActorUrl(activityActor) 46 const actorUrl = getActorUrl(activityActor)
45 let created = false 47 let created = false
46 48
47 let actor = await ActorModel.loadByUrl(actorUrl) 49 let actor = await fetchActorByUrl(actorUrl, fetchType)
48 // Orphan actor (not associated to an account of channel) so recreate it 50 // Orphan actor (not associated to an account of channel) so recreate it
49 if (actor && (!actor.Account && !actor.VideoChannel)) { 51 if (actor && (!actor.Account && !actor.VideoChannel)) {
50 await actor.destroy() 52 await actor.destroy()
@@ -65,7 +67,7 @@ async function getOrCreateActorAndServerAndModel (
65 67
66 try { 68 try {
67 // Assert we don't recurse another time 69 // Assert we don't recurse another time
68 ownerActor = await getOrCreateActorAndServerAndModel(accountAttributedTo.id, false) 70 ownerActor = await getOrCreateActorAndServerAndModel(accountAttributedTo.id, 'all', false)
69 } catch (err) { 71 } catch (err) {
70 logger.error('Cannot get or create account attributed to video channel ' + actor.url) 72 logger.error('Cannot get or create account attributed to video channel ' + actor.url)
71 throw new Error(err) 73 throw new Error(err)
@@ -76,10 +78,7 @@ async function getOrCreateActorAndServerAndModel (
76 created = true 78 created = true
77 } 79 }
78 80
79 if (actor.Account) actor.Account.Actor = actor 81 const { actor: actorRefreshed, refreshed } = await retryTransactionWrapper(refreshActorIfNeeded, actor, fetchType)
80 if (actor.VideoChannel) actor.VideoChannel.Actor = actor
81
82 const { actor: actorRefreshed, refreshed } = await retryTransactionWrapper(refreshActorIfNeeded, actor)
83 if (!actorRefreshed) throw new Error('Actor ' + actorRefreshed.url + ' does not exist anymore.') 82 if (!actorRefreshed) throw new Error('Actor ' + actorRefreshed.url + ' does not exist anymore.')
84 83
85 if ((created === true || refreshed === true) && updateCollections === true) { 84 if ((created === true || refreshed === true) && updateCollections === true) {
@@ -370,8 +369,14 @@ async function saveVideoChannel (actor: ActorModel, result: FetchRemoteActorResu
370 return videoChannelCreated 369 return videoChannelCreated
371} 370}
372 371
373async function refreshActorIfNeeded (actor: ActorModel): Promise<{ actor: ActorModel, refreshed: boolean }> { 372async function refreshActorIfNeeded (
374 if (!actor.isOutdated()) return { actor, refreshed: false } 373 actorArg: ActorModel,
374 fetchedType: ActorFetchByUrlType
375): Promise<{ actor: ActorModel, refreshed: boolean }> {
376 if (!actorArg.isOutdated()) return { actor: actorArg, refreshed: false }
377
378 // We need more attributes
379 const actor = fetchedType === 'all' ? actorArg : await ActorModel.loadByUrlAndPopulateAccountAndChannel(actorArg.url)
375 380
376 try { 381 try {
377 const actorUrl = await getUrlFromWebfinger(actor.preferredUsername + '@' + actor.getHost()) 382 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 @@
1import { CacheFileObject } from '../../../shared/index' 1import { CacheFileObject } from '../../../shared/index'
2import { VideoModel } from '../../models/video/video' 2import { VideoModel } from '../../models/video/video'
3import { ActorModel } from '../../models/activitypub/actor'
4import { sequelizeTypescript } from '../../initializers' 3import { sequelizeTypescript } from '../../initializers'
5import { VideoRedundancyModel } from '../../models/redundancy/video-redundancy' 4import { VideoRedundancyModel } from '../../models/redundancy/video-redundancy'
6 5
7function cacheFileActivityObjectToDBAttributes (cacheFileObject: CacheFileObject, video: VideoModel, byActor: ActorModel) { 6function cacheFileActivityObjectToDBAttributes (cacheFileObject: CacheFileObject, video: VideoModel, byActor: { id?: number }) {
8 const url = cacheFileObject.url 7 const url = cacheFileObject.url
9 8
10 const videoFile = video.VideoFiles.find(f => { 9 const videoFile = video.VideoFiles.find(f => {
@@ -23,7 +22,7 @@ function cacheFileActivityObjectToDBAttributes (cacheFileObject: CacheFileObject
23 } 22 }
24} 23}
25 24
26function createCacheFile (cacheFileObject: CacheFileObject, video: VideoModel, byActor: ActorModel) { 25function createCacheFile (cacheFileObject: CacheFileObject, video: VideoModel, byActor: { id?: number }) {
27 return sequelizeTypescript.transaction(async t => { 26 return sequelizeTypescript.transaction(async t => {
28 const attributes = cacheFileActivityObjectToDBAttributes(cacheFileObject, video, byActor) 27 const attributes = cacheFileActivityObjectToDBAttributes(cacheFileObject, video, byActor)
29 28
@@ -31,7 +30,7 @@ function createCacheFile (cacheFileObject: CacheFileObject, video: VideoModel, b
31 }) 30 })
32} 31}
33 32
34function updateCacheFile (cacheFileObject: CacheFileObject, redundancyModel: VideoRedundancyModel, byActor: ActorModel) { 33function updateCacheFile (cacheFileObject: CacheFileObject, redundancyModel: VideoRedundancyModel, byActor: { id?: number }) {
35 const attributes = cacheFileActivityObjectToDBAttributes(cacheFileObject, redundancyModel.VideoFile.Video, byActor) 34 const attributes = cacheFileActivityObjectToDBAttributes(cacheFileObject, redundancyModel.VideoFile.Video, byActor)
36 35
37 redundancyModel.set('expires', attributes.expiresOn) 36 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 @@
1import { ActivityAccept } from '../../../../shared/models/activitypub' 1import { ActivityAccept } from '../../../../shared/models/activitypub'
2import { getActorUrl } from '../../../helpers/activitypub'
3import { ActorModel } from '../../../models/activitypub/actor' 2import { ActorModel } from '../../../models/activitypub/actor'
4import { ActorFollowModel } from '../../../models/activitypub/actor-follow' 3import { ActorFollowModel } from '../../../models/activitypub/actor-follow'
5import { addFetchOutboxJob } from '../actor' 4import { addFetchOutboxJob } from '../actor'
6 5
7async function processAcceptActivity (activity: ActivityAccept, inboxActor?: ActorModel) { 6async function processAcceptActivity (activity: ActivityAccept, targetActor: ActorModel, inboxActor?: ActorModel) {
8 if (inboxActor === undefined) throw new Error('Need to accept on explicit inbox.') 7 if (inboxActor === undefined) throw new Error('Need to accept on explicit inbox.')
9 8
10 const actorUrl = getActorUrl(activity.actor)
11 const targetActor = await ActorModel.loadByUrl(actorUrl)
12
13 return processAccept(inboxActor, targetActor) 9 return processAccept(inboxActor, targetActor)
14} 10}
15 11
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'
2import { retryTransactionWrapper } from '../../../helpers/database-utils' 2import { retryTransactionWrapper } from '../../../helpers/database-utils'
3import { sequelizeTypescript } from '../../../initializers' 3import { sequelizeTypescript } from '../../../initializers'
4import { ActorModel } from '../../../models/activitypub/actor' 4import { ActorModel } from '../../../models/activitypub/actor'
5import { VideoModel } from '../../../models/video/video'
6import { VideoShareModel } from '../../../models/video/video-share' 5import { VideoShareModel } from '../../../models/video/video-share'
7import { getOrCreateActorAndServerAndModel } from '../actor'
8import { forwardVideoRelatedActivity } from '../send/utils' 6import { forwardVideoRelatedActivity } from '../send/utils'
9import { getOrCreateVideoAndAccountAndChannel } from '../videos' 7import { getOrCreateVideoAndAccountAndChannel } from '../videos'
10 8
11async function processAnnounceActivity (activity: ActivityAnnounce) { 9async function processAnnounceActivity (activity: ActivityAnnounce, actorAnnouncer: ActorModel) {
12 const actorAnnouncer = await getOrCreateActorAndServerAndModel(activity.actor)
13
14 return retryTransactionWrapper(processVideoShare, actorAnnouncer, activity) 10 return retryTransactionWrapper(processVideoShare, actorAnnouncer, activity)
15} 11}
16 12
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'
7import { AccountVideoRateModel } from '../../../models/account/account-video-rate' 7import { AccountVideoRateModel } from '../../../models/account/account-video-rate'
8import { ActorModel } from '../../../models/activitypub/actor' 8import { ActorModel } from '../../../models/activitypub/actor'
9import { VideoAbuseModel } from '../../../models/video/video-abuse' 9import { VideoAbuseModel } from '../../../models/video/video-abuse'
10import { getOrCreateActorAndServerAndModel } from '../actor'
11import { addVideoComment, resolveThread } from '../video-comments' 10import { addVideoComment, resolveThread } from '../video-comments'
12import { getOrCreateVideoAndAccountAndChannel } from '../videos' 11import { getOrCreateVideoAndAccountAndChannel } from '../videos'
13import { forwardActivity, forwardVideoRelatedActivity } from '../send/utils' 12import { forwardActivity, forwardVideoRelatedActivity } from '../send/utils'
14import { Redis } from '../../redis' 13import { Redis } from '../../redis'
15import { createCacheFile } from '../cache-file' 14import { createCacheFile } from '../cache-file'
16 15
17async function processCreateActivity (activity: ActivityCreate) { 16async function processCreateActivity (activity: ActivityCreate, byActor: ActorModel) {
18 const activityObject = activity.object 17 const activityObject = activity.object
19 const activityType = activityObject.type 18 const activityType = activityObject.type
20 const actor = await getOrCreateActorAndServerAndModel(activity.actor)
21 19
22 if (activityType === 'View') { 20 if (activityType === 'View') {
23 return processCreateView(actor, activity) 21 return processCreateView(byActor, activity)
24 } else if (activityType === 'Dislike') { 22 } else if (activityType === 'Dislike') {
25 return retryTransactionWrapper(processCreateDislike, actor, activity) 23 return retryTransactionWrapper(processCreateDislike, byActor, activity)
26 } else if (activityType === 'Video') { 24 } else if (activityType === 'Video') {
27 return processCreateVideo(activity) 25 return processCreateVideo(activity)
28 } else if (activityType === 'Flag') { 26 } else if (activityType === 'Flag') {
29 return retryTransactionWrapper(processCreateVideoAbuse, actor, activityObject as VideoAbuseObject) 27 return retryTransactionWrapper(processCreateVideoAbuse, byActor, activityObject as VideoAbuseObject)
30 } else if (activityType === 'Note') { 28 } else if (activityType === 'Note') {
31 return retryTransactionWrapper(processCreateVideoComment, actor, activity) 29 return retryTransactionWrapper(processCreateVideoComment, byActor, activity)
32 } else if (activityType === 'CacheFile') { 30 } else if (activityType === 'CacheFile') {
33 return retryTransactionWrapper(processCacheFile, actor, activity) 31 return retryTransactionWrapper(processCacheFile, byActor, activity)
34 } 32 }
35 33
36 logger.warn('Unknown activity object type %s when creating activity.', activityType, { activity: activity.id }) 34 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)
118 } 116 }
119} 117}
120 118
121async function processCreateVideoAbuse (actor: ActorModel, videoAbuseToCreateData: VideoAbuseObject) { 119async function processCreateVideoAbuse (byActor: ActorModel, videoAbuseToCreateData: VideoAbuseObject) {
122 logger.debug('Reporting remote abuse for video %s.', videoAbuseToCreateData.object) 120 logger.debug('Reporting remote abuse for video %s.', videoAbuseToCreateData.object)
123 121
124 const account = actor.Account 122 const account = byActor.Account
125 if (!account) throw new Error('Cannot create dislike with the non account actor ' + actor.url) 123 if (!account) throw new Error('Cannot create dislike with the non account actor ' + byActor.url)
126 124
127 const { video } = await getOrCreateVideoAndAccountAndChannel({ videoObject: videoAbuseToCreateData.object }) 125 const { video } = await getOrCreateVideoAndAccountAndChannel({ videoObject: videoAbuseToCreateData.object })
128 126
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'
7import { VideoModel } from '../../../models/video/video' 7import { VideoModel } from '../../../models/video/video'
8import { VideoChannelModel } from '../../../models/video/video-channel' 8import { VideoChannelModel } from '../../../models/video/video-channel'
9import { VideoCommentModel } from '../../../models/video/video-comment' 9import { VideoCommentModel } from '../../../models/video/video-comment'
10import { getOrCreateActorAndServerAndModel } from '../actor'
11import { forwardActivity } from '../send/utils' 10import { forwardActivity } from '../send/utils'
12 11
13async function processDeleteActivity (activity: ActivityDelete) { 12async function processDeleteActivity (activity: ActivityDelete, byActor: ActorModel) {
14 const objectUrl = typeof activity.object === 'string' ? activity.object : activity.object.id 13 const objectUrl = typeof activity.object === 'string' ? activity.object : activity.object.id
15 14
16 if (activity.actor === objectUrl) { 15 if (activity.actor === objectUrl) {
17 let actor = await ActorModel.loadByUrl(activity.actor) 16 // We need more attributes (all the account and channel)
18 if (!actor) return undefined 17 const byActorFull = await ActorModel.loadByUrlAndPopulateAccountAndChannel(byActor.url)
19 18
20 if (actor.type === 'Person') { 19 if (byActorFull.type === 'Person') {
21 if (!actor.Account) throw new Error('Actor ' + actor.url + ' is a person but we cannot find it in database.') 20 if (!byActorFull.Account) throw new Error('Actor ' + byActorFull.url + ' is a person but we cannot find it in database.')
22 21
23 actor.Account.Actor = await actor.Account.$get('Actor') as ActorModel 22 byActorFull.Account.Actor = await byActorFull.Account.$get('Actor') as ActorModel
24 return retryTransactionWrapper(processDeleteAccount, actor.Account) 23 return retryTransactionWrapper(processDeleteAccount, byActorFull.Account)
25 } else if (actor.type === 'Group') { 24 } else if (byActorFull.type === 'Group') {
26 if (!actor.VideoChannel) throw new Error('Actor ' + actor.url + ' is a group but we cannot find it in database.') 25 if (!byActorFull.VideoChannel) throw new Error('Actor ' + byActorFull.url + ' is a group but we cannot find it in database.')
27 26
28 actor.VideoChannel.Actor = await actor.VideoChannel.$get('Actor') as ActorModel 27 byActorFull.VideoChannel.Actor = await byActorFull.VideoChannel.$get('Actor') as ActorModel
29 return retryTransactionWrapper(processDeleteVideoChannel, actor.VideoChannel) 28 return retryTransactionWrapper(processDeleteVideoChannel, byActorFull.VideoChannel)
30 } 29 }
31 } 30 }
32 31
33 const actor = await getOrCreateActorAndServerAndModel(activity.actor)
34 { 32 {
35 const videoCommentInstance = await VideoCommentModel.loadByUrlAndPopulateAccount(objectUrl) 33 const videoCommentInstance = await VideoCommentModel.loadByUrlAndPopulateAccount(objectUrl)
36 if (videoCommentInstance) { 34 if (videoCommentInstance) {
37 return retryTransactionWrapper(processDeleteVideoComment, actor, videoCommentInstance, activity) 35 return retryTransactionWrapper(processDeleteVideoComment, byActor, videoCommentInstance, activity)
38 } 36 }
39 } 37 }
40 38
@@ -43,7 +41,7 @@ async function processDeleteActivity (activity: ActivityDelete) {
43 if (videoInstance) { 41 if (videoInstance) {
44 if (videoInstance.isOwned()) throw new Error(`Remote instance cannot delete owned video ${videoInstance.url}.`) 42 if (videoInstance.isOwned()) throw new Error(`Remote instance cannot delete owned video ${videoInstance.url}.`)
45 43
46 return retryTransactionWrapper(processDeleteVideo, actor, videoInstance) 44 return retryTransactionWrapper(processDeleteVideo, byActor, videoInstance)
47 } 45 }
48 } 46 }
49 47
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'
4import { sequelizeTypescript } from '../../../initializers' 4import { sequelizeTypescript } from '../../../initializers'
5import { ActorModel } from '../../../models/activitypub/actor' 5import { ActorModel } from '../../../models/activitypub/actor'
6import { ActorFollowModel } from '../../../models/activitypub/actor-follow' 6import { ActorFollowModel } from '../../../models/activitypub/actor-follow'
7import { getOrCreateActorAndServerAndModel } from '../actor'
8import { sendAccept } from '../send' 7import { sendAccept } from '../send'
9 8
10async function processFollowActivity (activity: ActivityFollow) { 9async function processFollowActivity (activity: ActivityFollow, byActor: ActorModel) {
11 const activityObject = activity.object 10 const activityObject = activity.object
12 const actor = await getOrCreateActorAndServerAndModel(activity.actor)
13 11
14 return retryTransactionWrapper(processFollow, actor, activityObject) 12 return retryTransactionWrapper(processFollow, byActor, activityObject)
15} 13}
16 14
17// --------------------------------------------------------------------------- 15// ---------------------------------------------------------------------------
@@ -24,7 +22,7 @@ export {
24 22
25async function processFollow (actor: ActorModel, targetActorURL: string) { 23async function processFollow (actor: ActorModel, targetActorURL: string) {
26 await sequelizeTypescript.transaction(async t => { 24 await sequelizeTypescript.transaction(async t => {
27 const targetActor = await ActorModel.loadByUrl(targetActorURL, t) 25 const targetActor = await ActorModel.loadByUrlAndPopulateAccountAndChannel(targetActorURL, t)
28 26
29 if (!targetActor) throw new Error('Unknown actor') 27 if (!targetActor) throw new Error('Unknown actor')
30 if (targetActor.isOwned() === false) throw new Error('This is not a local actor.') 28 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'
3import { sequelizeTypescript } from '../../../initializers' 3import { sequelizeTypescript } from '../../../initializers'
4import { AccountVideoRateModel } from '../../../models/account/account-video-rate' 4import { AccountVideoRateModel } from '../../../models/account/account-video-rate'
5import { ActorModel } from '../../../models/activitypub/actor' 5import { ActorModel } from '../../../models/activitypub/actor'
6import { getOrCreateActorAndServerAndModel } from '../actor'
7import { forwardVideoRelatedActivity } from '../send/utils' 6import { forwardVideoRelatedActivity } from '../send/utils'
8import { getOrCreateVideoAndAccountAndChannel } from '../videos' 7import { getOrCreateVideoAndAccountAndChannel } from '../videos'
9 8
10async function processLikeActivity (activity: ActivityLike) { 9async function processLikeActivity (activity: ActivityLike, byActor: ActorModel) {
11 const actor = await getOrCreateActorAndServerAndModel(activity.actor) 10 return retryTransactionWrapper(processLikeVideo, byActor, activity)
12
13 return retryTransactionWrapper(processLikeVideo, actor, activity)
14} 11}
15 12
16// --------------------------------------------------------------------------- 13// ---------------------------------------------------------------------------
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 @@
1import { ActivityReject } from '../../../../shared/models/activitypub/activity' 1import { ActivityReject } from '../../../../shared/models/activitypub/activity'
2import { getActorUrl } from '../../../helpers/activitypub'
3import { sequelizeTypescript } from '../../../initializers' 2import { sequelizeTypescript } from '../../../initializers'
4import { ActorModel } from '../../../models/activitypub/actor' 3import { ActorModel } from '../../../models/activitypub/actor'
5import { ActorFollowModel } from '../../../models/activitypub/actor-follow' 4import { ActorFollowModel } from '../../../models/activitypub/actor-follow'
6 5
7async function processRejectActivity (activity: ActivityReject, inboxActor?: ActorModel) { 6async function processRejectActivity (activity: ActivityReject, targetActor: ActorModel, inboxActor?: ActorModel) {
8 if (inboxActor === undefined) throw new Error('Need to reject on explicit inbox.') 7 if (inboxActor === undefined) throw new Error('Need to reject on explicit inbox.')
9 8
10 const actorUrl = getActorUrl(activity.actor)
11 const targetActor = await ActorModel.loadByUrl(actorUrl)
12
13 return processReject(inboxActor, targetActor) 9 return processReject(inboxActor, targetActor)
14} 10}
15 11
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'
13import { VideoShareModel } from '../../../models/video/video-share' 13import { VideoShareModel } from '../../../models/video/video-share'
14import { VideoRedundancyModel } from '../../../models/redundancy/video-redundancy' 14import { VideoRedundancyModel } from '../../../models/redundancy/video-redundancy'
15 15
16async function processUndoActivity (activity: ActivityUndo) { 16async function processUndoActivity (activity: ActivityUndo, byActor: ActorModel) {
17 const activityToUndo = activity.object 17 const activityToUndo = activity.object
18 18
19 const actorUrl = getActorUrl(activity.actor) 19 const actorUrl = getActorUrl(activity.actor)
@@ -26,16 +26,16 @@ async function processUndoActivity (activity: ActivityUndo) {
26 if (activityToUndo.object.type === 'Dislike') { 26 if (activityToUndo.object.type === 'Dislike') {
27 return retryTransactionWrapper(processUndoDislike, actorUrl, activity) 27 return retryTransactionWrapper(processUndoDislike, actorUrl, activity)
28 } else if (activityToUndo.object.type === 'CacheFile') { 28 } else if (activityToUndo.object.type === 'CacheFile') {
29 return retryTransactionWrapper(processUndoCacheFile, actorUrl, activity) 29 return retryTransactionWrapper(processUndoCacheFile, byActor, activity)
30 } 30 }
31 } 31 }
32 32
33 if (activityToUndo.type === 'Follow') { 33 if (activityToUndo.type === 'Follow') {
34 return retryTransactionWrapper(processUndoFollow, actorUrl, activityToUndo) 34 return retryTransactionWrapper(processUndoFollow, byActor, activityToUndo)
35 } 35 }
36 36
37 if (activityToUndo.type === 'Announce') { 37 if (activityToUndo.type === 'Announce') {
38 return retryTransactionWrapper(processUndoAnnounce, actorUrl, activityToUndo) 38 return retryTransactionWrapper(processUndoAnnounce, byActor, activityToUndo)
39 } 39 }
40 40
41 logger.warn('Unknown activity object type %s -> %s when undo activity.', activityToUndo.type, { activity: activity.id }) 41 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) {
99 }) 99 })
100} 100}
101 101
102async function processUndoCacheFile (actorUrl: string, activity: ActivityUndo) { 102async function processUndoCacheFile (byActor: ActorModel, activity: ActivityUndo) {
103 const cacheFileObject = activity.object.object as CacheFileObject 103 const cacheFileObject = activity.object.object as CacheFileObject
104 104
105 const { video } = await getOrCreateVideoAndAccountAndChannel({ videoObject: cacheFileObject.object }) 105 const { video } = await getOrCreateVideoAndAccountAndChannel({ videoObject: cacheFileObject.object })
106 106
107 return sequelizeTypescript.transaction(async t => { 107 return sequelizeTypescript.transaction(async t => {
108 const byActor = await ActorModel.loadByUrl(actorUrl)
109 if (!byActor) throw new Error('Unknown actor ' + actorUrl)
110
111 const cacheFile = await VideoRedundancyModel.loadByUrl(cacheFileObject.id) 108 const cacheFile = await VideoRedundancyModel.loadByUrl(cacheFileObject.id)
112 if (!cacheFile) throw new Error('Unknown video cache ' + cacheFile.url) 109 if (!cacheFile) throw new Error('Unknown video cache ' + cacheFile.url)
113 110
@@ -122,10 +119,9 @@ async function processUndoCacheFile (actorUrl: string, activity: ActivityUndo) {
122 }) 119 })
123} 120}
124 121
125function processUndoFollow (actorUrl: string, followActivity: ActivityFollow) { 122function processUndoFollow (follower: ActorModel, followActivity: ActivityFollow) {
126 return sequelizeTypescript.transaction(async t => { 123 return sequelizeTypescript.transaction(async t => {
127 const follower = await ActorModel.loadByUrl(actorUrl, t) 124 const following = await ActorModel.loadByUrlAndPopulateAccountAndChannel(followActivity.object, t)
128 const following = await ActorModel.loadByUrl(followActivity.object, t)
129 const actorFollow = await ActorFollowModel.loadByActorAndTarget(follower.id, following.id, t) 125 const actorFollow = await ActorFollowModel.loadByActorAndTarget(follower.id, following.id, t)
130 126
131 if (!actorFollow) throw new Error(`'Unknown actor follow ${follower.id} -> ${following.id}.`) 127 if (!actorFollow) throw new Error(`'Unknown actor follow ${follower.id} -> ${following.id}.`)
@@ -136,11 +132,8 @@ function processUndoFollow (actorUrl: string, followActivity: ActivityFollow) {
136 }) 132 })
137} 133}
138 134
139function processUndoAnnounce (actorUrl: string, announceActivity: ActivityAnnounce) { 135function processUndoAnnounce (byActor: ActorModel, announceActivity: ActivityAnnounce) {
140 return sequelizeTypescript.transaction(async t => { 136 return sequelizeTypescript.transaction(async t => {
141 const byActor = await ActorModel.loadByUrl(actorUrl, t)
142 if (!byActor) throw new Error('Unknown actor ' + actorUrl)
143
144 const share = await VideoShareModel.loadByUrl(announceActivity.id, t) 137 const share = await VideoShareModel.loadByUrl(announceActivity.id, t)
145 if (!share) throw new Error(`Unknown video share ${announceActivity.id}.`) 138 if (!share) throw new Error(`Unknown video share ${announceActivity.id}.`)
146 139
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'
6import { AccountModel } from '../../../models/account/account' 6import { AccountModel } from '../../../models/account/account'
7import { ActorModel } from '../../../models/activitypub/actor' 7import { ActorModel } from '../../../models/activitypub/actor'
8import { VideoChannelModel } from '../../../models/video/video-channel' 8import { VideoChannelModel } from '../../../models/video/video-channel'
9import { fetchAvatarIfExists, getOrCreateActorAndServerAndModel, updateActorAvatarInstance, updateActorInstance } from '../actor' 9import { fetchAvatarIfExists, updateActorAvatarInstance, updateActorInstance } from '../actor'
10import { getOrCreateVideoAndAccountAndChannel, updateVideoFromAP, getOrCreateVideoChannelFromVideoObject } from '../videos' 10import { getOrCreateVideoAndAccountAndChannel, getOrCreateVideoChannelFromVideoObject, updateVideoFromAP } from '../videos'
11import { sanitizeAndCheckVideoTorrentObject } from '../../../helpers/custom-validators/activitypub/videos' 11import { sanitizeAndCheckVideoTorrentObject } from '../../../helpers/custom-validators/activitypub/videos'
12import { isCacheFileObjectValid } from '../../../helpers/custom-validators/activitypub/cache-file' 12import { isCacheFileObjectValid } from '../../../helpers/custom-validators/activitypub/cache-file'
13import { VideoRedundancyModel } from '../../../models/redundancy/video-redundancy' 13import { VideoRedundancyModel } from '../../../models/redundancy/video-redundancy'
14import { createCacheFile, updateCacheFile } from '../cache-file' 14import { createCacheFile, updateCacheFile } from '../cache-file'
15 15
16async function processUpdateActivity (activity: ActivityUpdate) { 16async function processUpdateActivity (activity: ActivityUpdate, byActor: ActorModel) {
17 const actor = await getOrCreateActorAndServerAndModel(activity.actor)
18 const objectType = activity.object.type 17 const objectType = activity.object.type
19 18
20 if (objectType === 'Video') { 19 if (objectType === 'Video') {
21 return retryTransactionWrapper(processUpdateVideo, actor, activity) 20 return retryTransactionWrapper(processUpdateVideo, byActor, activity)
22 } 21 }
23 22
24 if (objectType === 'Person' || objectType === 'Application' || objectType === 'Group') { 23 if (objectType === 'Person' || objectType === 'Application' || objectType === 'Group') {
25 return retryTransactionWrapper(processUpdateActor, actor, activity) 24 // We need more attributes
25 const byActorFull = await ActorModel.loadByUrlAndPopulateAccountAndChannel(byActor.url)
26 return retryTransactionWrapper(processUpdateActor, byActorFull, activity)
26 } 27 }
27 28
28 if (objectType === 'CacheFile') { 29 if (objectType === 'CacheFile') {
29 return retryTransactionWrapper(processUpdateCacheFile, actor, activity) 30 // We need more attributes
31 const byActorFull = await ActorModel.loadByUrlAndPopulateAccountAndChannel(byActor.url)
32 return retryTransactionWrapper(processUpdateCacheFile, byActorFull, activity)
30 } 33 }
31 34
32 return undefined 35 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'
11import { processRejectActivity } from './process-reject' 11import { processRejectActivity } from './process-reject'
12import { processUndoActivity } from './process-undo' 12import { processUndoActivity } from './process-undo'
13import { processUpdateActivity } from './process-update' 13import { processUpdateActivity } from './process-update'
14import { getOrCreateActorAndServerAndModel } from '../actor'
14 15
15const processActivity: { [ P in ActivityType ]: (activity: Activity, inboxActor?: ActorModel) => Promise<any> } = { 16const processActivity: { [ P in ActivityType ]: (activity: Activity, byActor: ActorModel, inboxActor?: ActorModel) => Promise<any> } = {
16 Create: processCreateActivity, 17 Create: processCreateActivity,
17 Update: processUpdateActivity, 18 Update: processUpdateActivity,
18 Delete: processDeleteActivity, 19 Delete: processDeleteActivity,
@@ -25,6 +26,8 @@ const processActivity: { [ P in ActivityType ]: (activity: Activity, inboxActor?
25} 26}
26 27
27async function processActivities (activities: Activity[], signatureActor?: ActorModel, inboxActor?: ActorModel) { 28async function processActivities (activities: Activity[], signatureActor?: ActorModel, inboxActor?: ActorModel) {
29 const actorsCache: { [ url: string ]: ActorModel } = {}
30
28 for (const activity of activities) { 31 for (const activity of activities) {
29 const actorUrl = getActorUrl(activity.actor) 32 const actorUrl = getActorUrl(activity.actor)
30 33
@@ -34,6 +37,9 @@ async function processActivities (activities: Activity[], signatureActor?: Actor
34 continue 37 continue
35 } 38 }
36 39
40 const byActor = signatureActor || actorsCache[actorUrl] || await getOrCreateActorAndServerAndModel(actorUrl)
41 actorsCache[actorUrl] = byActor
42
37 const activityProcessor = processActivity[activity.type] 43 const activityProcessor = processActivity[activity.type]
38 if (activityProcessor === undefined) { 44 if (activityProcessor === undefined) {
39 logger.warn('Unknown activity type %s.', activity.type, { activityId: activity.id }) 45 logger.warn('Unknown activity type %s.', activity.type, { activityId: activity.id })
@@ -41,7 +47,7 @@ async function processActivities (activities: Activity[], signatureActor?: Actor
41 } 47 }
42 48
43 try { 49 try {
44 await activityProcessor(activity, inboxActor) 50 await activityProcessor(activity, byActor, inboxActor)
45 } catch (err) { 51 } catch (err) {
46 logger.warn('Cannot process activity %s.', activity.type, { err }) 52 logger.warn('Cannot process activity %s.', activity.type, { err })
47 } 53 }
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
107 const channel = videoObject.attributedTo.find(a => a.type === 'Group') 107 const channel = videoObject.attributedTo.find(a => a.type === 'Group')
108 if (!channel) throw new Error('Cannot find associated video channel to video ' + videoObject.url) 108 if (!channel) throw new Error('Cannot find associated video channel to video ' + videoObject.url)
109 109
110 return getOrCreateActorAndServerAndModel(channel.id) 110 return getOrCreateActorAndServerAndModel(channel.id, 'all')
111} 111}
112 112
113type SyncParam = { 113type 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
@@ -327,6 +327,29 @@ export class ActorModel extends Model<ActorModel> {
327 where: { 327 where: {
328 url 328 url
329 }, 329 },
330 transaction,
331 include: [
332 {
333 attributes: [ 'id' ],
334 model: AccountModel.unscoped(),
335 required: false
336 },
337 {
338 attributes: [ 'id' ],
339 model: VideoChannelModel.unscoped(),
340 required: false
341 }
342 ]
343 }
344
345 return ActorModel.unscoped().findOne(query)
346 }
347
348 static loadByUrlAndPopulateAccountAndChannel (url: string, transaction?: Sequelize.Transaction) {
349 const query = {
350 where: {
351 url
352 },
330 transaction 353 transaction
331 } 354 }
332 355