aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--server/helpers/custom-validators/activitypub/video-comments.ts19
-rw-r--r--server/lib/activitypub/video-comments.ts15
-rw-r--r--server/models/video/video-comment.ts19
-rw-r--r--server/tests/api/videos/multiple-servers.ts45
-rw-r--r--shared/models/activitypub/objects/common-objects.ts2
5 files changed, 84 insertions, 16 deletions
diff --git a/server/helpers/custom-validators/activitypub/video-comments.ts b/server/helpers/custom-validators/activitypub/video-comments.ts
index e04c5388f..96655c3f8 100644
--- a/server/helpers/custom-validators/activitypub/video-comments.ts
+++ b/server/helpers/custom-validators/activitypub/video-comments.ts
@@ -3,11 +3,28 @@ import { ACTIVITY_PUB } from '../../../initializers/constants'
3import { exists, isArray, isDateValid } from '../misc' 3import { exists, isArray, isDateValid } from '../misc'
4import { isActivityPubUrlValid } from './misc' 4import { isActivityPubUrlValid } from './misc'
5 5
6function isTypeValid (comment: any): boolean {
7 if (comment.type === 'Note') return true
8
9 if (comment.type === 'Tombstone' && comment.formerType === 'Note') return true
10
11 return false
12}
13
6function sanitizeAndCheckVideoCommentObject (comment: any) { 14function sanitizeAndCheckVideoCommentObject (comment: any) {
7 if (!comment || comment.type !== 'Note') return false 15 if (!comment) return false
16
17 if (!isTypeValid(comment)) return false
8 18
9 normalizeComment(comment) 19 normalizeComment(comment)
10 20
21 if (comment.type === 'Tombstone') {
22 return isActivityPubUrlValid(comment.id) &&
23 isDateValid(comment.published) &&
24 isDateValid(comment.deleted) &&
25 isActivityPubUrlValid(comment.url)
26 }
27
11 return isActivityPubUrlValid(comment.id) && 28 return isActivityPubUrlValid(comment.id) &&
12 isCommentContentValid(comment.content) && 29 isCommentContentValid(comment.content) &&
13 isActivityPubUrlValid(comment.inReplyTo) && 30 isActivityPubUrlValid(comment.inReplyTo) &&
diff --git a/server/lib/activitypub/video-comments.ts b/server/lib/activitypub/video-comments.ts
index 3e8306fa4..1a15842cf 100644
--- a/server/lib/activitypub/video-comments.ts
+++ b/server/lib/activitypub/video-comments.ts
@@ -131,9 +131,9 @@ async function resolveParentComment (params: ResolveThreadParams) {
131 } 131 }
132 132
133 const actorUrl = body.attributedTo 133 const actorUrl = body.attributedTo
134 if (!actorUrl) throw new Error('Miss attributed to in comment') 134 if (!actorUrl && body.type !== 'Tombstone') throw new Error('Miss attributed to in comment')
135 135
136 if (checkUrlsSameHost(url, actorUrl) !== true) { 136 if (actorUrl && checkUrlsSameHost(url, actorUrl) !== true) {
137 throw new Error(`Actor url ${actorUrl} has not the same host than the comment url ${url}`) 137 throw new Error(`Actor url ${actorUrl} has not the same host than the comment url ${url}`)
138 } 138 }
139 139
@@ -141,18 +141,19 @@ async function resolveParentComment (params: ResolveThreadParams) {
141 throw new Error(`Comment url ${url} host is different from the AP object id ${body.id}`) 141 throw new Error(`Comment url ${url} host is different from the AP object id ${body.id}`)
142 } 142 }
143 143
144 const actor = await getOrCreateActorAndServerAndModel(actorUrl, 'all') 144 const actor = actorUrl ? await getOrCreateActorAndServerAndModel(actorUrl, 'all') : null
145 const comment = new VideoCommentModel({ 145 const comment = new VideoCommentModel({
146 url: body.id, 146 url: body.id,
147 text: body.content, 147 text: body.content ? body.content : '',
148 videoId: null, 148 videoId: null,
149 accountId: actor.Account.id, 149 accountId: actor ? actor.Account.id : null,
150 inReplyToCommentId: null, 150 inReplyToCommentId: null,
151 originCommentId: null, 151 originCommentId: null,
152 createdAt: new Date(body.published), 152 createdAt: new Date(body.published),
153 updatedAt: new Date(body.updated) 153 updatedAt: new Date(body.updated),
154 deletedAt: body.deleted ? new Date(body.deleted) : null
154 }) as MCommentOwner 155 }) as MCommentOwner
155 comment.Account = actor.Account 156 comment.Account = actor ? actor.Account : null
156 157
157 return resolveThread({ 158 return resolveThread({
158 url: body.inReplyTo, 159 url: body.inReplyTo,
diff --git a/server/models/video/video-comment.ts b/server/models/video/video-comment.ts
index b44d65138..869a42afe 100644
--- a/server/models/video/video-comment.ts
+++ b/server/models/video/video-comment.ts
@@ -507,27 +507,30 @@ export class VideoCommentModel extends Model<VideoCommentModel> {
507 } 507 }
508 508
509 toActivityPubObject (this: MCommentAP, threadParentComments: MCommentOwner[]): VideoCommentObject | ActivityTombstoneObject { 509 toActivityPubObject (this: MCommentAP, threadParentComments: MCommentOwner[]): VideoCommentObject | ActivityTombstoneObject {
510 let inReplyTo: string
511 // New thread, so in AS we reply to the video
512 if (this.inReplyToCommentId === null) {
513 inReplyTo = this.Video.url
514 } else {
515 inReplyTo = this.InReplyToVideoComment.url
516 }
517
510 if (this.isDeleted()) { 518 if (this.isDeleted()) {
511 return { 519 return {
512 id: this.url, 520 id: this.url,
513 type: 'Tombstone', 521 type: 'Tombstone',
514 formerType: 'Note', 522 formerType: 'Note',
523 inReplyTo,
515 published: this.createdAt.toISOString(), 524 published: this.createdAt.toISOString(),
516 updated: this.updatedAt.toISOString(), 525 updated: this.updatedAt.toISOString(),
517 deleted: this.deletedAt.toISOString() 526 deleted: this.deletedAt.toISOString()
518 } 527 }
519 } 528 }
520 529
521 let inReplyTo: string
522 // New thread, so in AS we reply to the video
523 if (this.inReplyToCommentId === null) {
524 inReplyTo = this.Video.url
525 } else {
526 inReplyTo = this.InReplyToVideoComment.url
527 }
528
529 const tag: ActivityTagObject[] = [] 530 const tag: ActivityTagObject[] = []
530 for (const parentComment of threadParentComments) { 531 for (const parentComment of threadParentComments) {
532 if (!parentComment.Account) continue
533
531 const actor = parentComment.Account.Actor 534 const actor = parentComment.Account.Actor
532 535
533 tag.push({ 536 tag.push({
diff --git a/server/tests/api/videos/multiple-servers.ts b/server/tests/api/videos/multiple-servers.ts
index e7b57ba1f..836bdc658 100644
--- a/server/tests/api/videos/multiple-servers.ts
+++ b/server/tests/api/videos/multiple-servers.ts
@@ -15,6 +15,7 @@ import {
15 createUser, 15 createUser,
16 dateIsValid, 16 dateIsValid,
17 doubleFollow, 17 doubleFollow,
18 flushAndRunServer,
18 flushAndRunMultipleServers, 19 flushAndRunMultipleServers,
19 getLocalVideos, 20 getLocalVideos,
20 getVideo, 21 getVideo,
@@ -938,7 +939,51 @@ describe('Test multiple servers', function () {
938 } 939 }
939 }) 940 })
940 941
942 it('Should retrieve all comments when subscribing to a new server', async function () {
943 this.timeout(120000)
944
945 const newServer = await flushAndRunServer(4)
946
947 await setAccessTokensToServers([newServer])
948 await doubleFollow(newServer, servers[0])
949 await doubleFollow(newServer, servers[2])
950 await waitJobs([newServer, ...servers])
951
952 const res = await getVideoCommentThreads(newServer.url, videoUUID, 0, 5)
953
954 expect(res.body.total).to.equal(2)
955 expect(res.body.data).to.be.an('array')
956 expect(res.body.data).to.have.lengthOf(2)
957
958 {
959 const comment: VideoComment = res.body.data[0]
960 expect(comment).to.not.be.undefined
961 expect(comment.inReplyToCommentId).to.be.null
962 expect(comment.account.name).to.equal('root')
963 expect(comment.account.host).to.equal('localhost:' + servers[2].port)
964 expect(comment.totalReplies).to.equal(0)
965 expect(dateIsValid(comment.createdAt as string)).to.be.true
966 expect(dateIsValid(comment.updatedAt as string)).to.be.true
967 }
968
969 {
970 const deletedComment: VideoComment = res.body.data[1]
971 expect(deletedComment).to.not.be.undefined
972 expect(deletedComment.isDeleted).to.be.true
973 expect(deletedComment.deletedAt).to.not.be.null
974 expect(deletedComment.text).to.equal('')
975 expect(deletedComment.inReplyToCommentId).to.be.null
976 expect(deletedComment.account).to.be.null
977 expect(deletedComment.totalReplies).to.equal(3)
978 expect(dateIsValid(deletedComment.createdAt as string)).to.be.true
979 expect(dateIsValid(deletedComment.updatedAt as string)).to.be.true
980 expect(dateIsValid(deletedComment.deletedAt as string)).to.be.true
981 }
982 })
983
941 it('Should delete a remote thread by the origin server', async function () { 984 it('Should delete a remote thread by the origin server', async function () {
985 this.timeout(5000)
986
942 const res = await getVideoCommentThreads(servers[ 0 ].url, videoUUID, 0, 5) 987 const res = await getVideoCommentThreads(servers[ 0 ].url, videoUUID, 0, 5)
943 const threadId = res.body.data.find(c => c.text === 'my super second comment').id 988 const threadId = res.body.data.find(c => c.text === 'my super second comment').id
944 await deleteVideoComment(servers[ 0 ].url, servers[ 0 ].accessToken, videoUUID, threadId) 989 await deleteVideoComment(servers[ 0 ].url, servers[ 0 ].accessToken, videoUUID, threadId)
diff --git a/shared/models/activitypub/objects/common-objects.ts b/shared/models/activitypub/objects/common-objects.ts
index df287d570..de1116ab3 100644
--- a/shared/models/activitypub/objects/common-objects.ts
+++ b/shared/models/activitypub/objects/common-objects.ts
@@ -93,9 +93,11 @@ export interface ActivityPubAttributedTo {
93export interface ActivityTombstoneObject { 93export interface ActivityTombstoneObject {
94 '@context'?: any 94 '@context'?: any
95 id: string 95 id: string
96 url?: string
96 type: 'Tombstone' 97 type: 'Tombstone'
97 name?: string 98 name?: string
98 formerType?: string 99 formerType?: string
100 inReplyTo?: string
99 published: string 101 published: string
100 updated: string 102 updated: string
101 deleted: string 103 deleted: string