]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blame - server/lib/activitypub/actors/get.ts
Implement avatar miniatures (#4639)
[github/Chocobozzz/PeerTube.git] / server / lib / activitypub / actors / get.ts
CommitLineData
136d7efd
C
1
2import { checkUrlsSameHost, getAPId } from '@server/helpers/activitypub'
136d7efd
C
3import { retryTransactionWrapper } from '@server/helpers/database-utils'
4import { logger } from '@server/helpers/logger'
5import { JobQueue } from '@server/lib/job-queue'
868fce62 6import { ActorLoadByUrlType, loadActorByUrl } from '@server/lib/model-loaders'
136d7efd
C
7import { MActor, MActorAccountChannelId, MActorAccountChannelIdActor, MActorAccountId, MActorFullActor } from '@server/types/models'
8import { ActivityPubActor } from '@shared/models'
9import { refreshActorIfNeeded } from './refresh'
10import { APActorCreator, fetchRemoteActor } from './shared'
11
12function getOrCreateAPActor (
13 activityActor: string | ActivityPubActor,
14 fetchType: 'all',
15 recurseIfNeeded?: boolean,
16 updateCollections?: boolean
17): Promise<MActorFullActor>
18
19function getOrCreateAPActor (
20 activityActor: string | ActivityPubActor,
21 fetchType?: 'association-ids',
22 recurseIfNeeded?: boolean,
23 updateCollections?: boolean
24): Promise<MActorAccountChannelId>
25
26async function getOrCreateAPActor (
27 activityActor: string | ActivityPubActor,
868fce62 28 fetchType: ActorLoadByUrlType = 'association-ids',
136d7efd
C
29 recurseIfNeeded = true,
30 updateCollections = false
31): Promise<MActorFullActor | MActorAccountChannelId> {
32 const actorUrl = getAPId(activityActor)
33 let actor = await loadActorFromDB(actorUrl, fetchType)
34
35 let created = false
36 let accountPlaylistsUrl: string
37
38 // We don't have this actor in our database, fetch it on remote
39 if (!actor) {
40 const { actorObject } = await fetchRemoteActor(actorUrl)
41 if (actorObject === undefined) throw new Error('Cannot fetch remote actor ' + actorUrl)
42
fd658484
C
43 // actorUrl is just an alias/rediraction, so process object id instead
44 if (actorObject.id !== actorUrl) return getOrCreateAPActor(actorObject, 'all', recurseIfNeeded, updateCollections)
45
136d7efd
C
46 // Create the attributed to actor
47 // In PeerTube a video channel is owned by an account
48 let ownerActor: MActorFullActor
49 if (recurseIfNeeded === true && actorObject.type === 'Group') {
50 ownerActor = await getOrCreateAPOwner(actorObject, actorUrl)
51 }
52
53 const creator = new APActorCreator(actorObject, ownerActor)
54 actor = await retryTransactionWrapper(creator.create.bind(creator))
55 created = true
56 accountPlaylistsUrl = actorObject.playlists
57 }
58
59 if (actor.Account) (actor as MActorAccountChannelIdActor).Account.Actor = actor
60 if (actor.VideoChannel) (actor as MActorAccountChannelIdActor).VideoChannel.Actor = actor
61
4ead40e7 62 const { actor: actorRefreshed, refreshed } = await refreshActorIfNeeded({ actor, fetchedType: fetchType })
136d7efd
C
63 if (!actorRefreshed) throw new Error('Actor ' + actor.url + ' does not exist anymore.')
64
65 await scheduleOutboxFetchIfNeeded(actor, created, refreshed, updateCollections)
66 await schedulePlaylistFetchIfNeeded(actor, created, accountPlaylistsUrl)
67
68 return actorRefreshed
69}
70
b5e1cd9a
C
71function getOrCreateAPOwner (actorObject: ActivityPubActor, actorUrl: string) {
72 const accountAttributedTo = actorObject.attributedTo.find(a => a.type === 'Person')
73 if (!accountAttributedTo) throw new Error('Cannot find account attributed to video channel ' + actorUrl)
74
75 if (checkUrlsSameHost(accountAttributedTo.id, actorUrl) !== true) {
76 throw new Error(`Account attributed to ${accountAttributedTo.id} does not have the same host than actor url ${actorUrl}`)
77 }
78
79 try {
80 // Don't recurse another time
81 const recurseIfNeeded = false
82 return getOrCreateAPActor(accountAttributedTo.id, 'all', recurseIfNeeded)
83 } catch (err) {
84 logger.error('Cannot get or create account attributed to video channel ' + actorUrl)
85 throw new Error(err)
86 }
87}
88
136d7efd
C
89// ---------------------------------------------------------------------------
90
91export {
b5e1cd9a 92 getOrCreateAPOwner,
136d7efd
C
93 getOrCreateAPActor
94}
95
96// ---------------------------------------------------------------------------
97
868fce62
C
98async function loadActorFromDB (actorUrl: string, fetchType: ActorLoadByUrlType) {
99 let actor = await loadActorByUrl(actorUrl, fetchType)
136d7efd
C
100
101 // Orphan actor (not associated to an account of channel) so recreate it
102 if (actor && (!actor.Account && !actor.VideoChannel)) {
103 await actor.destroy()
104 actor = null
105 }
106
107 return actor
108}
109
136d7efd
C
110async function scheduleOutboxFetchIfNeeded (actor: MActor, created: boolean, refreshed: boolean, updateCollections: boolean) {
111 if ((created === true || refreshed === true) && updateCollections === true) {
112 const payload = { uri: actor.outboxUrl, type: 'activity' as 'activity' }
113 await JobQueue.Instance.createJobWithPromise({ type: 'activitypub-http-fetcher', payload })
114 }
115}
116
117async function schedulePlaylistFetchIfNeeded (actor: MActorAccountId, created: boolean, accountPlaylistsUrl: string) {
118 // We created a new account: fetch the playlists
119 if (created === true && actor.Account && accountPlaylistsUrl) {
37a44fc9 120 const payload = { uri: accountPlaylistsUrl, type: 'account-playlists' as 'account-playlists' }
136d7efd
C
121 await JobQueue.Instance.createJobWithPromise({ type: 'activitypub-http-fetcher', payload })
122 }
123}