aboutsummaryrefslogtreecommitdiffhomepage
path: root/server/lib/activitypub
diff options
context:
space:
mode:
Diffstat (limited to 'server/lib/activitypub')
-rw-r--r--server/lib/activitypub/index.ts10
-rw-r--r--server/lib/activitypub/process/index.ts8
-rw-r--r--server/lib/activitypub/process/misc.ts (renamed from server/lib/activitypub/misc.ts)18
-rw-r--r--server/lib/activitypub/process/process-accept.ts (renamed from server/lib/activitypub/process-accept.ts)6
-rw-r--r--server/lib/activitypub/process/process-add.ts (renamed from server/lib/activitypub/process-add.ts)14
-rw-r--r--server/lib/activitypub/process/process-announce.ts (renamed from server/lib/activitypub/process-announce.ts)15
-rw-r--r--server/lib/activitypub/process/process-create.ts (renamed from server/lib/activitypub/process-create.ts)14
-rw-r--r--server/lib/activitypub/process/process-delete.ts (renamed from server/lib/activitypub/process-delete.ts)16
-rw-r--r--server/lib/activitypub/process/process-follow.ts (renamed from server/lib/activitypub/process-follow.ts)20
-rw-r--r--server/lib/activitypub/process/process-undo.ts31
-rw-r--r--server/lib/activitypub/process/process-update.ts (renamed from server/lib/activitypub/process-update.ts)18
-rw-r--r--server/lib/activitypub/send-request.ts275
-rw-r--r--server/lib/activitypub/send/index.ts7
-rw-r--r--server/lib/activitypub/send/misc.ts58
-rw-r--r--server/lib/activitypub/send/send-accept.ts34
-rw-r--r--server/lib/activitypub/send/send-add.ts38
-rw-r--r--server/lib/activitypub/send/send-announce.ts45
-rw-r--r--server/lib/activitypub/send/send-create.ts44
-rw-r--r--server/lib/activitypub/send/send-delete.ts53
-rw-r--r--server/lib/activitypub/send/send-follow.ts34
-rw-r--r--server/lib/activitypub/send/send-undo.ts39
-rw-r--r--server/lib/activitypub/send/send-update.ts55
22 files changed, 510 insertions, 342 deletions
diff --git a/server/lib/activitypub/index.ts b/server/lib/activitypub/index.ts
index e08108aac..1bea0a412 100644
--- a/server/lib/activitypub/index.ts
+++ b/server/lib/activitypub/index.ts
@@ -1,8 +1,2 @@
1export * from './process-accept' 1export * from './process'
2export * from './process-add' 2export * from './send'
3export * from './process-announce'
4export * from './process-create'
5export * from './process-delete'
6export * from './process-follow'
7export * from './process-update'
8export * from './send-request'
diff --git a/server/lib/activitypub/process/index.ts b/server/lib/activitypub/process/index.ts
new file mode 100644
index 000000000..e80b46b6f
--- /dev/null
+++ b/server/lib/activitypub/process/index.ts
@@ -0,0 +1,8 @@
1export * from './process-accept'
2export * from './process-add'
3export * from './process-announce'
4export * from './process-create'
5export * from './process-delete'
6export * from './process-follow'
7export * from './process-undo'
8export * from './process-update'
diff --git a/server/lib/activitypub/misc.ts b/server/lib/activitypub/process/misc.ts
index 4c210eb10..e90a793fc 100644
--- a/server/lib/activitypub/misc.ts
+++ b/server/lib/activitypub/process/misc.ts
@@ -1,13 +1,13 @@
1import * as magnetUtil from 'magnet-uri' 1import * as magnetUtil from 'magnet-uri'
2import { VideoTorrentObject } from '../../../shared' 2import { VideoTorrentObject } from '../../../../shared'
3import { VideoChannelObject } from '../../../shared/models/activitypub/objects/video-channel-object' 3import { VideoChannelObject } from '../../../../shared/models/activitypub/objects/video-channel-object'
4import { isVideoFileInfoHashValid } from '../../helpers/custom-validators/videos' 4import { isVideoFileInfoHashValid } from '../../../helpers/custom-validators/videos'
5import { ACTIVITY_PUB, VIDEO_MIMETYPE_EXT } from '../../initializers/constants' 5import { ACTIVITY_PUB, VIDEO_MIMETYPE_EXT } from '../../../initializers/constants'
6import { AccountInstance } from '../../models/account/account-interface' 6import { AccountInstance } from '../../../models/account/account-interface'
7import { VideoChannelInstance } from '../../models/video/video-channel-interface' 7import { VideoChannelInstance } from '../../../models/video/video-channel-interface'
8import { VideoFileAttributes } from '../../models/video/video-file-interface' 8import { VideoFileAttributes } from '../../../models/video/video-file-interface'
9import { VideoAttributes, VideoInstance } from '../../models/video/video-interface' 9import { VideoAttributes, VideoInstance } from '../../../models/video/video-interface'
10import { VideoPrivacy } from '../../../shared/models/videos/video-privacy.enum' 10import { VideoPrivacy } from '../../../../shared/models/videos/video-privacy.enum'
11 11
12function videoChannelActivityObjectToDBAttributes (videoChannelObject: VideoChannelObject, account: AccountInstance) { 12function videoChannelActivityObjectToDBAttributes (videoChannelObject: VideoChannelObject, account: AccountInstance) {
13 return { 13 return {
diff --git a/server/lib/activitypub/process-accept.ts b/server/lib/activitypub/process/process-accept.ts
index 9e0cd4032..e159c41b5 100644
--- a/server/lib/activitypub/process-accept.ts
+++ b/server/lib/activitypub/process/process-accept.ts
@@ -1,6 +1,6 @@
1import { ActivityAccept } from '../../../shared/models/activitypub/activity' 1import { ActivityAccept } from '../../../../shared/models/activitypub/activity'
2import { database as db } from '../../initializers' 2import { database as db } from '../../../initializers'
3import { AccountInstance } from '../../models/account/account-interface' 3import { AccountInstance } from '../../../models/account/account-interface'
4 4
5async function processAcceptActivity (activity: ActivityAccept, inboxAccount?: AccountInstance) { 5async function processAcceptActivity (activity: ActivityAccept, inboxAccount?: AccountInstance) {
6 if (inboxAccount === undefined) throw new Error('Need to accept on explicit inbox.') 6 if (inboxAccount === undefined) throw new Error('Need to accept on explicit inbox.')
diff --git a/server/lib/activitypub/process-add.ts b/server/lib/activitypub/process/process-add.ts
index e1769bee8..f064c1ab6 100644
--- a/server/lib/activitypub/process-add.ts
+++ b/server/lib/activitypub/process/process-add.ts
@@ -1,11 +1,11 @@
1import * as Bluebird from 'bluebird' 1import * as Bluebird from 'bluebird'
2import { VideoTorrentObject } from '../../../shared' 2import { VideoTorrentObject } from '../../../../shared'
3import { ActivityAdd } from '../../../shared/models/activitypub/activity' 3import { ActivityAdd } from '../../../../shared/models/activitypub/activity'
4import { generateThumbnailFromUrl, getOrCreateAccount, logger, retryTransactionWrapper } from '../../helpers' 4import { generateThumbnailFromUrl, getOrCreateAccount, logger, retryTransactionWrapper } from '../../../helpers'
5import { getOrCreateVideoChannel } from '../../helpers/activitypub' 5import { getOrCreateVideoChannel } from '../../../helpers/activitypub'
6import { database as db } from '../../initializers' 6import { database as db } from '../../../initializers'
7import { AccountInstance } from '../../models/account/account-interface' 7import { AccountInstance } from '../../../models/account/account-interface'
8import { VideoChannelInstance } from '../../models/video/video-channel-interface' 8import { VideoChannelInstance } from '../../../models/video/video-channel-interface'
9import { videoActivityObjectToDBAttributes, videoFileActivityUrlToDBAttributes } from './misc' 9import { videoActivityObjectToDBAttributes, videoFileActivityUrlToDBAttributes } from './misc'
10 10
11async function processAddActivity (activity: ActivityAdd) { 11async function processAddActivity (activity: ActivityAdd) {
diff --git a/server/lib/activitypub/process-announce.ts b/server/lib/activitypub/process/process-announce.ts
index eb38aecca..656db08a9 100644
--- a/server/lib/activitypub/process-announce.ts
+++ b/server/lib/activitypub/process/process-announce.ts
@@ -1,9 +1,9 @@
1import { ActivityAnnounce } from '../../../shared/models/activitypub/activity' 1import { ActivityAnnounce } from '../../../../shared/models/activitypub/activity'
2import { getOrCreateAccount } from '../../helpers/activitypub' 2import { getOrCreateAccount } from '../../../helpers/activitypub'
3import { logger } from '../../helpers/logger' 3import { logger } from '../../../helpers/logger'
4import { database as db } from '../../initializers/index' 4import { database as db } from '../../../initializers/index'
5import { VideoInstance } from '../../models/index' 5import { VideoInstance } from '../../../models/index'
6import { VideoChannelInstance } from '../../models/video/video-channel-interface' 6import { VideoChannelInstance } from '../../../models/video/video-channel-interface'
7import { processAddActivity } from './process-add' 7import { processAddActivity } from './process-add'
8import { processCreateActivity } from './process-create' 8import { processCreateActivity } from './process-create'
9 9
@@ -35,7 +35,8 @@ async function processAnnounceActivity (activity: ActivityAnnounce) {
35 'Unknown activity object type %s -> %s when announcing activity.', announcedActivity.type, announcedActivity.object.type, 35 'Unknown activity object type %s -> %s when announcing activity.', announcedActivity.type, announcedActivity.object.type,
36 { activity: activity.id } 36 { activity: activity.id }
37 ) 37 )
38 return Promise.resolve(undefined) 38
39 return undefined
39} 40}
40 41
41// --------------------------------------------------------------------------- 42// ---------------------------------------------------------------------------
diff --git a/server/lib/activitypub/process-create.ts b/server/lib/activitypub/process/process-create.ts
index de8e09adf..aac941a6c 100644
--- a/server/lib/activitypub/process-create.ts
+++ b/server/lib/activitypub/process/process-create.ts
@@ -1,9 +1,9 @@
1import { ActivityCreate, VideoChannelObject } from '../../../shared' 1import { ActivityCreate, VideoChannelObject } from '../../../../shared'
2import { VideoAbuseObject } from '../../../shared/models/activitypub/objects/video-abuse-object' 2import { VideoAbuseObject } from '../../../../shared/models/activitypub/objects/video-abuse-object'
3import { logger, retryTransactionWrapper } from '../../helpers' 3import { logger, retryTransactionWrapper } from '../../../helpers'
4import { getActivityPubUrl, getOrCreateAccount } from '../../helpers/activitypub' 4import { getOrCreateAccount, getVideoChannelActivityPubUrl } from '../../../helpers/activitypub'
5import { database as db } from '../../initializers' 5import { database as db } from '../../../initializers'
6import { AccountInstance } from '../../models/account/account-interface' 6import { AccountInstance } from '../../../models/account/account-interface'
7import { videoChannelActivityObjectToDBAttributes } from './misc' 7import { videoChannelActivityObjectToDBAttributes } from './misc'
8 8
9async function processCreateActivity (activity: ActivityCreate) { 9async function processCreateActivity (activity: ActivityCreate) {
@@ -47,7 +47,7 @@ function addRemoteVideoChannel (account: AccountInstance, videoChannelToCreateDa
47 47
48 const videoChannelData = videoChannelActivityObjectToDBAttributes(videoChannelToCreateData, account) 48 const videoChannelData = videoChannelActivityObjectToDBAttributes(videoChannelToCreateData, account)
49 videoChannel = db.VideoChannel.build(videoChannelData) 49 videoChannel = db.VideoChannel.build(videoChannelData)
50 videoChannel.url = getActivityPubUrl('videoChannel', videoChannel.uuid) 50 videoChannel.url = getVideoChannelActivityPubUrl(videoChannel)
51 51
52 videoChannel = await videoChannel.save({ transaction: t }) 52 videoChannel = await videoChannel.save({ transaction: t })
53 logger.info('Remote video channel with uuid %s inserted.', videoChannelToCreateData.uuid) 53 logger.info('Remote video channel with uuid %s inserted.', videoChannelToCreateData.uuid)
diff --git a/server/lib/activitypub/process-delete.ts b/server/lib/activitypub/process/process-delete.ts
index 0d5756e9c..af5d964d4 100644
--- a/server/lib/activitypub/process-delete.ts
+++ b/server/lib/activitypub/process/process-delete.ts
@@ -1,11 +1,11 @@
1import { ActivityDelete } from '../../../shared/models/activitypub/activity' 1import { ActivityDelete } from '../../../../shared/models/activitypub/activity'
2import { getOrCreateAccount } from '../../helpers/activitypub' 2import { getOrCreateAccount } from '../../../helpers/activitypub'
3import { retryTransactionWrapper } from '../../helpers/database-utils' 3import { retryTransactionWrapper } from '../../../helpers/database-utils'
4import { logger } from '../../helpers/logger' 4import { logger } from '../../../helpers/logger'
5import { database as db } from '../../initializers' 5import { database as db } from '../../../initializers'
6import { AccountInstance } from '../../models/account/account-interface' 6import { AccountInstance } from '../../../models/account/account-interface'
7import { VideoChannelInstance } from '../../models/video/video-channel-interface' 7import { VideoChannelInstance } from '../../../models/video/video-channel-interface'
8import { VideoInstance } from '../../models/video/video-interface' 8import { VideoInstance } from '../../../models/video/video-interface'
9 9
10async function processDeleteActivity (activity: ActivityDelete) { 10async function processDeleteActivity (activity: ActivityDelete) {
11 const account = await getOrCreateAccount(activity.actor) 11 const account = await getOrCreateAccount(activity.actor)
diff --git a/server/lib/activitypub/process-follow.ts b/server/lib/activitypub/process/process-follow.ts
index a805c0757..553639580 100644
--- a/server/lib/activitypub/process-follow.ts
+++ b/server/lib/activitypub/process/process-follow.ts
@@ -1,9 +1,9 @@
1import { ActivityFollow } from '../../../shared/models/activitypub/activity' 1import { ActivityFollow } from '../../../../shared/models/activitypub/activity'
2import { getOrCreateAccount, retryTransactionWrapper } from '../../helpers' 2import { getOrCreateAccount, retryTransactionWrapper } from '../../../helpers'
3import { database as db } from '../../initializers' 3import { database as db } from '../../../initializers'
4import { AccountInstance } from '../../models/account/account-interface' 4import { AccountInstance } from '../../../models/account/account-interface'
5import { sendAccept } from './send-request' 5import { logger } from '../../../helpers/logger'
6import { logger } from '../../helpers/logger' 6import { sendAccept } from '../send/send-accept'
7 7
8async function processFollowActivity (activity: ActivityFollow) { 8async function processFollowActivity (activity: ActivityFollow) {
9 const activityObject = activity.object 9 const activityObject = activity.object
@@ -33,10 +33,10 @@ async function follow (account: AccountInstance, targetAccountURL: string) {
33 await db.sequelize.transaction(async t => { 33 await db.sequelize.transaction(async t => {
34 const targetAccount = await db.Account.loadByUrl(targetAccountURL, t) 34 const targetAccount = await db.Account.loadByUrl(targetAccountURL, t)
35 35
36 if (targetAccount === undefined) throw new Error('Unknown account') 36 if (!targetAccount) throw new Error('Unknown account')
37 if (targetAccount.isOwned() === false) throw new Error('This is not a local account.') 37 if (targetAccount.isOwned() === false) throw new Error('This is not a local account.')
38 38
39 await db.AccountFollow.findOrCreate({ 39 const [ accountFollow ] = await db.AccountFollow.findOrCreate({
40 where: { 40 where: {
41 accountId: account.id, 41 accountId: account.id,
42 targetAccountId: targetAccount.id 42 targetAccountId: targetAccount.id
@@ -48,9 +48,11 @@ async function follow (account: AccountInstance, targetAccountURL: string) {
48 }, 48 },
49 transaction: t 49 transaction: t
50 }) 50 })
51 accountFollow.AccountFollower = account
52 accountFollow.AccountFollowing = targetAccount
51 53
52 // Target sends to account he accepted the follow request 54 // Target sends to account he accepted the follow request
53 return sendAccept(targetAccount, account, t) 55 return sendAccept(accountFollow, t)
54 }) 56 })
55 57
56 logger.info('Account uuid %s is followed by account %s.', account.url, targetAccountURL) 58 logger.info('Account uuid %s is followed by account %s.', account.url, targetAccountURL)
diff --git a/server/lib/activitypub/process/process-undo.ts b/server/lib/activitypub/process/process-undo.ts
new file mode 100644
index 000000000..5d09423e1
--- /dev/null
+++ b/server/lib/activitypub/process/process-undo.ts
@@ -0,0 +1,31 @@
1import { ActivityUndo } from '../../../../shared/models/activitypub/activity'
2import { logger } from '../../../helpers/logger'
3import { database as db } from '../../../initializers'
4
5async function processUndoActivity (activity: ActivityUndo) {
6 const activityToUndo = activity.object
7
8 if (activityToUndo.type === 'Follow') {
9 const follower = await db.Account.loadByUrl(activity.actor)
10 const following = await db.Account.loadByUrl(activityToUndo.object)
11 const accountFollow = await db.AccountFollow.loadByAccountAndTarget(follower.id, following.id)
12
13 if (!accountFollow) throw new Error(`'Unknown account follow (${follower.id} -> ${following.id}.`)
14
15 await accountFollow.destroy()
16
17 return undefined
18 }
19
20 logger.warn('Unknown activity object type %s -> %s when undo activity.', activityToUndo.type, { activity: activity.id })
21
22 return undefined
23}
24
25// ---------------------------------------------------------------------------
26
27export {
28 processUndoActivity
29}
30
31// ---------------------------------------------------------------------------
diff --git a/server/lib/activitypub/process-update.ts b/server/lib/activitypub/process/process-update.ts
index a9aa5eeb4..a3bfb1baf 100644
--- a/server/lib/activitypub/process-update.ts
+++ b/server/lib/activitypub/process/process-update.ts
@@ -1,12 +1,12 @@
1import { VideoChannelObject, VideoTorrentObject } from '../../../shared' 1import { VideoChannelObject, VideoTorrentObject } from '../../../../shared'
2import { ActivityUpdate } from '../../../shared/models/activitypub/activity' 2import { ActivityUpdate } from '../../../../shared/models/activitypub/activity'
3import { getOrCreateAccount } from '../../helpers/activitypub' 3import { getOrCreateAccount } from '../../../helpers/activitypub'
4import { retryTransactionWrapper } from '../../helpers/database-utils' 4import { retryTransactionWrapper } from '../../../helpers/database-utils'
5import { logger } from '../../helpers/logger' 5import { logger } from '../../../helpers/logger'
6import { resetSequelizeInstance } from '../../helpers/utils' 6import { resetSequelizeInstance } from '../../../helpers/utils'
7import { database as db } from '../../initializers' 7import { database as db } from '../../../initializers'
8import { AccountInstance } from '../../models/account/account-interface' 8import { AccountInstance } from '../../../models/account/account-interface'
9import { VideoInstance } from '../../models/video/video-interface' 9import { VideoInstance } from '../../../models/video/video-interface'
10import { videoActivityObjectToDBAttributes, videoFileActivityUrlToDBAttributes } from './misc' 10import { videoActivityObjectToDBAttributes, videoFileActivityUrlToDBAttributes } from './misc'
11import Bluebird = require('bluebird') 11import Bluebird = require('bluebird')
12 12
diff --git a/server/lib/activitypub/send-request.ts b/server/lib/activitypub/send-request.ts
deleted file mode 100644
index 261ff04ab..000000000
--- a/server/lib/activitypub/send-request.ts
+++ /dev/null
@@ -1,275 +0,0 @@
1import { Transaction } from 'sequelize'
2import {
3 ActivityAccept,
4 ActivityAdd,
5 ActivityCreate,
6 ActivityDelete,
7 ActivityFollow,
8 ActivityUpdate
9} from '../../../shared/models/activitypub/activity'
10import { getActivityPubUrl } from '../../helpers/activitypub'
11import { logger } from '../../helpers/logger'
12import { database as db } from '../../initializers'
13import { AccountInstance, VideoChannelInstance, VideoInstance } from '../../models'
14import { VideoAbuseInstance } from '../../models/video/video-abuse-interface'
15import { activitypubHttpJobScheduler } from '../jobs'
16import { ACTIVITY_PUB } from '../../initializers/constants'
17import { VideoPrivacy } from '../../../shared/models/videos/video-privacy.enum'
18
19async function sendCreateVideoChannel (videoChannel: VideoChannelInstance, t: Transaction) {
20 const byAccount = videoChannel.Account
21
22 const videoChannelObject = videoChannel.toActivityPubObject()
23 const data = await createActivityData(videoChannel.url, byAccount, videoChannelObject)
24
25 return broadcastToFollowers(data, byAccount, [ byAccount ], t)
26}
27
28async function sendUpdateVideoChannel (videoChannel: VideoChannelInstance, t: Transaction) {
29 const byAccount = videoChannel.Account
30
31 const videoChannelObject = videoChannel.toActivityPubObject()
32 const data = await updateActivityData(videoChannel.url, byAccount, videoChannelObject)
33
34 const accountsInvolved = await db.VideoChannelShare.loadAccountsByShare(videoChannel.id)
35 accountsInvolved.push(byAccount)
36
37 return broadcastToFollowers(data, byAccount, accountsInvolved, t)
38}
39
40async function sendDeleteVideoChannel (videoChannel: VideoChannelInstance, t: Transaction) {
41 const byAccount = videoChannel.Account
42
43 const data = await deleteActivityData(videoChannel.url, byAccount)
44
45 const accountsInvolved = await db.VideoChannelShare.loadAccountsByShare(videoChannel.id)
46 accountsInvolved.push(byAccount)
47
48 return broadcastToFollowers(data, byAccount, accountsInvolved, t)
49}
50
51async function sendAddVideo (video: VideoInstance, t: Transaction) {
52 const byAccount = video.VideoChannel.Account
53
54 const videoObject = video.toActivityPubObject()
55 const data = await addActivityData(video.url, byAccount, video, video.VideoChannel.url, videoObject)
56
57 return broadcastToFollowers(data, byAccount, [ byAccount ], t)
58}
59
60async function sendUpdateVideo (video: VideoInstance, t: Transaction) {
61 const byAccount = video.VideoChannel.Account
62
63 const videoObject = video.toActivityPubObject()
64 const data = await updateActivityData(video.url, byAccount, videoObject)
65
66 const accountsInvolved = await db.VideoShare.loadAccountsByShare(video.id)
67 accountsInvolved.push(byAccount)
68
69 return broadcastToFollowers(data, byAccount, accountsInvolved, t)
70}
71
72async function sendDeleteVideo (video: VideoInstance, t: Transaction) {
73 const byAccount = video.VideoChannel.Account
74
75 const data = await deleteActivityData(video.url, byAccount)
76
77 const accountsInvolved = await db.VideoShare.loadAccountsByShare(video.id)
78 accountsInvolved.push(byAccount)
79
80 return broadcastToFollowers(data, byAccount, accountsInvolved, t)
81}
82
83async function sendDeleteAccount (account: AccountInstance, t: Transaction) {
84 const data = await deleteActivityData(account.url, account)
85
86 return broadcastToFollowers(data, account, [ account ], t)
87}
88
89async function sendVideoChannelAnnounce (byAccount: AccountInstance, videoChannel: VideoChannelInstance, t: Transaction) {
90 const url = getActivityPubUrl('videoChannel', videoChannel.uuid) + '#announce'
91 const announcedActivity = await createActivityData(url, videoChannel.Account, videoChannel.toActivityPubObject())
92
93 const data = await announceActivityData(url, byAccount, announcedActivity)
94 return broadcastToFollowers(data, byAccount, [ byAccount ], t)
95}
96
97async function sendVideoAnnounce (byAccount: AccountInstance, video: VideoInstance, t: Transaction) {
98 const url = getActivityPubUrl('video', video.uuid) + '#announce'
99
100 const videoChannel = video.VideoChannel
101 const announcedActivity = await addActivityData(url, videoChannel.Account, video, videoChannel.url, video.toActivityPubObject())
102
103 const data = await announceActivityData(url, byAccount, announcedActivity)
104 return broadcastToFollowers(data, byAccount, [ byAccount ], t)
105}
106
107async function sendVideoAbuse (byAccount: AccountInstance, videoAbuse: VideoAbuseInstance, video: VideoInstance, t: Transaction) {
108 const url = getActivityPubUrl('videoAbuse', videoAbuse.id.toString())
109 const data = await createActivityData(url, byAccount, videoAbuse.toActivityPubObject())
110
111 return unicastTo(data, byAccount, video.VideoChannel.Account.sharedInboxUrl, t)
112}
113
114async function sendAccept (byAccount: AccountInstance, toAccount: AccountInstance, t: Transaction) {
115 const data = await acceptActivityData(byAccount)
116
117 return unicastTo(data, byAccount, toAccount.inboxUrl, t)
118}
119
120async function sendFollow (byAccount: AccountInstance, toAccount: AccountInstance, t: Transaction) {
121 const data = await followActivityData(toAccount.url, byAccount)
122
123 return unicastTo(data, byAccount, toAccount.inboxUrl, t)
124}
125
126// ---------------------------------------------------------------------------
127
128export {
129 sendCreateVideoChannel,
130 sendUpdateVideoChannel,
131 sendDeleteVideoChannel,
132 sendAddVideo,
133 sendUpdateVideo,
134 sendDeleteVideo,
135 sendDeleteAccount,
136 sendAccept,
137 sendFollow,
138 sendVideoAbuse,
139 sendVideoChannelAnnounce,
140 sendVideoAnnounce
141}
142
143// ---------------------------------------------------------------------------
144
145async function broadcastToFollowers (data: any, byAccount: AccountInstance, toAccountFollowers: AccountInstance[], t: Transaction) {
146 const toAccountFollowerIds = toAccountFollowers.map(a => a.id)
147 const result = await db.AccountFollow.listAcceptedFollowerSharedInboxUrls(toAccountFollowerIds)
148 if (result.data.length === 0) {
149 logger.info('Not broadcast because of 0 followers for %s.', toAccountFollowerIds.join(', '))
150 return undefined
151 }
152
153 const jobPayload = {
154 uris: result.data,
155 signatureAccountId: byAccount.id,
156 body: data
157 }
158
159 return activitypubHttpJobScheduler.createJob(t, 'activitypubHttpBroadcastHandler', jobPayload)
160}
161
162async function unicastTo (data: any, byAccount: AccountInstance, toAccountUrl: string, t: Transaction) {
163 const jobPayload = {
164 uris: [ toAccountUrl ],
165 signatureAccountId: byAccount.id,
166 body: data
167 }
168
169 return activitypubHttpJobScheduler.createJob(t, 'activitypubHttpUnicastHandler', jobPayload)
170}
171
172async function getAudience (accountSender: AccountInstance, isPublic = true) {
173 const followerInboxUrls = await accountSender.getFollowerSharedInboxUrls()
174
175 // Thanks Mastodon: https://github.com/tootsuite/mastodon/blob/master/app/lib/activitypub/tag_manager.rb#L47
176 let to = []
177 let cc = []
178
179 if (isPublic) {
180 to = [ ACTIVITY_PUB.PUBLIC ]
181 cc = followerInboxUrls
182 } else { // Unlisted
183 to = followerInboxUrls
184 cc = [ ACTIVITY_PUB.PUBLIC ]
185 }
186
187 return { to, cc }
188}
189
190async function createActivityData (url: string, byAccount: AccountInstance, object: any) {
191 const { to, cc } = await getAudience(byAccount)
192 const activity: ActivityCreate = {
193 type: 'Create',
194 id: url,
195 actor: byAccount.url,
196 to,
197 cc,
198 object
199 }
200
201 return activity
202}
203
204async function updateActivityData (url: string, byAccount: AccountInstance, object: any) {
205 const { to, cc } = await getAudience(byAccount)
206 const activity: ActivityUpdate = {
207 type: 'Update',
208 id: url,
209 actor: byAccount.url,
210 to,
211 cc,
212 object
213 }
214
215 return activity
216}
217
218async function deleteActivityData (url: string, byAccount: AccountInstance) {
219 const activity: ActivityDelete = {
220 type: 'Delete',
221 id: url,
222 actor: byAccount.url
223 }
224
225 return activity
226}
227
228async function addActivityData (url: string, byAccount: AccountInstance, video: VideoInstance, target: string, object: any) {
229 const videoPublic = video.privacy === VideoPrivacy.PUBLIC
230
231 const { to, cc } = await getAudience(byAccount, videoPublic)
232 const activity: ActivityAdd = {
233 type: 'Add',
234 id: url,
235 actor: byAccount.url,
236 to,
237 cc,
238 object,
239 target
240 }
241
242 return activity
243}
244
245async function announceActivityData (url: string, byAccount: AccountInstance, object: any) {
246 const activity = {
247 type: 'Announce',
248 id: url,
249 actor: byAccount.url,
250 object
251 }
252
253 return activity
254}
255
256async function followActivityData (url: string, byAccount: AccountInstance) {
257 const activity: ActivityFollow = {
258 type: 'Follow',
259 id: byAccount.url,
260 actor: byAccount.url,
261 object: url
262 }
263
264 return activity
265}
266
267async function acceptActivityData (byAccount: AccountInstance) {
268 const activity: ActivityAccept = {
269 type: 'Accept',
270 id: byAccount.url,
271 actor: byAccount.url
272 }
273
274 return activity
275}
diff --git a/server/lib/activitypub/send/index.ts b/server/lib/activitypub/send/index.ts
new file mode 100644
index 000000000..5f15dd4b5
--- /dev/null
+++ b/server/lib/activitypub/send/index.ts
@@ -0,0 +1,7 @@
1export * from './send-accept'
2export * from './send-add'
3export * from './send-announce'
4export * from './send-create'
5export * from './send-delete'
6export * from './send-follow'
7export * from './send-update'
diff --git a/server/lib/activitypub/send/misc.ts b/server/lib/activitypub/send/misc.ts
new file mode 100644
index 000000000..bea955b67
--- /dev/null
+++ b/server/lib/activitypub/send/misc.ts
@@ -0,0 +1,58 @@
1import { Transaction } from 'sequelize'
2import { logger } from '../../../helpers/logger'
3import { ACTIVITY_PUB, database as db } from '../../../initializers'
4import { AccountInstance } from '../../../models/account/account-interface'
5import { activitypubHttpJobScheduler } from '../../jobs/activitypub-http-job-scheduler/activitypub-http-job-scheduler'
6
7async function broadcastToFollowers (data: any, byAccount: AccountInstance, toAccountFollowers: AccountInstance[], t: Transaction) {
8 const toAccountFollowerIds = toAccountFollowers.map(a => a.id)
9 const result = await db.AccountFollow.listAcceptedFollowerSharedInboxUrls(toAccountFollowerIds)
10 if (result.data.length === 0) {
11 logger.info('Not broadcast because of 0 followers for %s.', toAccountFollowerIds.join(', '))
12 return undefined
13 }
14
15 const jobPayload = {
16 uris: result.data,
17 signatureAccountId: byAccount.id,
18 body: data
19 }
20
21 return activitypubHttpJobScheduler.createJob(t, 'activitypubHttpBroadcastHandler', jobPayload)
22}
23
24async function unicastTo (data: any, byAccount: AccountInstance, toAccountUrl: string, t: Transaction) {
25 const jobPayload = {
26 uris: [ toAccountUrl ],
27 signatureAccountId: byAccount.id,
28 body: data
29 }
30
31 return activitypubHttpJobScheduler.createJob(t, 'activitypubHttpUnicastHandler', jobPayload)
32}
33
34async function getAudience (accountSender: AccountInstance, isPublic = true) {
35 const followerInboxUrls = await accountSender.getFollowerSharedInboxUrls()
36
37 // Thanks Mastodon: https://github.com/tootsuite/mastodon/blob/master/app/lib/activitypub/tag_manager.rb#L47
38 let to = []
39 let cc = []
40
41 if (isPublic) {
42 to = [ ACTIVITY_PUB.PUBLIC ]
43 cc = followerInboxUrls
44 } else { // Unlisted
45 to = followerInboxUrls
46 cc = [ ACTIVITY_PUB.PUBLIC ]
47 }
48
49 return { to, cc }
50}
51
52// ---------------------------------------------------------------------------
53
54export {
55 broadcastToFollowers,
56 unicastTo,
57 getAudience
58}
diff --git a/server/lib/activitypub/send/send-accept.ts b/server/lib/activitypub/send/send-accept.ts
new file mode 100644
index 000000000..0324a30fa
--- /dev/null
+++ b/server/lib/activitypub/send/send-accept.ts
@@ -0,0 +1,34 @@
1import { Transaction } from 'sequelize'
2import { ActivityAccept } from '../../../../shared/models/activitypub/activity'
3import { AccountInstance } from '../../../models'
4import { AccountFollowInstance } from '../../../models/account/account-follow-interface'
5import { unicastTo } from './misc'
6import { getAccountFollowAcceptActivityPubUrl } from '../../../helpers/activitypub'
7
8async function sendAccept (accountFollow: AccountFollowInstance, t: Transaction) {
9 const follower = accountFollow.AccountFollower
10 const me = accountFollow.AccountFollowing
11
12 const url = getAccountFollowAcceptActivityPubUrl(accountFollow)
13 const data = await acceptActivityData(url, me)
14
15 return unicastTo(data, me, follower.inboxUrl, t)
16}
17
18// ---------------------------------------------------------------------------
19
20export {
21 sendAccept
22}
23
24// ---------------------------------------------------------------------------
25
26async function acceptActivityData (url: string, byAccount: AccountInstance) {
27 const activity: ActivityAccept = {
28 type: 'Accept',
29 id: url,
30 actor: byAccount.url
31 }
32
33 return activity
34}
diff --git a/server/lib/activitypub/send/send-add.ts b/server/lib/activitypub/send/send-add.ts
new file mode 100644
index 000000000..3012b7533
--- /dev/null
+++ b/server/lib/activitypub/send/send-add.ts
@@ -0,0 +1,38 @@
1import { Transaction } from 'sequelize'
2import { ActivityAdd } from '../../../../shared/models/activitypub/activity'
3import { VideoPrivacy } from '../../../../shared/models/videos/video-privacy.enum'
4import { AccountInstance, VideoInstance } from '../../../models'
5import { broadcastToFollowers, getAudience } from './misc'
6
7async function sendAddVideo (video: VideoInstance, t: Transaction) {
8 const byAccount = video.VideoChannel.Account
9
10 const videoObject = video.toActivityPubObject()
11 const data = await addActivityData(video.url, byAccount, video, video.VideoChannel.url, videoObject)
12
13 return broadcastToFollowers(data, byAccount, [ byAccount ], t)
14}
15
16async function addActivityData (url: string, byAccount: AccountInstance, video: VideoInstance, target: string, object: any) {
17 const videoPublic = video.privacy === VideoPrivacy.PUBLIC
18
19 const { to, cc } = await getAudience(byAccount, videoPublic)
20 const activity: ActivityAdd = {
21 type: 'Add',
22 id: url,
23 actor: byAccount.url,
24 to,
25 cc,
26 object,
27 target
28 }
29
30 return activity
31}
32
33// ---------------------------------------------------------------------------
34
35export {
36 addActivityData,
37 sendAddVideo
38}
diff --git a/server/lib/activitypub/send/send-announce.ts b/server/lib/activitypub/send/send-announce.ts
new file mode 100644
index 000000000..b9217e4f6
--- /dev/null
+++ b/server/lib/activitypub/send/send-announce.ts
@@ -0,0 +1,45 @@
1import { Transaction } from 'sequelize'
2import { AccountInstance, VideoInstance } from '../../../models'
3import { VideoChannelInstance } from '../../../models/video/video-channel-interface'
4import { broadcastToFollowers } from './misc'
5import { addActivityData } from './send-add'
6import { createActivityData } from './send-create'
7import { getAnnounceActivityPubUrl } from '../../../helpers/activitypub'
8
9async function sendVideoAnnounce (byAccount: AccountInstance, video: VideoInstance, t: Transaction) {
10 const url = getAnnounceActivityPubUrl(video.url, byAccount)
11
12 const videoChannel = video.VideoChannel
13 const announcedActivity = await addActivityData(url, videoChannel.Account, video, videoChannel.url, video.toActivityPubObject())
14
15 const data = await announceActivityData(url, byAccount, announcedActivity)
16 return broadcastToFollowers(data, byAccount, [ byAccount ], t)
17}
18
19async function sendVideoChannelAnnounce (byAccount: AccountInstance, videoChannel: VideoChannelInstance, t: Transaction) {
20 const url = getAnnounceActivityPubUrl(videoChannel.url, byAccount)
21 const announcedActivity = await createActivityData(url, videoChannel.Account, videoChannel.toActivityPubObject())
22
23 const data = await announceActivityData(url, byAccount, announcedActivity)
24 return broadcastToFollowers(data, byAccount, [ byAccount ], t)
25}
26
27// ---------------------------------------------------------------------------
28
29export {
30 sendVideoAnnounce,
31 sendVideoChannelAnnounce
32}
33
34// ---------------------------------------------------------------------------
35
36async function announceActivityData (url: string, byAccount: AccountInstance, object: any) {
37 const activity = {
38 type: 'Announce',
39 id: url,
40 actor: byAccount.url,
41 object
42 }
43
44 return activity
45}
diff --git a/server/lib/activitypub/send/send-create.ts b/server/lib/activitypub/send/send-create.ts
new file mode 100644
index 000000000..66bfeee89
--- /dev/null
+++ b/server/lib/activitypub/send/send-create.ts
@@ -0,0 +1,44 @@
1import { Transaction } from 'sequelize'
2import { ActivityCreate } from '../../../../shared/models/activitypub/activity'
3import { AccountInstance, VideoChannelInstance, VideoInstance } from '../../../models'
4import { VideoAbuseInstance } from '../../../models/video/video-abuse-interface'
5import { broadcastToFollowers, getAudience, unicastTo } from './misc'
6import { getVideoAbuseActivityPubUrl } from '../../../helpers/activitypub'
7
8async function sendCreateVideoChannel (videoChannel: VideoChannelInstance, t: Transaction) {
9 const byAccount = videoChannel.Account
10
11 const videoChannelObject = videoChannel.toActivityPubObject()
12 const data = await createActivityData(videoChannel.url, byAccount, videoChannelObject)
13
14 return broadcastToFollowers(data, byAccount, [ byAccount ], t)
15}
16
17async function sendVideoAbuse (byAccount: AccountInstance, videoAbuse: VideoAbuseInstance, video: VideoInstance, t: Transaction) {
18 const url = getVideoAbuseActivityPubUrl(videoAbuse)
19 const data = await createActivityData(url, byAccount, videoAbuse.toActivityPubObject())
20
21 return unicastTo(data, byAccount, video.VideoChannel.Account.sharedInboxUrl, t)
22}
23
24async function createActivityData (url: string, byAccount: AccountInstance, object: any) {
25 const { to, cc } = await getAudience(byAccount)
26 const activity: ActivityCreate = {
27 type: 'Create',
28 id: url,
29 actor: byAccount.url,
30 to,
31 cc,
32 object
33 }
34
35 return activity
36}
37
38// ---------------------------------------------------------------------------
39
40export {
41 sendCreateVideoChannel,
42 sendVideoAbuse,
43 createActivityData
44}
diff --git a/server/lib/activitypub/send/send-delete.ts b/server/lib/activitypub/send/send-delete.ts
new file mode 100644
index 000000000..5be0e2d24
--- /dev/null
+++ b/server/lib/activitypub/send/send-delete.ts
@@ -0,0 +1,53 @@
1import { Transaction } from 'sequelize'
2import { ActivityDelete } from '../../../../shared/models/activitypub/activity'
3import { database as db } from '../../../initializers'
4import { AccountInstance, VideoChannelInstance, VideoInstance } from '../../../models'
5import { broadcastToFollowers } from './misc'
6
7async function sendDeleteVideoChannel (videoChannel: VideoChannelInstance, t: Transaction) {
8 const byAccount = videoChannel.Account
9
10 const data = await deleteActivityData(videoChannel.url, byAccount)
11
12 const accountsInvolved = await db.VideoChannelShare.loadAccountsByShare(videoChannel.id)
13 accountsInvolved.push(byAccount)
14
15 return broadcastToFollowers(data, byAccount, accountsInvolved, t)
16}
17
18async function sendDeleteVideo (video: VideoInstance, t: Transaction) {
19 const byAccount = video.VideoChannel.Account
20
21 const data = await deleteActivityData(video.url, byAccount)
22
23 const accountsInvolved = await db.VideoShare.loadAccountsByShare(video.id)
24 accountsInvolved.push(byAccount)
25
26 return broadcastToFollowers(data, byAccount, accountsInvolved, t)
27}
28
29async function sendDeleteAccount (account: AccountInstance, t: Transaction) {
30 const data = await deleteActivityData(account.url, account)
31
32 return broadcastToFollowers(data, account, [ account ], t)
33}
34
35// ---------------------------------------------------------------------------
36
37export {
38 sendDeleteVideoChannel,
39 sendDeleteVideo,
40 sendDeleteAccount
41}
42
43// ---------------------------------------------------------------------------
44
45async function deleteActivityData (url: string, byAccount: AccountInstance) {
46 const activity: ActivityDelete = {
47 type: 'Delete',
48 id: url,
49 actor: byAccount.url
50 }
51
52 return activity
53}
diff --git a/server/lib/activitypub/send/send-follow.ts b/server/lib/activitypub/send/send-follow.ts
new file mode 100644
index 000000000..48d641c22
--- /dev/null
+++ b/server/lib/activitypub/send/send-follow.ts
@@ -0,0 +1,34 @@
1import { Transaction } from 'sequelize'
2import { ActivityFollow } from '../../../../shared/models/activitypub/activity'
3import { AccountInstance } from '../../../models'
4import { AccountFollowInstance } from '../../../models/account/account-follow-interface'
5import { unicastTo } from './misc'
6import { getAccountFollowActivityPubUrl } from '../../../helpers/activitypub'
7
8async function sendFollow (accountFollow: AccountFollowInstance, t: Transaction) {
9 const me = accountFollow.AccountFollower
10 const following = accountFollow.AccountFollowing
11
12 const url = getAccountFollowActivityPubUrl(accountFollow)
13 const data = await followActivityData(url, me, following)
14
15 return unicastTo(data, me, following.inboxUrl, t)
16}
17
18async function followActivityData (url: string, byAccount: AccountInstance, targetAccount: AccountInstance) {
19 const activity: ActivityFollow = {
20 type: 'Follow',
21 id: url,
22 actor: byAccount.url,
23 object: targetAccount.url
24 }
25
26 return activity
27}
28
29// ---------------------------------------------------------------------------
30
31export {
32 sendFollow,
33 followActivityData
34}
diff --git a/server/lib/activitypub/send/send-undo.ts b/server/lib/activitypub/send/send-undo.ts
new file mode 100644
index 000000000..39da824f3
--- /dev/null
+++ b/server/lib/activitypub/send/send-undo.ts
@@ -0,0 +1,39 @@
1import { Transaction } from 'sequelize'
2import { ActivityFollow, ActivityUndo } from '../../../../shared/models/activitypub/activity'
3import { AccountInstance } from '../../../models'
4import { AccountFollowInstance } from '../../../models/account/account-follow-interface'
5import { unicastTo } from './misc'
6import { getAccountFollowActivityPubUrl, getUndoActivityPubUrl } from '../../../helpers/activitypub'
7import { followActivityData } from './send-follow'
8
9async function sendUndoFollow (accountFollow: AccountFollowInstance, t: Transaction) {
10 const me = accountFollow.AccountFollower
11 const following = accountFollow.AccountFollowing
12
13 const followUrl = getAccountFollowActivityPubUrl(accountFollow)
14 const undoUrl = getUndoActivityPubUrl(followUrl)
15
16 const object = await followActivityData(followUrl, me, following)
17 const data = await undoActivityData(undoUrl, me, object)
18
19 return unicastTo(data, me, following.inboxUrl, t)
20}
21
22// ---------------------------------------------------------------------------
23
24export {
25 sendUndoFollow
26}
27
28// ---------------------------------------------------------------------------
29
30async function undoActivityData (url: string, byAccount: AccountInstance, object: ActivityFollow) {
31 const activity: ActivityUndo = {
32 type: 'Undo',
33 id: url,
34 actor: byAccount.url,
35 object
36 }
37
38 return activity
39}
diff --git a/server/lib/activitypub/send/send-update.ts b/server/lib/activitypub/send/send-update.ts
new file mode 100644
index 000000000..42738f973
--- /dev/null
+++ b/server/lib/activitypub/send/send-update.ts
@@ -0,0 +1,55 @@
1import { Transaction } from 'sequelize'
2import { ActivityUpdate } from '../../../../shared/models/activitypub/activity'
3import { getUpdateActivityPubUrl } from '../../../helpers/activitypub'
4import { database as db } from '../../../initializers'
5import { AccountInstance, VideoChannelInstance, VideoInstance } from '../../../models'
6import { broadcastToFollowers, getAudience } from './misc'
7
8async function sendUpdateVideoChannel (videoChannel: VideoChannelInstance, t: Transaction) {
9 const byAccount = videoChannel.Account
10
11 const url = getUpdateActivityPubUrl(videoChannel.url, videoChannel.updatedAt.toISOString())
12 const videoChannelObject = videoChannel.toActivityPubObject()
13 const data = await updateActivityData(url, byAccount, videoChannelObject)
14
15 const accountsInvolved = await db.VideoChannelShare.loadAccountsByShare(videoChannel.id)
16 accountsInvolved.push(byAccount)
17
18 return broadcastToFollowers(data, byAccount, accountsInvolved, t)
19}
20
21async function sendUpdateVideo (video: VideoInstance, t: Transaction) {
22 const byAccount = video.VideoChannel.Account
23
24 const url = getUpdateActivityPubUrl(video.url, video.updatedAt.toISOString())
25 const videoObject = video.toActivityPubObject()
26 const data = await updateActivityData(url, byAccount, videoObject)
27
28 const accountsInvolved = await db.VideoShare.loadAccountsByShare(video.id)
29 accountsInvolved.push(byAccount)
30
31 return broadcastToFollowers(data, byAccount, accountsInvolved, t)
32}
33
34// ---------------------------------------------------------------------------
35
36export {
37 sendUpdateVideoChannel,
38 sendUpdateVideo
39}
40
41// ---------------------------------------------------------------------------
42
43async function updateActivityData (url: string, byAccount: AccountInstance, object: any) {
44 const { to, cc } = await getAudience(byAccount)
45 const activity: ActivityUpdate = {
46 type: 'Update',
47 id: url,
48 actor: byAccount.url,
49 to,
50 cc,
51 object
52 }
53
54 return activity
55}