Skip to content

Commit a8e22a0

Browse files
committed
fix: delete tombstones and log failing activities #98
1 parent 6655387 commit a8e22a0

File tree

2 files changed

+79
-4
lines changed

2 files changed

+79
-4
lines changed

src/server/apsystem.test.ts

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -624,6 +624,73 @@ test('ActivityPubSystem - Handle Delete activity', async t => {
624624
t.falsy(isActivityPresent, 'The activity should be removed from the index/collection')
625625
})
626626

627+
test('ActivityPubSystem - Handle Delete Tombstone activity', async t => {
628+
const store = newStore()
629+
const mockFetch = new MockFetch()
630+
const hookSystem = new HookSystem(store, mockFetch.fetch as FetchLike)
631+
const moderation = new ModerationChecker(store)
632+
const aps = new ActivityPubSystem(
633+
'http://localhost',
634+
store,
635+
moderation,
636+
hookSystem,
637+
mockLog,
638+
mockFetch.fetch as FetchLike
639+
)
640+
641+
const actorMention = '@user1@example.com'
642+
const activityId = 'https://example.com/activity1'
643+
644+
const actorUrl = mockFetch.mockActor(actorMention)
645+
646+
await store.forActor(actorMention).setInfo({
647+
keypair: { ...generateKeypair() },
648+
actorUrl,
649+
publicKeyId: 'testAccount#main-key'
650+
})
651+
652+
const activity: APActivity = {
653+
'@context': 'https://www.w3.org/ns/activitystreams',
654+
type: 'Like',
655+
published: new Date().toISOString(),
656+
actor: actorUrl,
657+
object: 'https://example.com/note1',
658+
id: activityId
659+
}
660+
await store.forActor(actorMention).inbox.add(activity)
661+
662+
const deleteActivity: APActivity = {
663+
'@context': 'https://www.w3.org/ns/activitystreams',
664+
type: 'Delete',
665+
published: new Date().toISOString(),
666+
actor: actorUrl,
667+
object: {
668+
id: activityId,
669+
type: 'Tombstone'
670+
},
671+
id: 'https://example.com/activity2'
672+
}
673+
674+
mockFetch.addAPObject(deleteActivity)
675+
676+
await aps.ingestActivity(actorMention, deleteActivity)
677+
678+
try {
679+
await store.forActor(actorMention).inbox.get(activityId)
680+
t.fail('The activity should be deleted from the inbox')
681+
} catch (error) {
682+
if (error instanceof Error) {
683+
t.true(error.message.includes('Activity not found'), 'The activity should be deleted from the inbox')
684+
} else {
685+
t.fail('Unexpected error type')
686+
}
687+
}
688+
689+
const storedActivities = await store.forActor(actorMention).inbox.list({ object: activityId })
690+
const isActivityPresent = storedActivities.some((a) => a.id === activityId)
691+
t.falsy(isActivityPresent, 'The activity should be removed from the index/collection')
692+
})
693+
627694
// After all tests, restore all sinon mocks
628695
test.afterEach(() => {
629696
// Restore all sinon mocks

src/server/apsystem.ts

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -539,12 +539,20 @@ export default class ActivityPubSystem {
539539
)
540540
return
541541
}
542+
542543
// Remove activity from inbox and any other index/collection
543-
if (typeof activity.object !== 'string') {
544-
throw createError(400, 'Error deleting activity, must have activity URL in object field')
544+
let object = activity.object
545+
if (typeof object !== 'string') {
546+
if ((object != null) && 'id' in object && typeof object.id === 'string') {
547+
object = object.id
548+
} else {
549+
this.log.warn({ activity }, 'Error deleting activity, must have activity URL in object field')
550+
throw createError(400, 'Error deleting activity, must have activity URL in object field')
551+
}
545552
}
546-
await actorStore.inbox.remove(activity.object)
547-
this.log.info({ activityId }, 'Deleted activity from inbox')
553+
554+
await actorStore.inbox.remove(object)
555+
this.log.info({ activityId, object }, 'Deleted activity from inbox')
548556
// Notify CMS that the activity was deleted
549557
await this.hookSystem.dispatchOnApproved(fromActor, activity)
550558
return

0 commit comments

Comments
 (0)