]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blame - server/lib/activitypub/send/utils.ts
Fix missing delete cascade video -> channel
[github/Chocobozzz/PeerTube.git] / server / lib / activitypub / send / utils.ts
CommitLineData
54141398 1import { Transaction } from 'sequelize'
7d9ba5c0
C
2import { getServerActor } from '@server/models/application/application'
3import { ContextType } from '@shared/models/activitypub/context'
a2377d15 4import { Activity, ActivityAudience } from '../../../../shared/models/activitypub'
7d9ba5c0 5import { afterCommitIfTransaction } from '../../../helpers/database-utils'
da854ddd 6import { logger } from '../../../helpers/logger'
7d9ba5c0
C
7import { ActorModel } from '../../../models/actor/actor'
8import { ActorFollowModel } from '../../../models/actor/actor-follow'
9import { MActor, MActorId, MActorLight, MActorWithInboxes, MVideoAccountLight, MVideoId, MVideoImmutable } from '../../../types/models'
94a5ff8a 10import { JobQueue } from '../../job-queue'
a2377d15 11import { getActorsInvolvedInVideo, getAudienceFromFollowersOf, getRemoteVideoAudience } from '../audience'
9588d4f4 12
a2377d15 13async function sendVideoRelatedActivity (activityBuilder: (audience: ActivityAudience) => Activity, options: {
a1587156 14 byActor: MActorLight
2c8776fc 15 video: MVideoImmutable | MVideoAccountLight
f51c02c7 16 transaction?: Transaction
598edb8a 17 contextType?: ContextType
a2377d15 18}) {
598edb8a 19 const { byActor, video, transaction, contextType } = options
5224c394
C
20
21 const actorsInvolvedInVideo = await getActorsInvolvedInVideo(video, transaction)
a2377d15
C
22
23 // Send to origin
5224c394 24 if (video.isOwned() === false) {
2c8776fc
C
25 const accountActor = (video as MVideoAccountLight).VideoChannel?.Account?.Actor || await ActorModel.loadAccountActorByVideoId(video.id)
26
27 const audience = getRemoteVideoAudience(accountActor, actorsInvolvedInVideo)
a2377d15
C
28 const activity = activityBuilder(audience)
29
5224c394 30 return afterCommitIfTransaction(transaction, () => {
2c8776fc 31 return unicastTo(activity, byActor, accountActor.getSharedInbox(), contextType)
2284f202 32 })
a2377d15
C
33 }
34
35 // Send to followers
36 const audience = getAudienceFromFollowersOf(actorsInvolvedInVideo)
37 const activity = activityBuilder(audience)
38
5224c394 39 const actorsException = [ byActor ]
2284f202 40
598edb8a 41 return broadcastToFollowers(activity, byActor, actorsInvolvedInVideo, transaction, actorsException, contextType)
a2377d15
C
42}
43
9588d4f4
C
44async function forwardVideoRelatedActivity (
45 activity: Activity,
46 t: Transaction,
bdd428a6 47 followersException: MActorWithInboxes[],
943e5193 48 video: MVideoId
9588d4f4
C
49) {
50 // Mastodon does not add our announces in audience, so we forward to them manually
51 const additionalActors = await getActorsInvolvedInVideo(video, t)
52 const additionalFollowerUrls = additionalActors.map(a => a.followersUrl)
53
54 return forwardActivity(activity, t, followersException, additionalFollowerUrls)
55}
63c93323
C
56
57async function forwardActivity (
58 activity: Activity,
59 t: Transaction,
47581df0 60 followersException: MActorWithInboxes[] = [],
93ef8a9d 61 additionalFollowerUrls: string[] = []
63c93323 62) {
8e0fd45e
C
63 logger.info('Forwarding activity %s.', activity.id)
64
63c93323
C
65 const to = activity.to || []
66 const cc = activity.cc || []
67
93ef8a9d 68 const followersUrls = additionalFollowerUrls
63c93323
C
69 for (const dest of to.concat(cc)) {
70 if (dest.endsWith('/followers')) {
71 followersUrls.push(dest)
72 }
73 }
74
50d6de9c
C
75 const toActorFollowers = await ActorModel.listByFollowersUrls(followersUrls, t)
76 const uris = await computeFollowerUris(toActorFollowers, followersException, t)
63c93323
C
77
78 if (uris.length === 0) {
50d6de9c 79 logger.info('0 followers for %s, no forwarding.', toActorFollowers.map(a => a.id).join(', '))
df1966c9 80 return undefined
63c93323
C
81 }
82
83 logger.debug('Creating forwarding job.', { uris })
84
94a5ff8a 85 const payload = {
63c93323
C
86 uris,
87 body: activity
88 }
2284f202 89 return afterCommitIfTransaction(t, () => JobQueue.Instance.createJob({ type: 'activitypub-http-broadcast', payload }))
63c93323 90}
54141398 91
40ff5707
C
92async function broadcastToFollowers (
93 data: any,
453e83ea
C
94 byActor: MActorId,
95 toFollowersOf: MActorId[],
40ff5707 96 t: Transaction,
598edb8a
C
97 actorsException: MActorWithInboxes[] = [],
98 contextType?: ContextType
40ff5707 99) {
c48e82b5 100 const uris = await computeFollowerUris(toFollowersOf, actorsException, t)
2284f202 101
598edb8a 102 return afterCommitIfTransaction(t, () => broadcastTo(uris, data, byActor, contextType))
93ef8a9d
C
103}
104
105async function broadcastToActors (
106 data: any,
453e83ea
C
107 byActor: MActorId,
108 toActors: MActor[],
2284f202 109 t?: Transaction,
598edb8a
C
110 actorsException: MActorWithInboxes[] = [],
111 contextType?: ContextType
93ef8a9d
C
112) {
113 const uris = await computeUris(toActors, actorsException)
598edb8a 114 return afterCommitIfTransaction(t, () => broadcastTo(uris, data, byActor, contextType))
93ef8a9d
C
115}
116
598edb8a 117function broadcastTo (uris: string[], data: any, byActor: MActorId, contextType?: ContextType) {
93ef8a9d 118 if (uris.length === 0) return undefined
54141398 119
63c93323 120 logger.debug('Creating broadcast job.', { uris })
40ff5707 121
94a5ff8a 122 const payload = {
40ff5707 123 uris,
50d6de9c 124 signatureActorId: byActor.id,
598edb8a
C
125 body: data,
126 contextType
54141398
C
127 }
128
94a5ff8a 129 return JobQueue.Instance.createJob({ type: 'activitypub-http-broadcast', payload })
54141398
C
130}
131
598edb8a 132function unicastTo (data: any, byActor: MActorId, toActorUrl: string, contextType?: ContextType) {
50d6de9c 133 logger.debug('Creating unicast job.', { uri: toActorUrl })
63c93323 134
94a5ff8a
C
135 const payload = {
136 uri: toActorUrl,
50d6de9c 137 signatureActorId: byActor.id,
598edb8a
C
138 body: data,
139 contextType
54141398
C
140 }
141
2284f202 142 JobQueue.Instance.createJob({ type: 'activitypub-http-unicast', payload })
54141398
C
143}
144
e251f170 145// ---------------------------------------------------------------------------
54141398 146
e251f170
C
147export {
148 broadcastToFollowers,
149 unicastTo,
150 forwardActivity,
9588d4f4 151 broadcastToActors,
a2377d15
C
152 forwardVideoRelatedActivity,
153 sendVideoRelatedActivity
54141398
C
154}
155
e251f170 156// ---------------------------------------------------------------------------
e12a0092 157
47581df0 158async function computeFollowerUris (toFollowersOf: MActorId[], actorsException: MActorWithInboxes[], t: Transaction) {
c48e82b5 159 const toActorFollowerIds = toFollowersOf.map(a => a.id)
63c93323 160
50d6de9c 161 const result = await ActorFollowModel.listAcceptedFollowerSharedInboxUrls(toActorFollowerIds, t)
06a05d5f
C
162 const sharedInboxesException = await buildSharedInboxesException(actorsException)
163
bdd428a6 164 return result.data.filter(sharedInbox => sharedInboxesException.includes(sharedInbox) === false)
93ef8a9d
C
165}
166
47581df0 167async function computeUris (toActors: MActor[], actorsException: MActorWithInboxes[] = []) {
06a05d5f
C
168 const serverActor = await getServerActor()
169 const targetUrls = toActors
170 .filter(a => a.id !== serverActor.id) // Don't send to ourselves
47581df0 171 .map(a => a.getSharedInbox())
06a05d5f
C
172
173 const toActorSharedInboxesSet = new Set(targetUrls)
93ef8a9d 174
06a05d5f 175 const sharedInboxesException = await buildSharedInboxesException(actorsException)
93ef8a9d 176 return Array.from(toActorSharedInboxesSet)
bdd428a6 177 .filter(sharedInbox => sharedInboxesException.includes(sharedInbox) === false)
54141398 178}
06a05d5f 179
47581df0 180async function buildSharedInboxesException (actorsException: MActorWithInboxes[]) {
06a05d5f
C
181 const serverActor = await getServerActor()
182
183 return actorsException
47581df0 184 .map(f => f.getSharedInbox())
06a05d5f
C
185 .concat([ serverActor.sharedInboxUrl ])
186}