diff options
-rw-r--r-- | server/controllers/api/search.ts | 2 | ||||
-rw-r--r-- | server/helpers/actor.ts | 13 | ||||
-rw-r--r-- | server/lib/activitypub/actor.ts | 21 | ||||
-rw-r--r-- | server/lib/activitypub/cache-file.ts | 7 | ||||
-rw-r--r-- | server/lib/activitypub/process/process-accept.ts | 6 | ||||
-rw-r--r-- | server/lib/activitypub/process/process-announce.ts | 6 | ||||
-rw-r--r-- | server/lib/activitypub/process/process-create.ts | 20 | ||||
-rw-r--r-- | server/lib/activitypub/process/process-delete.ts | 28 | ||||
-rw-r--r-- | server/lib/activitypub/process/process-follow.ts | 8 | ||||
-rw-r--r-- | server/lib/activitypub/process/process-like.ts | 7 | ||||
-rw-r--r-- | server/lib/activitypub/process/process-reject.ts | 6 | ||||
-rw-r--r-- | server/lib/activitypub/process/process-undo.ts | 23 | ||||
-rw-r--r-- | server/lib/activitypub/process/process-update.ts | 17 | ||||
-rw-r--r-- | server/lib/activitypub/process/process.ts | 10 | ||||
-rw-r--r-- | server/lib/activitypub/videos.ts | 2 | ||||
-rw-r--r-- | server/models/activitypub/actor.ts | 23 |
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 @@ | |||
1 | import { ActorModel } from '../models/activitypub/actor' | ||
2 | |||
3 | type ActorFetchByUrlType = 'all' | 'actor-and-association-ids' | ||
4 | function 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 | |||
10 | export { | ||
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' | |||
21 | import { VideoChannelModel } from '../../models/video/video-channel' | 21 | import { VideoChannelModel } from '../../models/video/video-channel' |
22 | import { JobQueue } from '../job-queue' | 22 | import { JobQueue } from '../job-queue' |
23 | import { getServerActor } from '../../helpers/utils' | 23 | import { getServerActor } from '../../helpers/utils' |
24 | import { 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 |
26 | function setAsyncActorKeys (actor: ActorModel) { | 27 | function setAsyncActorKeys (actor: ActorModel) { |
@@ -38,13 +39,14 @@ function setAsyncActorKeys (actor: ActorModel) { | |||
38 | 39 | ||
39 | async function getOrCreateActorAndServerAndModel ( | 40 | async 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 | ||
373 | async function refreshActorIfNeeded (actor: ActorModel): Promise<{ actor: ActorModel, refreshed: boolean }> { | 372 | async 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 @@ | |||
1 | import { CacheFileObject } from '../../../shared/index' | 1 | import { CacheFileObject } from '../../../shared/index' |
2 | import { VideoModel } from '../../models/video/video' | 2 | import { VideoModel } from '../../models/video/video' |
3 | import { ActorModel } from '../../models/activitypub/actor' | ||
4 | import { sequelizeTypescript } from '../../initializers' | 3 | import { sequelizeTypescript } from '../../initializers' |
5 | import { VideoRedundancyModel } from '../../models/redundancy/video-redundancy' | 4 | import { VideoRedundancyModel } from '../../models/redundancy/video-redundancy' |
6 | 5 | ||
7 | function cacheFileActivityObjectToDBAttributes (cacheFileObject: CacheFileObject, video: VideoModel, byActor: ActorModel) { | 6 | function 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 | ||
26 | function createCacheFile (cacheFileObject: CacheFileObject, video: VideoModel, byActor: ActorModel) { | 25 | function 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 | ||
34 | function updateCacheFile (cacheFileObject: CacheFileObject, redundancyModel: VideoRedundancyModel, byActor: ActorModel) { | 33 | function 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 @@ | |||
1 | import { ActivityAccept } from '../../../../shared/models/activitypub' | 1 | import { ActivityAccept } from '../../../../shared/models/activitypub' |
2 | import { getActorUrl } from '../../../helpers/activitypub' | ||
3 | import { ActorModel } from '../../../models/activitypub/actor' | 2 | import { ActorModel } from '../../../models/activitypub/actor' |
4 | import { ActorFollowModel } from '../../../models/activitypub/actor-follow' | 3 | import { ActorFollowModel } from '../../../models/activitypub/actor-follow' |
5 | import { addFetchOutboxJob } from '../actor' | 4 | import { addFetchOutboxJob } from '../actor' |
6 | 5 | ||
7 | async function processAcceptActivity (activity: ActivityAccept, inboxActor?: ActorModel) { | 6 | async 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' | |||
2 | import { retryTransactionWrapper } from '../../../helpers/database-utils' | 2 | import { retryTransactionWrapper } from '../../../helpers/database-utils' |
3 | import { sequelizeTypescript } from '../../../initializers' | 3 | import { sequelizeTypescript } from '../../../initializers' |
4 | import { ActorModel } from '../../../models/activitypub/actor' | 4 | import { ActorModel } from '../../../models/activitypub/actor' |
5 | import { VideoModel } from '../../../models/video/video' | ||
6 | import { VideoShareModel } from '../../../models/video/video-share' | 5 | import { VideoShareModel } from '../../../models/video/video-share' |
7 | import { getOrCreateActorAndServerAndModel } from '../actor' | ||
8 | import { forwardVideoRelatedActivity } from '../send/utils' | 6 | import { forwardVideoRelatedActivity } from '../send/utils' |
9 | import { getOrCreateVideoAndAccountAndChannel } from '../videos' | 7 | import { getOrCreateVideoAndAccountAndChannel } from '../videos' |
10 | 8 | ||
11 | async function processAnnounceActivity (activity: ActivityAnnounce) { | 9 | async 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' | |||
7 | import { AccountVideoRateModel } from '../../../models/account/account-video-rate' | 7 | import { AccountVideoRateModel } from '../../../models/account/account-video-rate' |
8 | import { ActorModel } from '../../../models/activitypub/actor' | 8 | import { ActorModel } from '../../../models/activitypub/actor' |
9 | import { VideoAbuseModel } from '../../../models/video/video-abuse' | 9 | import { VideoAbuseModel } from '../../../models/video/video-abuse' |
10 | import { getOrCreateActorAndServerAndModel } from '../actor' | ||
11 | import { addVideoComment, resolveThread } from '../video-comments' | 10 | import { addVideoComment, resolveThread } from '../video-comments' |
12 | import { getOrCreateVideoAndAccountAndChannel } from '../videos' | 11 | import { getOrCreateVideoAndAccountAndChannel } from '../videos' |
13 | import { forwardActivity, forwardVideoRelatedActivity } from '../send/utils' | 12 | import { forwardActivity, forwardVideoRelatedActivity } from '../send/utils' |
14 | import { Redis } from '../../redis' | 13 | import { Redis } from '../../redis' |
15 | import { createCacheFile } from '../cache-file' | 14 | import { createCacheFile } from '../cache-file' |
16 | 15 | ||
17 | async function processCreateActivity (activity: ActivityCreate) { | 16 | async 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 | ||
121 | async function processCreateVideoAbuse (actor: ActorModel, videoAbuseToCreateData: VideoAbuseObject) { | 119 | async 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' | |||
7 | import { VideoModel } from '../../../models/video/video' | 7 | import { VideoModel } from '../../../models/video/video' |
8 | import { VideoChannelModel } from '../../../models/video/video-channel' | 8 | import { VideoChannelModel } from '../../../models/video/video-channel' |
9 | import { VideoCommentModel } from '../../../models/video/video-comment' | 9 | import { VideoCommentModel } from '../../../models/video/video-comment' |
10 | import { getOrCreateActorAndServerAndModel } from '../actor' | ||
11 | import { forwardActivity } from '../send/utils' | 10 | import { forwardActivity } from '../send/utils' |
12 | 11 | ||
13 | async function processDeleteActivity (activity: ActivityDelete) { | 12 | async 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' | |||
4 | import { sequelizeTypescript } from '../../../initializers' | 4 | import { sequelizeTypescript } from '../../../initializers' |
5 | import { ActorModel } from '../../../models/activitypub/actor' | 5 | import { ActorModel } from '../../../models/activitypub/actor' |
6 | import { ActorFollowModel } from '../../../models/activitypub/actor-follow' | 6 | import { ActorFollowModel } from '../../../models/activitypub/actor-follow' |
7 | import { getOrCreateActorAndServerAndModel } from '../actor' | ||
8 | import { sendAccept } from '../send' | 7 | import { sendAccept } from '../send' |
9 | 8 | ||
10 | async function processFollowActivity (activity: ActivityFollow) { | 9 | async 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 | ||
25 | async function processFollow (actor: ActorModel, targetActorURL: string) { | 23 | async 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' | |||
3 | import { sequelizeTypescript } from '../../../initializers' | 3 | import { sequelizeTypescript } from '../../../initializers' |
4 | import { AccountVideoRateModel } from '../../../models/account/account-video-rate' | 4 | import { AccountVideoRateModel } from '../../../models/account/account-video-rate' |
5 | import { ActorModel } from '../../../models/activitypub/actor' | 5 | import { ActorModel } from '../../../models/activitypub/actor' |
6 | import { getOrCreateActorAndServerAndModel } from '../actor' | ||
7 | import { forwardVideoRelatedActivity } from '../send/utils' | 6 | import { forwardVideoRelatedActivity } from '../send/utils' |
8 | import { getOrCreateVideoAndAccountAndChannel } from '../videos' | 7 | import { getOrCreateVideoAndAccountAndChannel } from '../videos' |
9 | 8 | ||
10 | async function processLikeActivity (activity: ActivityLike) { | 9 | async 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 @@ | |||
1 | import { ActivityReject } from '../../../../shared/models/activitypub/activity' | 1 | import { ActivityReject } from '../../../../shared/models/activitypub/activity' |
2 | import { getActorUrl } from '../../../helpers/activitypub' | ||
3 | import { sequelizeTypescript } from '../../../initializers' | 2 | import { sequelizeTypescript } from '../../../initializers' |
4 | import { ActorModel } from '../../../models/activitypub/actor' | 3 | import { ActorModel } from '../../../models/activitypub/actor' |
5 | import { ActorFollowModel } from '../../../models/activitypub/actor-follow' | 4 | import { ActorFollowModel } from '../../../models/activitypub/actor-follow' |
6 | 5 | ||
7 | async function processRejectActivity (activity: ActivityReject, inboxActor?: ActorModel) { | 6 | async 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' | |||
13 | import { VideoShareModel } from '../../../models/video/video-share' | 13 | import { VideoShareModel } from '../../../models/video/video-share' |
14 | import { VideoRedundancyModel } from '../../../models/redundancy/video-redundancy' | 14 | import { VideoRedundancyModel } from '../../../models/redundancy/video-redundancy' |
15 | 15 | ||
16 | async function processUndoActivity (activity: ActivityUndo) { | 16 | async 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 | ||
102 | async function processUndoCacheFile (actorUrl: string, activity: ActivityUndo) { | 102 | async 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 | ||
125 | function processUndoFollow (actorUrl: string, followActivity: ActivityFollow) { | 122 | function 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 | ||
139 | function processUndoAnnounce (actorUrl: string, announceActivity: ActivityAnnounce) { | 135 | function 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' | |||
6 | import { AccountModel } from '../../../models/account/account' | 6 | import { AccountModel } from '../../../models/account/account' |
7 | import { ActorModel } from '../../../models/activitypub/actor' | 7 | import { ActorModel } from '../../../models/activitypub/actor' |
8 | import { VideoChannelModel } from '../../../models/video/video-channel' | 8 | import { VideoChannelModel } from '../../../models/video/video-channel' |
9 | import { fetchAvatarIfExists, getOrCreateActorAndServerAndModel, updateActorAvatarInstance, updateActorInstance } from '../actor' | 9 | import { fetchAvatarIfExists, updateActorAvatarInstance, updateActorInstance } from '../actor' |
10 | import { getOrCreateVideoAndAccountAndChannel, updateVideoFromAP, getOrCreateVideoChannelFromVideoObject } from '../videos' | 10 | import { getOrCreateVideoAndAccountAndChannel, getOrCreateVideoChannelFromVideoObject, updateVideoFromAP } from '../videos' |
11 | import { sanitizeAndCheckVideoTorrentObject } from '../../../helpers/custom-validators/activitypub/videos' | 11 | import { sanitizeAndCheckVideoTorrentObject } from '../../../helpers/custom-validators/activitypub/videos' |
12 | import { isCacheFileObjectValid } from '../../../helpers/custom-validators/activitypub/cache-file' | 12 | import { isCacheFileObjectValid } from '../../../helpers/custom-validators/activitypub/cache-file' |
13 | import { VideoRedundancyModel } from '../../../models/redundancy/video-redundancy' | 13 | import { VideoRedundancyModel } from '../../../models/redundancy/video-redundancy' |
14 | import { createCacheFile, updateCacheFile } from '../cache-file' | 14 | import { createCacheFile, updateCacheFile } from '../cache-file' |
15 | 15 | ||
16 | async function processUpdateActivity (activity: ActivityUpdate) { | 16 | async 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' | |||
11 | import { processRejectActivity } from './process-reject' | 11 | import { processRejectActivity } from './process-reject' |
12 | import { processUndoActivity } from './process-undo' | 12 | import { processUndoActivity } from './process-undo' |
13 | import { processUpdateActivity } from './process-update' | 13 | import { processUpdateActivity } from './process-update' |
14 | import { getOrCreateActorAndServerAndModel } from '../actor' | ||
14 | 15 | ||
15 | const processActivity: { [ P in ActivityType ]: (activity: Activity, inboxActor?: ActorModel) => Promise<any> } = { | 16 | const 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 | ||
27 | async function processActivities (activities: Activity[], signatureActor?: ActorModel, inboxActor?: ActorModel) { | 28 | async 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 | ||
113 | type SyncParam = { | 113 | 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 | |||
@@ -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 | ||