Send server announce when users upload a video
[oweals/peertube.git] / server / lib / activitypub / send-request.ts
1 import * as Sequelize from 'sequelize'
2
3 import { database as db } from '../../initializers'
4 import {
5   AccountInstance,
6   VideoInstance,
7   VideoChannelInstance
8 } from '../../models'
9 import { httpRequestJobScheduler } from '../jobs'
10 import { signObject, activityPubContextify } from '../../helpers'
11 import { Activity } from '../../../shared'
12 import { VideoAbuseInstance } from '../../models/video/video-abuse-interface'
13 import { getActivityPubUrl } from '../../helpers/activitypub'
14 import { logger } from '../../helpers/logger'
15
16 async function sendCreateVideoChannel (videoChannel: VideoChannelInstance, t: Sequelize.Transaction) {
17   const videoChannelObject = videoChannel.toActivityPubObject()
18   const data = await createActivityData(videoChannel.url, videoChannel.Account, videoChannelObject)
19
20   return broadcastToFollowers(data, [ videoChannel.Account ], t)
21 }
22
23 async function sendUpdateVideoChannel (videoChannel: VideoChannelInstance, t: Sequelize.Transaction) {
24   const videoChannelObject = videoChannel.toActivityPubObject()
25   const data = await updateActivityData(videoChannel.url, videoChannel.Account, videoChannelObject)
26
27   return broadcastToFollowers(data, [ videoChannel.Account ], t)
28 }
29
30 async function sendDeleteVideoChannel (videoChannel: VideoChannelInstance, t: Sequelize.Transaction) {
31   const data = await deleteActivityData(videoChannel.url, videoChannel.Account)
32
33   return broadcastToFollowers(data, [ videoChannel.Account ], t)
34 }
35
36 async function sendAddVideo (video: VideoInstance, t: Sequelize.Transaction) {
37   const videoObject = video.toActivityPubObject()
38   const data = await addActivityData(video.url, video.VideoChannel.Account, video.VideoChannel.url, videoObject)
39
40   return broadcastToFollowers(data, [ video.VideoChannel.Account ], t)
41 }
42
43 async function sendUpdateVideo (video: VideoInstance, t: Sequelize.Transaction) {
44   const videoObject = video.toActivityPubObject()
45   const data = await updateActivityData(video.url, video.VideoChannel.Account, videoObject)
46
47   return broadcastToFollowers(data, [ video.VideoChannel.Account ], t)
48 }
49
50 async function sendDeleteVideo (video: VideoInstance, t: Sequelize.Transaction) {
51   const data = await deleteActivityData(video.url, video.VideoChannel.Account)
52
53   return broadcastToFollowers(data, [ video.VideoChannel.Account ], t)
54 }
55
56 async function sendDeleteAccount (account: AccountInstance, t: Sequelize.Transaction) {
57   const data = await deleteActivityData(account.url, account)
58
59   return broadcastToFollowers(data, [ account ], t)
60 }
61
62 async function sendAnnounce (byAccount: AccountInstance, instance: VideoInstance | VideoChannelInstance, t: Sequelize.Transaction) {
63   const object = instance.toActivityPubObject()
64
65   let url = ''
66   let objectActorUrl: string
67   if ((instance as any).VideoChannel !== undefined) {
68     objectActorUrl = (instance as VideoInstance).VideoChannel.Account.url
69     url = getActivityPubUrl('video', instance.uuid) + '#announce'
70   } else {
71     objectActorUrl = (instance as VideoChannelInstance).Account.url
72     url = getActivityPubUrl('videoChannel', instance.uuid) + '#announce'
73   }
74
75   const objectWithActor = Object.assign(object, {
76     actor: objectActorUrl
77   })
78
79   const data = await announceActivityData(url, byAccount, objectWithActor)
80   return broadcastToFollowers(data, [ byAccount ], t)
81 }
82
83 async function sendVideoAbuse (
84   fromAccount: AccountInstance,
85   videoAbuse: VideoAbuseInstance,
86   video: VideoInstance,
87   t: Sequelize.Transaction
88 ) {
89   const url = getActivityPubUrl('videoAbuse', videoAbuse.id.toString())
90   const data = await createActivityData(url, fromAccount, video.url)
91
92   return unicastTo(data, video.VideoChannel.Account.sharedInboxUrl, t)
93 }
94
95 async function sendAccept (fromAccount: AccountInstance, toAccount: AccountInstance, t: Sequelize.Transaction) {
96   const data = await acceptActivityData(fromAccount)
97
98   return unicastTo(data, toAccount.inboxUrl, t)
99 }
100
101 async function sendFollow (fromAccount: AccountInstance, toAccount: AccountInstance, t: Sequelize.Transaction) {
102   const data = await followActivityData(toAccount.url, fromAccount)
103
104   return unicastTo(data, toAccount.inboxUrl, t)
105 }
106
107 // ---------------------------------------------------------------------------
108
109 export {
110   sendCreateVideoChannel,
111   sendUpdateVideoChannel,
112   sendDeleteVideoChannel,
113   sendAddVideo,
114   sendUpdateVideo,
115   sendDeleteVideo,
116   sendDeleteAccount,
117   sendAccept,
118   sendFollow,
119   sendVideoAbuse,
120   sendAnnounce
121 }
122
123 // ---------------------------------------------------------------------------
124
125 async function broadcastToFollowers (data: any, toAccountFollowers: AccountInstance[], t: Sequelize.Transaction) {
126   const toAccountFollowerIds = toAccountFollowers.map(a => a.id)
127   const result = await db.AccountFollow.listAcceptedFollowerSharedInboxUrls(toAccountFollowerIds)
128   if (result.data.length === 0) {
129     logger.info('Not broadcast because of 0 followers for %s.', toAccountFollowerIds.join(', '))
130     return
131   }
132
133   const jobPayload = {
134     uris: result.data,
135     body: data
136   }
137
138   return httpRequestJobScheduler.createJob(t, 'httpRequestBroadcastHandler', jobPayload)
139 }
140
141 async function unicastTo (data: any, toAccountUrl: string, t: Sequelize.Transaction) {
142   const jobPayload = {
143     uris: [ toAccountUrl ],
144     body: data
145   }
146
147   return httpRequestJobScheduler.createJob(t, 'httpRequestUnicastHandler', jobPayload)
148 }
149
150 function buildSignedActivity (byAccount: AccountInstance, data: Object) {
151   const activity = activityPubContextify(data)
152
153   return signObject(byAccount, activity) as Promise<Activity>
154 }
155
156 async function getPublicActivityTo (account: AccountInstance) {
157   const inboxUrls = await account.getFollowerSharedInboxUrls()
158
159   return inboxUrls.concat('https://www.w3.org/ns/activitystreams#Public')
160 }
161
162 async function createActivityData (url: string, byAccount: AccountInstance, object: any) {
163   const to = await getPublicActivityTo(byAccount)
164   const base = {
165     type: 'Create',
166     id: url,
167     actor: byAccount.url,
168     to,
169     object
170   }
171
172   return buildSignedActivity(byAccount, base)
173 }
174
175 async function updateActivityData (url: string, byAccount: AccountInstance, object: any) {
176   const to = await getPublicActivityTo(byAccount)
177   const base = {
178     type: 'Update',
179     id: url,
180     actor: byAccount.url,
181     to,
182     object
183   }
184
185   return buildSignedActivity(byAccount, base)
186 }
187
188 async function deleteActivityData (url: string, byAccount: AccountInstance) {
189   const base = {
190     type: 'Delete',
191     id: url,
192     actor: byAccount.url
193   }
194
195   return buildSignedActivity(byAccount, base)
196 }
197
198 async function addActivityData (url: string, byAccount: AccountInstance, target: string, object: any) {
199   const to = await getPublicActivityTo(byAccount)
200   const base = {
201     type: 'Add',
202     id: url,
203     actor: byAccount.url,
204     to,
205     object,
206     target
207   }
208
209   return buildSignedActivity(byAccount, base)
210 }
211
212 async function announceActivityData (url: string, byAccount: AccountInstance, object: any) {
213   const base = {
214     type: 'Announce',
215     id: url,
216     actor: byAccount.url,
217     object
218   }
219
220   return buildSignedActivity(byAccount, base)
221 }
222
223 async function followActivityData (url: string, byAccount: AccountInstance) {
224   const base = {
225     type: 'Follow',
226     id: byAccount.url,
227     actor: byAccount.url,
228     object: url
229   }
230
231   return buildSignedActivity(byAccount, base)
232 }
233
234 async function acceptActivityData (byAccount: AccountInstance) {
235   const base = {
236     type: 'Accept',
237     id: byAccount.url,
238     actor: byAccount.url
239   }
240
241   return buildSignedActivity(byAccount, base)
242 }