From 73c0809326670867642f8a36f68d8564e0e406b5 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Tue, 27 Mar 2018 10:26:52 +0200 Subject: Fix delete comment federation --- server/lib/activitypub/process/process-delete.ts | 15 ++++++--- server/lib/activitypub/send/misc.ts | 6 ++-- server/lib/activitypub/send/send-create.ts | 8 ++--- server/lib/activitypub/send/send-delete.ts | 40 +++++++++++++++++------- server/lib/activitypub/send/send-like.ts | 2 +- server/lib/activitypub/send/send-undo.ts | 2 +- server/lib/activitypub/send/send-update.ts | 2 +- server/tests/api/videos/multiple-servers.ts | 31 +++++++++++++++++- 8 files changed, 80 insertions(+), 26 deletions(-) (limited to 'server') diff --git a/server/lib/activitypub/process/process-delete.ts b/server/lib/activitypub/process/process-delete.ts index 03eadcbfc..b58f6c04a 100644 --- a/server/lib/activitypub/process/process-delete.ts +++ b/server/lib/activitypub/process/process-delete.ts @@ -8,6 +8,7 @@ import { VideoModel } from '../../../models/video/video' import { VideoChannelModel } from '../../../models/video/video-channel' import { VideoCommentModel } from '../../../models/video/video-comment' import { getOrCreateActorAndServerAndModel } from '../actor' +import { forwardActivity } from '../send/misc' async function processDeleteActivity (activity: ActivityDelete) { const objectUrl = typeof activity.object === 'string' ? activity.object : activity.object.id @@ -33,7 +34,7 @@ async function processDeleteActivity (activity: ActivityDelete) { { const videoCommentInstance = await VideoCommentModel.loadByUrlAndPopulateAccount(objectUrl) if (videoCommentInstance) { - return processDeleteVideoComment(actor, videoCommentInstance) + return processDeleteVideoComment(actor, videoCommentInstance, activity) } } @@ -116,21 +117,27 @@ async function deleteRemoteVideoChannel (videoChannelToRemove: VideoChannelModel logger.info('Remote video channel with uuid %s removed.', videoChannelToRemove.Actor.uuid) } -async function processDeleteVideoComment (actor: ActorModel, videoComment: VideoCommentModel) { +async function processDeleteVideoComment (byActor: ActorModel, videoComment: VideoCommentModel, activity: ActivityDelete) { const options = { - arguments: [ actor, videoComment ], + arguments: [ byActor, videoComment, activity ], errorMessage: 'Cannot remove the remote video comment with many retries.' } await retryTransactionWrapper(deleteRemoteVideoComment, options) } -function deleteRemoteVideoComment (actor: ActorModel, videoComment: VideoCommentModel) { +function deleteRemoteVideoComment (byActor: ActorModel, videoComment: VideoCommentModel, activity: ActivityDelete) { logger.debug('Removing remote video comment "%s".', videoComment.url) return sequelizeTypescript.transaction(async t => { await videoComment.destroy({ transaction: t }) + if (videoComment.Video.isOwned()) { + // Don't resend the activity to the sender + const exceptions = [ byActor ] + await forwardActivity(activity, t, exceptions) + } + logger.info('Remote video comment %s removed.', videoComment.url) }) } diff --git a/server/lib/activitypub/send/misc.ts b/server/lib/activitypub/send/misc.ts index b20fcf124..b2d968c9c 100644 --- a/server/lib/activitypub/send/misc.ts +++ b/server/lib/activitypub/send/misc.ts @@ -96,7 +96,7 @@ function getOriginVideoAudience (video: VideoModel, actorsInvolvedInVideo: Actor } } -function getOriginVideoCommentAudience ( +function getVideoCommentAudience ( videoComment: VideoCommentModel, threadParentComments: VideoCommentModel[], actorsInvolvedInVideo: ActorModel[], @@ -160,7 +160,7 @@ function buildAudience (followerInboxUrls: string[], isPublic = true) { return { to, cc } } -function audiencify (object: any, audience: ActivityAudience) { +function audiencify (object: T, audience: ActivityAudience) { return Object.assign(object, audience) } @@ -192,7 +192,7 @@ export { getObjectFollowersAudience, forwardActivity, audiencify, - getOriginVideoCommentAudience, + getVideoCommentAudience, computeUris, broadcastToActors } diff --git a/server/lib/activitypub/send/send-create.ts b/server/lib/activitypub/send/send-create.ts index 2fa008453..d73dd8d24 100644 --- a/server/lib/activitypub/send/send-create.ts +++ b/server/lib/activitypub/send/send-create.ts @@ -15,7 +15,7 @@ import { getAudience, getObjectFollowersAudience, getOriginVideoAudience, - getOriginVideoCommentAudience, + getVideoCommentAudience, unicastTo } from './misc' @@ -47,7 +47,7 @@ async function sendCreateVideoCommentToOrigin (comment: VideoCommentModel, t: Tr const actorsInvolvedInComment = await getActorsInvolvedInVideo(comment.Video, t) actorsInvolvedInComment.push(byActor) - const audience = getOriginVideoCommentAudience(comment, threadParentComments, actorsInvolvedInComment) + const audience = getVideoCommentAudience(comment, threadParentComments, actorsInvolvedInComment) const data = await createActivityData(comment.url, byActor, commentObject, t, audience) @@ -70,7 +70,7 @@ async function sendCreateVideoCommentToVideoFollowers (comment: VideoCommentMode const actorsInvolvedInComment = await getActorsInvolvedInVideo(comment.Video, t) actorsInvolvedInComment.push(byActor) - const audience = getOriginVideoCommentAudience(comment, threadParentComments, actorsInvolvedInComment) + const audience = getVideoCommentAudience(comment, threadParentComments, actorsInvolvedInComment, true) const data = await createActivityData(comment.url, byActor, commentObject, t, audience) // This was a reply, send it to the parent actors @@ -144,7 +144,7 @@ async function createActivityData ( } return audiencify({ - type: 'Create', + type: 'Create' as 'Create', id: url + '/activity', actor: byActor.url, object: audiencify(object, audience) diff --git a/server/lib/activitypub/send/send-delete.ts b/server/lib/activitypub/send/send-delete.ts index 9f1ea3bd0..bb5d6913c 100644 --- a/server/lib/activitypub/send/send-delete.ts +++ b/server/lib/activitypub/send/send-delete.ts @@ -1,11 +1,11 @@ import { Transaction } from 'sequelize' -import { ActivityDelete } from '../../../../shared/models/activitypub' +import { ActivityAudience, ActivityDelete } from '../../../../shared/models/activitypub' import { ActorModel } from '../../../models/activitypub/actor' import { VideoModel } from '../../../models/video/video' import { VideoCommentModel } from '../../../models/video/video-comment' import { VideoShareModel } from '../../../models/video/video-share' import { getDeleteActivityPubUrl } from '../url' -import { broadcastToFollowers } from './misc' +import { audiencify, broadcastToActors, broadcastToFollowers, getActorsInvolvedInVideo, getVideoCommentAudience, unicastTo } from './misc' async function sendDeleteVideo (video: VideoModel, t: Transaction) { const url = getDeleteActivityPubUrl(video.url) @@ -30,16 +30,30 @@ async function sendDeleteActor (byActor: ActorModel, t: Transaction) { } async function sendDeleteVideoComment (videoComment: VideoCommentModel, t: Transaction) { - const url = getDeleteActivityPubUrl(videoComment.url) + const isVideoOrigin = videoComment.Video.isOwned() + const url = getDeleteActivityPubUrl(videoComment.url) const byActor = videoComment.Account.Actor - const data = deleteActivityData(url, videoComment.url, byActor) + const threadParentComments = await VideoCommentModel.listThreadParentComments(videoComment, t) - const actorsInvolved = await VideoShareModel.loadActorsByShare(videoComment.Video.id, t) - actorsInvolved.push(videoComment.Video.VideoChannel.Account.Actor) - actorsInvolved.push(byActor) + const actorsInvolvedInComment = await getActorsInvolvedInVideo(videoComment.Video, t) + actorsInvolvedInComment.push(byActor) - return broadcastToFollowers(data, byActor, actorsInvolved, t) + const audience = getVideoCommentAudience(videoComment, threadParentComments, actorsInvolvedInComment, isVideoOrigin) + const data = deleteActivityData(url, videoComment.url, byActor, audience) + + // This was a reply, send it to the parent actors + const actorsException = [ byActor ] + await broadcastToActors(data, byActor, threadParentComments.map(c => c.Account.Actor), actorsException) + + // Broadcast to our followers + await broadcastToFollowers(data, byActor, [ byActor ], t) + + // Send to actors involved in the comment + if (isVideoOrigin) return broadcastToFollowers(data, byActor, actorsInvolvedInComment, t, actorsException) + + // Send to origin + return unicastTo(data, byActor, videoComment.Video.VideoChannel.Account.Actor.sharedInboxUrl) } // --------------------------------------------------------------------------- @@ -52,11 +66,15 @@ export { // --------------------------------------------------------------------------- -function deleteActivityData (url: string, object: string, byActor: ActorModel): ActivityDelete { - return { - type: 'Delete', +function deleteActivityData (url: string, object: string, byActor: ActorModel, audience?: ActivityAudience): ActivityDelete { + const activity = { + type: 'Delete' as 'Delete', id: url, actor: byActor.url, object } + + if (audience) return audiencify(activity, audience) + + return activity } diff --git a/server/lib/activitypub/send/send-like.ts b/server/lib/activitypub/send/send-like.ts index 78ed1aaf2..b01249e69 100644 --- a/server/lib/activitypub/send/send-like.ts +++ b/server/lib/activitypub/send/send-like.ts @@ -46,7 +46,7 @@ async function likeActivityData ( } return audiencify({ - type: 'Like', + type: 'Like' as 'Like', id: url, actor: byActor.url, object: video.url diff --git a/server/lib/activitypub/send/send-undo.ts b/server/lib/activitypub/send/send-undo.ts index 4a08b5ca1..41a500384 100644 --- a/server/lib/activitypub/send/send-undo.ts +++ b/server/lib/activitypub/send/send-undo.ts @@ -108,7 +108,7 @@ async function undoActivityData ( } return audiencify({ - type: 'Undo', + type: 'Undo' as 'Undo', id: url, actor: byActor.url, object diff --git a/server/lib/activitypub/send/send-update.ts b/server/lib/activitypub/send/send-update.ts index 622648308..e15fecff6 100644 --- a/server/lib/activitypub/send/send-update.ts +++ b/server/lib/activitypub/send/send-update.ts @@ -67,7 +67,7 @@ async function updateActivityData ( } return audiencify({ - type: 'Update', + type: 'Update' as 'Update', id: url, actor: byActor.url, object: audiencify(object, audience) diff --git a/server/tests/api/videos/multiple-servers.ts b/server/tests/api/videos/multiple-servers.ts index 74c6b8462..e31c7febd 100644 --- a/server/tests/api/videos/multiple-servers.ts +++ b/server/tests/api/videos/multiple-servers.ts @@ -728,6 +728,8 @@ describe('Test multiple servers', function () { }) describe('Should comment these videos', function () { + let childOfFirstChild: VideoCommentThreadTree + it('Should add comment (threads and replies)', async function () { this.timeout(25000) @@ -821,7 +823,7 @@ describe('Test multiple servers', function () { expect(firstChild.comment.account.host).equal('localhost:9002') expect(firstChild.children).to.have.lengthOf(1) - const childOfFirstChild = firstChild.children[0] + childOfFirstChild = firstChild.children[0] expect(childOfFirstChild.comment.text).to.equal('my super answer to answer of thread 1') expect(childOfFirstChild.comment.account.name).equal('root') expect(childOfFirstChild.comment.account.host).equal('localhost:9003') @@ -835,6 +837,33 @@ describe('Test multiple servers', function () { } }) + it('Should delete a reply', async function () { + this.timeout(10000) + + await deleteVideoComment(servers[2].url, servers[2].accessToken, videoUUID, childOfFirstChild.comment.id) + + await wait(5000) + }) + + it('Should not have this comment anymore', async function () { + for (const server of servers) { + const res1 = await getVideoCommentThreads(server.url, videoUUID, 0, 5) + const threadId = res1.body.data.find(c => c.text === 'my super first comment').id + + const res2 = await getVideoThreadComments(server.url, videoUUID, threadId) + + const tree: VideoCommentThreadTree = res2.body + expect(tree.comment.text).equal('my super first comment') + + const firstChild = tree.children[0] + expect(firstChild.comment.text).to.equal('my super answer to thread 1') + expect(firstChild.children).to.have.lengthOf(0) + + const secondChild = tree.children[1] + expect(secondChild.comment.text).to.equal('my second answer to thread 1') + } + }) + it('Should delete the thread comments', async function () { this.timeout(10000) -- cgit v1.2.3