aboutsummaryrefslogtreecommitdiffhomepage
path: root/server
diff options
context:
space:
mode:
authorChocobozzz <me@florianbigard.com>2018-03-27 10:26:52 +0200
committerChocobozzz <me@florianbigard.com>2018-03-27 11:11:15 +0200
commit73c0809326670867642f8a36f68d8564e0e406b5 (patch)
treeb2849106a252f8b2108318d90767dcb747d9dd6c /server
parent649f0334e0673a28d1674aa82de29326fa3cdb63 (diff)
downloadPeerTube-73c0809326670867642f8a36f68d8564e0e406b5.tar.gz
PeerTube-73c0809326670867642f8a36f68d8564e0e406b5.tar.zst
PeerTube-73c0809326670867642f8a36f68d8564e0e406b5.zip
Fix delete comment federation
Diffstat (limited to 'server')
-rw-r--r--server/lib/activitypub/process/process-delete.ts15
-rw-r--r--server/lib/activitypub/send/misc.ts6
-rw-r--r--server/lib/activitypub/send/send-create.ts8
-rw-r--r--server/lib/activitypub/send/send-delete.ts40
-rw-r--r--server/lib/activitypub/send/send-like.ts2
-rw-r--r--server/lib/activitypub/send/send-undo.ts2
-rw-r--r--server/lib/activitypub/send/send-update.ts2
-rw-r--r--server/tests/api/videos/multiple-servers.ts31
8 files changed, 80 insertions, 26 deletions
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'
8import { VideoChannelModel } from '../../../models/video/video-channel' 8import { VideoChannelModel } from '../../../models/video/video-channel'
9import { VideoCommentModel } from '../../../models/video/video-comment' 9import { VideoCommentModel } from '../../../models/video/video-comment'
10import { getOrCreateActorAndServerAndModel } from '../actor' 10import { getOrCreateActorAndServerAndModel } from '../actor'
11import { forwardActivity } from '../send/misc'
11 12
12async function processDeleteActivity (activity: ActivityDelete) { 13async function processDeleteActivity (activity: ActivityDelete) {
13 const objectUrl = typeof activity.object === 'string' ? activity.object : activity.object.id 14 const objectUrl = typeof activity.object === 'string' ? activity.object : activity.object.id
@@ -33,7 +34,7 @@ async function processDeleteActivity (activity: ActivityDelete) {
33 { 34 {
34 const videoCommentInstance = await VideoCommentModel.loadByUrlAndPopulateAccount(objectUrl) 35 const videoCommentInstance = await VideoCommentModel.loadByUrlAndPopulateAccount(objectUrl)
35 if (videoCommentInstance) { 36 if (videoCommentInstance) {
36 return processDeleteVideoComment(actor, videoCommentInstance) 37 return processDeleteVideoComment(actor, videoCommentInstance, activity)
37 } 38 }
38 } 39 }
39 40
@@ -116,21 +117,27 @@ async function deleteRemoteVideoChannel (videoChannelToRemove: VideoChannelModel
116 logger.info('Remote video channel with uuid %s removed.', videoChannelToRemove.Actor.uuid) 117 logger.info('Remote video channel with uuid %s removed.', videoChannelToRemove.Actor.uuid)
117} 118}
118 119
119async function processDeleteVideoComment (actor: ActorModel, videoComment: VideoCommentModel) { 120async function processDeleteVideoComment (byActor: ActorModel, videoComment: VideoCommentModel, activity: ActivityDelete) {
120 const options = { 121 const options = {
121 arguments: [ actor, videoComment ], 122 arguments: [ byActor, videoComment, activity ],
122 errorMessage: 'Cannot remove the remote video comment with many retries.' 123 errorMessage: 'Cannot remove the remote video comment with many retries.'
123 } 124 }
124 125
125 await retryTransactionWrapper(deleteRemoteVideoComment, options) 126 await retryTransactionWrapper(deleteRemoteVideoComment, options)
126} 127}
127 128
128function deleteRemoteVideoComment (actor: ActorModel, videoComment: VideoCommentModel) { 129function deleteRemoteVideoComment (byActor: ActorModel, videoComment: VideoCommentModel, activity: ActivityDelete) {
129 logger.debug('Removing remote video comment "%s".', videoComment.url) 130 logger.debug('Removing remote video comment "%s".', videoComment.url)
130 131
131 return sequelizeTypescript.transaction(async t => { 132 return sequelizeTypescript.transaction(async t => {
132 await videoComment.destroy({ transaction: t }) 133 await videoComment.destroy({ transaction: t })
133 134
135 if (videoComment.Video.isOwned()) {
136 // Don't resend the activity to the sender
137 const exceptions = [ byActor ]
138 await forwardActivity(activity, t, exceptions)
139 }
140
134 logger.info('Remote video comment %s removed.', videoComment.url) 141 logger.info('Remote video comment %s removed.', videoComment.url)
135 }) 142 })
136} 143}
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
96 } 96 }
97} 97}
98 98
99function getOriginVideoCommentAudience ( 99function getVideoCommentAudience (
100 videoComment: VideoCommentModel, 100 videoComment: VideoCommentModel,
101 threadParentComments: VideoCommentModel[], 101 threadParentComments: VideoCommentModel[],
102 actorsInvolvedInVideo: ActorModel[], 102 actorsInvolvedInVideo: ActorModel[],
@@ -160,7 +160,7 @@ function buildAudience (followerInboxUrls: string[], isPublic = true) {
160 return { to, cc } 160 return { to, cc }
161} 161}
162 162
163function audiencify (object: any, audience: ActivityAudience) { 163function audiencify <T> (object: T, audience: ActivityAudience) {
164 return Object.assign(object, audience) 164 return Object.assign(object, audience)
165} 165}
166 166
@@ -192,7 +192,7 @@ export {
192 getObjectFollowersAudience, 192 getObjectFollowersAudience,
193 forwardActivity, 193 forwardActivity,
194 audiencify, 194 audiencify,
195 getOriginVideoCommentAudience, 195 getVideoCommentAudience,
196 computeUris, 196 computeUris,
197 broadcastToActors 197 broadcastToActors
198} 198}
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 {
15 getAudience, 15 getAudience,
16 getObjectFollowersAudience, 16 getObjectFollowersAudience,
17 getOriginVideoAudience, 17 getOriginVideoAudience,
18 getOriginVideoCommentAudience, 18 getVideoCommentAudience,
19 unicastTo 19 unicastTo
20} from './misc' 20} from './misc'
21 21
@@ -47,7 +47,7 @@ async function sendCreateVideoCommentToOrigin (comment: VideoCommentModel, t: Tr
47 47
48 const actorsInvolvedInComment = await getActorsInvolvedInVideo(comment.Video, t) 48 const actorsInvolvedInComment = await getActorsInvolvedInVideo(comment.Video, t)
49 actorsInvolvedInComment.push(byActor) 49 actorsInvolvedInComment.push(byActor)
50 const audience = getOriginVideoCommentAudience(comment, threadParentComments, actorsInvolvedInComment) 50 const audience = getVideoCommentAudience(comment, threadParentComments, actorsInvolvedInComment)
51 51
52 const data = await createActivityData(comment.url, byActor, commentObject, t, audience) 52 const data = await createActivityData(comment.url, byActor, commentObject, t, audience)
53 53
@@ -70,7 +70,7 @@ async function sendCreateVideoCommentToVideoFollowers (comment: VideoCommentMode
70 const actorsInvolvedInComment = await getActorsInvolvedInVideo(comment.Video, t) 70 const actorsInvolvedInComment = await getActorsInvolvedInVideo(comment.Video, t)
71 actorsInvolvedInComment.push(byActor) 71 actorsInvolvedInComment.push(byActor)
72 72
73 const audience = getOriginVideoCommentAudience(comment, threadParentComments, actorsInvolvedInComment) 73 const audience = getVideoCommentAudience(comment, threadParentComments, actorsInvolvedInComment, true)
74 const data = await createActivityData(comment.url, byActor, commentObject, t, audience) 74 const data = await createActivityData(comment.url, byActor, commentObject, t, audience)
75 75
76 // This was a reply, send it to the parent actors 76 // This was a reply, send it to the parent actors
@@ -144,7 +144,7 @@ async function createActivityData (
144 } 144 }
145 145
146 return audiencify({ 146 return audiencify({
147 type: 'Create', 147 type: 'Create' as 'Create',
148 id: url + '/activity', 148 id: url + '/activity',
149 actor: byActor.url, 149 actor: byActor.url,
150 object: audiencify(object, audience) 150 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 @@
1import { Transaction } from 'sequelize' 1import { Transaction } from 'sequelize'
2import { ActivityDelete } from '../../../../shared/models/activitypub' 2import { ActivityAudience, ActivityDelete } from '../../../../shared/models/activitypub'
3import { ActorModel } from '../../../models/activitypub/actor' 3import { ActorModel } from '../../../models/activitypub/actor'
4import { VideoModel } from '../../../models/video/video' 4import { VideoModel } from '../../../models/video/video'
5import { VideoCommentModel } from '../../../models/video/video-comment' 5import { VideoCommentModel } from '../../../models/video/video-comment'
6import { VideoShareModel } from '../../../models/video/video-share' 6import { VideoShareModel } from '../../../models/video/video-share'
7import { getDeleteActivityPubUrl } from '../url' 7import { getDeleteActivityPubUrl } from '../url'
8import { broadcastToFollowers } from './misc' 8import { audiencify, broadcastToActors, broadcastToFollowers, getActorsInvolvedInVideo, getVideoCommentAudience, unicastTo } from './misc'
9 9
10async function sendDeleteVideo (video: VideoModel, t: Transaction) { 10async function sendDeleteVideo (video: VideoModel, t: Transaction) {
11 const url = getDeleteActivityPubUrl(video.url) 11 const url = getDeleteActivityPubUrl(video.url)
@@ -30,16 +30,30 @@ async function sendDeleteActor (byActor: ActorModel, t: Transaction) {
30} 30}
31 31
32async function sendDeleteVideoComment (videoComment: VideoCommentModel, t: Transaction) { 32async function sendDeleteVideoComment (videoComment: VideoCommentModel, t: Transaction) {
33 const url = getDeleteActivityPubUrl(videoComment.url) 33 const isVideoOrigin = videoComment.Video.isOwned()
34 34
35 const url = getDeleteActivityPubUrl(videoComment.url)
35 const byActor = videoComment.Account.Actor 36 const byActor = videoComment.Account.Actor
36 const data = deleteActivityData(url, videoComment.url, byActor) 37 const threadParentComments = await VideoCommentModel.listThreadParentComments(videoComment, t)
37 38
38 const actorsInvolved = await VideoShareModel.loadActorsByShare(videoComment.Video.id, t) 39 const actorsInvolvedInComment = await getActorsInvolvedInVideo(videoComment.Video, t)
39 actorsInvolved.push(videoComment.Video.VideoChannel.Account.Actor) 40 actorsInvolvedInComment.push(byActor)
40 actorsInvolved.push(byActor)
41 41
42 return broadcastToFollowers(data, byActor, actorsInvolved, t) 42 const audience = getVideoCommentAudience(videoComment, threadParentComments, actorsInvolvedInComment, isVideoOrigin)
43 const data = deleteActivityData(url, videoComment.url, byActor, audience)
44
45 // This was a reply, send it to the parent actors
46 const actorsException = [ byActor ]
47 await broadcastToActors(data, byActor, threadParentComments.map(c => c.Account.Actor), actorsException)
48
49 // Broadcast to our followers
50 await broadcastToFollowers(data, byActor, [ byActor ], t)
51
52 // Send to actors involved in the comment
53 if (isVideoOrigin) return broadcastToFollowers(data, byActor, actorsInvolvedInComment, t, actorsException)
54
55 // Send to origin
56 return unicastTo(data, byActor, videoComment.Video.VideoChannel.Account.Actor.sharedInboxUrl)
43} 57}
44 58
45// --------------------------------------------------------------------------- 59// ---------------------------------------------------------------------------
@@ -52,11 +66,15 @@ export {
52 66
53// --------------------------------------------------------------------------- 67// ---------------------------------------------------------------------------
54 68
55function deleteActivityData (url: string, object: string, byActor: ActorModel): ActivityDelete { 69function deleteActivityData (url: string, object: string, byActor: ActorModel, audience?: ActivityAudience): ActivityDelete {
56 return { 70 const activity = {
57 type: 'Delete', 71 type: 'Delete' as 'Delete',
58 id: url, 72 id: url,
59 actor: byActor.url, 73 actor: byActor.url,
60 object 74 object
61 } 75 }
76
77 if (audience) return audiencify(activity, audience)
78
79 return activity
62} 80}
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 (
46 } 46 }
47 47
48 return audiencify({ 48 return audiencify({
49 type: 'Like', 49 type: 'Like' as 'Like',
50 id: url, 50 id: url,
51 actor: byActor.url, 51 actor: byActor.url,
52 object: video.url 52 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 (
108 } 108 }
109 109
110 return audiencify({ 110 return audiencify({
111 type: 'Undo', 111 type: 'Undo' as 'Undo',
112 id: url, 112 id: url,
113 actor: byActor.url, 113 actor: byActor.url,
114 object 114 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 (
67 } 67 }
68 68
69 return audiencify({ 69 return audiencify({
70 type: 'Update', 70 type: 'Update' as 'Update',
71 id: url, 71 id: url,
72 actor: byActor.url, 72 actor: byActor.url,
73 object: audiencify(object, audience) 73 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 () {
728 }) 728 })
729 729
730 describe('Should comment these videos', function () { 730 describe('Should comment these videos', function () {
731 let childOfFirstChild: VideoCommentThreadTree
732
731 it('Should add comment (threads and replies)', async function () { 733 it('Should add comment (threads and replies)', async function () {
732 this.timeout(25000) 734 this.timeout(25000)
733 735
@@ -821,7 +823,7 @@ describe('Test multiple servers', function () {
821 expect(firstChild.comment.account.host).equal('localhost:9002') 823 expect(firstChild.comment.account.host).equal('localhost:9002')
822 expect(firstChild.children).to.have.lengthOf(1) 824 expect(firstChild.children).to.have.lengthOf(1)
823 825
824 const childOfFirstChild = firstChild.children[0] 826 childOfFirstChild = firstChild.children[0]
825 expect(childOfFirstChild.comment.text).to.equal('my super answer to answer of thread 1') 827 expect(childOfFirstChild.comment.text).to.equal('my super answer to answer of thread 1')
826 expect(childOfFirstChild.comment.account.name).equal('root') 828 expect(childOfFirstChild.comment.account.name).equal('root')
827 expect(childOfFirstChild.comment.account.host).equal('localhost:9003') 829 expect(childOfFirstChild.comment.account.host).equal('localhost:9003')
@@ -835,6 +837,33 @@ describe('Test multiple servers', function () {
835 } 837 }
836 }) 838 })
837 839
840 it('Should delete a reply', async function () {
841 this.timeout(10000)
842
843 await deleteVideoComment(servers[2].url, servers[2].accessToken, videoUUID, childOfFirstChild.comment.id)
844
845 await wait(5000)
846 })
847
848 it('Should not have this comment anymore', async function () {
849 for (const server of servers) {
850 const res1 = await getVideoCommentThreads(server.url, videoUUID, 0, 5)
851 const threadId = res1.body.data.find(c => c.text === 'my super first comment').id
852
853 const res2 = await getVideoThreadComments(server.url, videoUUID, threadId)
854
855 const tree: VideoCommentThreadTree = res2.body
856 expect(tree.comment.text).equal('my super first comment')
857
858 const firstChild = tree.children[0]
859 expect(firstChild.comment.text).to.equal('my super answer to thread 1')
860 expect(firstChild.children).to.have.lengthOf(0)
861
862 const secondChild = tree.children[1]
863 expect(secondChild.comment.text).to.equal('my second answer to thread 1')
864 }
865 })
866
838 it('Should delete the thread comments', async function () { 867 it('Should delete the thread comments', async function () {
839 this.timeout(10000) 868 this.timeout(10000)
840 869