diff options
Diffstat (limited to 'server/lib/activitypub')
-rw-r--r-- | server/lib/activitypub/process-accept.ts | 27 | ||||
-rw-r--r-- | server/lib/activitypub/process-delete.ts | 105 | ||||
-rw-r--r-- | server/lib/activitypub/process-follow.ts | 32 | ||||
-rw-r--r-- | server/lib/activitypub/send-request.ts | 26 |
4 files changed, 178 insertions, 12 deletions
diff --git a/server/lib/activitypub/process-accept.ts b/server/lib/activitypub/process-accept.ts new file mode 100644 index 000000000..37e42bd3a --- /dev/null +++ b/server/lib/activitypub/process-accept.ts | |||
@@ -0,0 +1,27 @@ | |||
1 | import { ActivityAccept } from '../../../shared/models/activitypub/activity' | ||
2 | import { database as db } from '../../initializers' | ||
3 | import { AccountInstance } from '../../models/account/account-interface' | ||
4 | |||
5 | async function processAcceptActivity (activity: ActivityAccept, inboxAccount?: AccountInstance) { | ||
6 | if (inboxAccount === undefined) throw new Error('Need to accept on explicit inbox.') | ||
7 | |||
8 | const targetAccount = await db.Account.loadByUrl(activity.actor) | ||
9 | |||
10 | return processFollow(inboxAccount, targetAccount) | ||
11 | } | ||
12 | |||
13 | // --------------------------------------------------------------------------- | ||
14 | |||
15 | export { | ||
16 | processAcceptActivity | ||
17 | } | ||
18 | |||
19 | // --------------------------------------------------------------------------- | ||
20 | |||
21 | async function processFollow (account: AccountInstance, targetAccount: AccountInstance) { | ||
22 | const follow = await db.AccountFollow.loadByAccountAndTarget(account.id, targetAccount.id) | ||
23 | if (!follow) throw new Error('Cannot find associated follow.') | ||
24 | |||
25 | follow.set('state', 'accepted') | ||
26 | return follow.save() | ||
27 | } | ||
diff --git a/server/lib/activitypub/process-delete.ts b/server/lib/activitypub/process-delete.ts new file mode 100644 index 000000000..377df432d --- /dev/null +++ b/server/lib/activitypub/process-delete.ts | |||
@@ -0,0 +1,105 @@ | |||
1 | import { ActivityDelete } from '../../../shared/models/activitypub/activity' | ||
2 | import { getOrCreateAccount } from '../../helpers/activitypub' | ||
3 | import { retryTransactionWrapper } from '../../helpers/database-utils' | ||
4 | import { logger } from '../../helpers/logger' | ||
5 | import { database as db } from '../../initializers' | ||
6 | import { AccountInstance } from '../../models/account/account-interface' | ||
7 | import { VideoChannelInstance } from '../../models/video/video-channel-interface' | ||
8 | import { VideoInstance } from '../../models/video/video-interface' | ||
9 | |||
10 | async function processDeleteActivity (activity: ActivityDelete) { | ||
11 | const account = await getOrCreateAccount(activity.actor) | ||
12 | |||
13 | if (account.url === activity.id) { | ||
14 | return processDeleteAccount(account) | ||
15 | } | ||
16 | |||
17 | { | ||
18 | let videoObject = await db.Video.loadByUrl(activity.id) | ||
19 | if (videoObject !== undefined) { | ||
20 | return processDeleteVideo(account, videoObject) | ||
21 | } | ||
22 | } | ||
23 | |||
24 | { | ||
25 | let videoChannelObject = await db.VideoChannel.loadByUrl(activity.id) | ||
26 | if (videoChannelObject !== undefined) { | ||
27 | return processDeleteVideoChannel(account, videoChannelObject) | ||
28 | } | ||
29 | } | ||
30 | |||
31 | return undefined | ||
32 | } | ||
33 | |||
34 | // --------------------------------------------------------------------------- | ||
35 | |||
36 | export { | ||
37 | processDeleteActivity | ||
38 | } | ||
39 | |||
40 | // --------------------------------------------------------------------------- | ||
41 | |||
42 | async function processDeleteVideo (account: AccountInstance, videoToDelete: VideoInstance) { | ||
43 | const options = { | ||
44 | arguments: [ account, videoToDelete ], | ||
45 | errorMessage: 'Cannot remove the remote video with many retries.' | ||
46 | } | ||
47 | |||
48 | await retryTransactionWrapper(deleteRemoteVideo, options) | ||
49 | } | ||
50 | |||
51 | async function deleteRemoteVideo (account: AccountInstance, videoToDelete: VideoInstance) { | ||
52 | logger.debug('Removing remote video "%s".', videoToDelete.uuid) | ||
53 | |||
54 | await db.sequelize.transaction(async t => { | ||
55 | if (videoToDelete.VideoChannel.Account.id !== account.id) { | ||
56 | throw new Error('Account ' + account.url + ' does not own video channel ' + videoToDelete.VideoChannel.url) | ||
57 | } | ||
58 | |||
59 | await videoToDelete.destroy({ transaction: t }) | ||
60 | }) | ||
61 | |||
62 | logger.info('Remote video with uuid %s removed.', videoToDelete.uuid) | ||
63 | } | ||
64 | |||
65 | async function processDeleteVideoChannel (account: AccountInstance, videoChannelToRemove: VideoChannelInstance) { | ||
66 | const options = { | ||
67 | arguments: [ account, videoChannelToRemove ], | ||
68 | errorMessage: 'Cannot remove the remote video channel with many retries.' | ||
69 | } | ||
70 | |||
71 | await retryTransactionWrapper(deleteRemoteVideoChannel, options) | ||
72 | } | ||
73 | |||
74 | async function deleteRemoteVideoChannel (account: AccountInstance, videoChannelToRemove: VideoChannelInstance) { | ||
75 | logger.debug('Removing remote video channel "%s".', videoChannelToRemove.uuid) | ||
76 | |||
77 | await db.sequelize.transaction(async t => { | ||
78 | if (videoChannelToRemove.Account.id !== account.id) { | ||
79 | throw new Error('Account ' + account.url + ' does not own video channel ' + videoChannelToRemove.url) | ||
80 | } | ||
81 | |||
82 | await videoChannelToRemove.destroy({ transaction: t }) | ||
83 | }) | ||
84 | |||
85 | logger.info('Remote video channel with uuid %s removed.', videoChannelToRemove.uuid) | ||
86 | } | ||
87 | |||
88 | async function processDeleteAccount (accountToRemove: AccountInstance) { | ||
89 | const options = { | ||
90 | arguments: [ accountToRemove ], | ||
91 | errorMessage: 'Cannot remove the remote account with many retries.' | ||
92 | } | ||
93 | |||
94 | await retryTransactionWrapper(deleteRemoteAccount, options) | ||
95 | } | ||
96 | |||
97 | async function deleteRemoteAccount (accountToRemove: AccountInstance) { | ||
98 | logger.debug('Removing remote account "%s".', accountToRemove.uuid) | ||
99 | |||
100 | await db.sequelize.transaction(async t => { | ||
101 | await accountToRemove.destroy({ transaction: t }) | ||
102 | }) | ||
103 | |||
104 | logger.info('Remote account with uuid %s removed.', accountToRemove.uuid) | ||
105 | } | ||
diff --git a/server/lib/activitypub/process-follow.ts b/server/lib/activitypub/process-follow.ts new file mode 100644 index 000000000..a04fc7994 --- /dev/null +++ b/server/lib/activitypub/process-follow.ts | |||
@@ -0,0 +1,32 @@ | |||
1 | import { ActivityFollow } from '../../../shared/models/activitypub/activity' | ||
2 | import { getOrCreateAccount } from '../../helpers' | ||
3 | import { database as db } from '../../initializers' | ||
4 | import { AccountInstance } from '../../models/account/account-interface' | ||
5 | |||
6 | async function processFollowActivity (activity: ActivityFollow) { | ||
7 | const activityObject = activity.object | ||
8 | const account = await getOrCreateAccount(activity.actor) | ||
9 | |||
10 | return processFollow(account, activityObject) | ||
11 | } | ||
12 | |||
13 | // --------------------------------------------------------------------------- | ||
14 | |||
15 | export { | ||
16 | processFollowActivity | ||
17 | } | ||
18 | |||
19 | // --------------------------------------------------------------------------- | ||
20 | |||
21 | async function processFollow (account: AccountInstance, targetAccountURL: string) { | ||
22 | const targetAccount = await db.Account.loadByUrl(targetAccountURL) | ||
23 | |||
24 | if (targetAccount === undefined) throw new Error('Unknown account') | ||
25 | if (targetAccount.isOwned() === false) throw new Error('This is not a local account.') | ||
26 | |||
27 | return db.AccountFollow.create({ | ||
28 | accountId: account.id, | ||
29 | targetAccountId: targetAccount.id, | ||
30 | state: 'accepted' | ||
31 | }) | ||
32 | } | ||
diff --git a/server/lib/activitypub/send-request.ts b/server/lib/activitypub/send-request.ts index 91101f5ad..ce9a96f14 100644 --- a/server/lib/activitypub/send-request.ts +++ b/server/lib/activitypub/send-request.ts | |||
@@ -25,8 +25,7 @@ function sendUpdateVideoChannel (videoChannel: VideoChannelInstance, t: Sequeliz | |||
25 | } | 25 | } |
26 | 26 | ||
27 | function sendDeleteVideoChannel (videoChannel: VideoChannelInstance, t: Sequelize.Transaction) { | 27 | function sendDeleteVideoChannel (videoChannel: VideoChannelInstance, t: Sequelize.Transaction) { |
28 | const videoChannelObject = videoChannel.toActivityPubObject() | 28 | const data = deleteActivityData(videoChannel.url, videoChannel.Account) |
29 | const data = deleteActivityData(videoChannel.url, videoChannel.Account, videoChannelObject) | ||
30 | 29 | ||
31 | return broadcastToFollowers(data, videoChannel.Account, t) | 30 | return broadcastToFollowers(data, videoChannel.Account, t) |
32 | } | 31 | } |
@@ -46,12 +45,17 @@ function sendUpdateVideo (video: VideoInstance, t: Sequelize.Transaction) { | |||
46 | } | 45 | } |
47 | 46 | ||
48 | function sendDeleteVideo (video: VideoInstance, t: Sequelize.Transaction) { | 47 | function sendDeleteVideo (video: VideoInstance, t: Sequelize.Transaction) { |
49 | const videoObject = video.toActivityPubObject() | 48 | const data = deleteActivityData(video.url, video.VideoChannel.Account) |
50 | const data = deleteActivityData(video.url, video.VideoChannel.Account, videoObject) | ||
51 | 49 | ||
52 | return broadcastToFollowers(data, video.VideoChannel.Account, t) | 50 | return broadcastToFollowers(data, video.VideoChannel.Account, t) |
53 | } | 51 | } |
54 | 52 | ||
53 | function sendDeleteAccount (account: AccountInstance, t: Sequelize.Transaction) { | ||
54 | const data = deleteActivityData(account.url, account) | ||
55 | |||
56 | return broadcastToFollowers(data, account, t) | ||
57 | } | ||
58 | |||
55 | // --------------------------------------------------------------------------- | 59 | // --------------------------------------------------------------------------- |
56 | 60 | ||
57 | export { | 61 | export { |
@@ -60,13 +64,14 @@ export { | |||
60 | sendDeleteVideoChannel, | 64 | sendDeleteVideoChannel, |
61 | sendAddVideo, | 65 | sendAddVideo, |
62 | sendUpdateVideo, | 66 | sendUpdateVideo, |
63 | sendDeleteVideo | 67 | sendDeleteVideo, |
68 | sendDeleteAccount | ||
64 | } | 69 | } |
65 | 70 | ||
66 | // --------------------------------------------------------------------------- | 71 | // --------------------------------------------------------------------------- |
67 | 72 | ||
68 | async function broadcastToFollowers (data: any, fromAccount: AccountInstance, t: Sequelize.Transaction) { | 73 | async function broadcastToFollowers (data: any, fromAccount: AccountInstance, t: Sequelize.Transaction) { |
69 | const result = await db.Account.listFollowerUrlsForApi(fromAccount.name, 0) | 74 | const result = await db.Account.listFollowerUrlsForApi(fromAccount.id, 0) |
70 | 75 | ||
71 | const jobPayload = { | 76 | const jobPayload = { |
72 | uris: result.data, | 77 | uris: result.data, |
@@ -114,14 +119,11 @@ async function updateActivityData (url: string, byAccount: AccountInstance, obje | |||
114 | return buildSignedActivity(byAccount, base) | 119 | return buildSignedActivity(byAccount, base) |
115 | } | 120 | } |
116 | 121 | ||
117 | async function deleteActivityData (url: string, byAccount: AccountInstance, object: any) { | 122 | async function deleteActivityData (url: string, byAccount: AccountInstance) { |
118 | const to = await getPublicActivityTo(byAccount) | ||
119 | const base = { | 123 | const base = { |
120 | type: 'Update', | 124 | type: 'Delete', |
121 | id: url, | 125 | id: url, |
122 | actor: byAccount.url, | 126 | actor: byAccount.url |
123 | to, | ||
124 | object | ||
125 | } | 127 | } |
126 | 128 | ||
127 | return buildSignedActivity(byAccount, base) | 129 | return buildSignedActivity(byAccount, base) |