From 7a7724e66e4533523083e7336cd0d0c747c4a33b Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Mon, 13 Nov 2017 17:39:41 +0100 Subject: Handle follow/accept --- server/lib/activitypub/process-accept.ts | 27 ++++++++ server/lib/activitypub/process-delete.ts | 105 +++++++++++++++++++++++++++++++ server/lib/activitypub/process-follow.ts | 32 ++++++++++ server/lib/activitypub/send-request.ts | 26 ++++---- 4 files changed, 178 insertions(+), 12 deletions(-) create mode 100644 server/lib/activitypub/process-accept.ts create mode 100644 server/lib/activitypub/process-delete.ts create mode 100644 server/lib/activitypub/process-follow.ts (limited to 'server/lib') 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 @@ +import { ActivityAccept } from '../../../shared/models/activitypub/activity' +import { database as db } from '../../initializers' +import { AccountInstance } from '../../models/account/account-interface' + +async function processAcceptActivity (activity: ActivityAccept, inboxAccount?: AccountInstance) { + if (inboxAccount === undefined) throw new Error('Need to accept on explicit inbox.') + + const targetAccount = await db.Account.loadByUrl(activity.actor) + + return processFollow(inboxAccount, targetAccount) +} + +// --------------------------------------------------------------------------- + +export { + processAcceptActivity +} + +// --------------------------------------------------------------------------- + +async function processFollow (account: AccountInstance, targetAccount: AccountInstance) { + const follow = await db.AccountFollow.loadByAccountAndTarget(account.id, targetAccount.id) + if (!follow) throw new Error('Cannot find associated follow.') + + follow.set('state', 'accepted') + return follow.save() +} 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 @@ +import { ActivityDelete } from '../../../shared/models/activitypub/activity' +import { getOrCreateAccount } from '../../helpers/activitypub' +import { retryTransactionWrapper } from '../../helpers/database-utils' +import { logger } from '../../helpers/logger' +import { database as db } from '../../initializers' +import { AccountInstance } from '../../models/account/account-interface' +import { VideoChannelInstance } from '../../models/video/video-channel-interface' +import { VideoInstance } from '../../models/video/video-interface' + +async function processDeleteActivity (activity: ActivityDelete) { + const account = await getOrCreateAccount(activity.actor) + + if (account.url === activity.id) { + return processDeleteAccount(account) + } + + { + let videoObject = await db.Video.loadByUrl(activity.id) + if (videoObject !== undefined) { + return processDeleteVideo(account, videoObject) + } + } + + { + let videoChannelObject = await db.VideoChannel.loadByUrl(activity.id) + if (videoChannelObject !== undefined) { + return processDeleteVideoChannel(account, videoChannelObject) + } + } + + return undefined +} + +// --------------------------------------------------------------------------- + +export { + processDeleteActivity +} + +// --------------------------------------------------------------------------- + +async function processDeleteVideo (account: AccountInstance, videoToDelete: VideoInstance) { + const options = { + arguments: [ account, videoToDelete ], + errorMessage: 'Cannot remove the remote video with many retries.' + } + + await retryTransactionWrapper(deleteRemoteVideo, options) +} + +async function deleteRemoteVideo (account: AccountInstance, videoToDelete: VideoInstance) { + logger.debug('Removing remote video "%s".', videoToDelete.uuid) + + await db.sequelize.transaction(async t => { + if (videoToDelete.VideoChannel.Account.id !== account.id) { + throw new Error('Account ' + account.url + ' does not own video channel ' + videoToDelete.VideoChannel.url) + } + + await videoToDelete.destroy({ transaction: t }) + }) + + logger.info('Remote video with uuid %s removed.', videoToDelete.uuid) +} + +async function processDeleteVideoChannel (account: AccountInstance, videoChannelToRemove: VideoChannelInstance) { + const options = { + arguments: [ account, videoChannelToRemove ], + errorMessage: 'Cannot remove the remote video channel with many retries.' + } + + await retryTransactionWrapper(deleteRemoteVideoChannel, options) +} + +async function deleteRemoteVideoChannel (account: AccountInstance, videoChannelToRemove: VideoChannelInstance) { + logger.debug('Removing remote video channel "%s".', videoChannelToRemove.uuid) + + await db.sequelize.transaction(async t => { + if (videoChannelToRemove.Account.id !== account.id) { + throw new Error('Account ' + account.url + ' does not own video channel ' + videoChannelToRemove.url) + } + + await videoChannelToRemove.destroy({ transaction: t }) + }) + + logger.info('Remote video channel with uuid %s removed.', videoChannelToRemove.uuid) +} + +async function processDeleteAccount (accountToRemove: AccountInstance) { + const options = { + arguments: [ accountToRemove ], + errorMessage: 'Cannot remove the remote account with many retries.' + } + + await retryTransactionWrapper(deleteRemoteAccount, options) +} + +async function deleteRemoteAccount (accountToRemove: AccountInstance) { + logger.debug('Removing remote account "%s".', accountToRemove.uuid) + + await db.sequelize.transaction(async t => { + await accountToRemove.destroy({ transaction: t }) + }) + + logger.info('Remote account with uuid %s removed.', accountToRemove.uuid) +} 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 @@ +import { ActivityFollow } from '../../../shared/models/activitypub/activity' +import { getOrCreateAccount } from '../../helpers' +import { database as db } from '../../initializers' +import { AccountInstance } from '../../models/account/account-interface' + +async function processFollowActivity (activity: ActivityFollow) { + const activityObject = activity.object + const account = await getOrCreateAccount(activity.actor) + + return processFollow(account, activityObject) +} + +// --------------------------------------------------------------------------- + +export { + processFollowActivity +} + +// --------------------------------------------------------------------------- + +async function processFollow (account: AccountInstance, targetAccountURL: string) { + const targetAccount = await db.Account.loadByUrl(targetAccountURL) + + if (targetAccount === undefined) throw new Error('Unknown account') + if (targetAccount.isOwned() === false) throw new Error('This is not a local account.') + + return db.AccountFollow.create({ + accountId: account.id, + targetAccountId: targetAccount.id, + state: 'accepted' + }) +} 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 } function sendDeleteVideoChannel (videoChannel: VideoChannelInstance, t: Sequelize.Transaction) { - const videoChannelObject = videoChannel.toActivityPubObject() - const data = deleteActivityData(videoChannel.url, videoChannel.Account, videoChannelObject) + const data = deleteActivityData(videoChannel.url, videoChannel.Account) return broadcastToFollowers(data, videoChannel.Account, t) } @@ -46,12 +45,17 @@ function sendUpdateVideo (video: VideoInstance, t: Sequelize.Transaction) { } function sendDeleteVideo (video: VideoInstance, t: Sequelize.Transaction) { - const videoObject = video.toActivityPubObject() - const data = deleteActivityData(video.url, video.VideoChannel.Account, videoObject) + const data = deleteActivityData(video.url, video.VideoChannel.Account) return broadcastToFollowers(data, video.VideoChannel.Account, t) } +function sendDeleteAccount (account: AccountInstance, t: Sequelize.Transaction) { + const data = deleteActivityData(account.url, account) + + return broadcastToFollowers(data, account, t) +} + // --------------------------------------------------------------------------- export { @@ -60,13 +64,14 @@ export { sendDeleteVideoChannel, sendAddVideo, sendUpdateVideo, - sendDeleteVideo + sendDeleteVideo, + sendDeleteAccount } // --------------------------------------------------------------------------- async function broadcastToFollowers (data: any, fromAccount: AccountInstance, t: Sequelize.Transaction) { - const result = await db.Account.listFollowerUrlsForApi(fromAccount.name, 0) + const result = await db.Account.listFollowerUrlsForApi(fromAccount.id, 0) const jobPayload = { uris: result.data, @@ -114,14 +119,11 @@ async function updateActivityData (url: string, byAccount: AccountInstance, obje return buildSignedActivity(byAccount, base) } -async function deleteActivityData (url: string, byAccount: AccountInstance, object: any) { - const to = await getPublicActivityTo(byAccount) +async function deleteActivityData (url: string, byAccount: AccountInstance) { const base = { - type: 'Update', + type: 'Delete', id: url, - actor: byAccount.url, - to, - object + actor: byAccount.url } return buildSignedActivity(byAccount, base) -- cgit v1.2.3