kopia lustrzana https://github.com/cloudflare/wildebeest
MOW-135: prevent duplicated reblogs
rodzic
1d9775b3aa
commit
bf26dcca14
|
|
@ -25,6 +25,7 @@ import { createReblog } from 'wildebeest/backend/src/mastodon/reblog'
|
|||
import { insertReply } from 'wildebeest/backend/src/mastodon/reply'
|
||||
import type { Activity } from 'wildebeest/backend/src/activitypub/activities'
|
||||
import { originalActorIdSymbol } from 'wildebeest/backend/src/activitypub/objects'
|
||||
import { hasReblog } from 'wildebeest/backend/src/mastodon/reblog'
|
||||
|
||||
function extractID(domain: string, s: string | URL): string {
|
||||
return s.toString().replace(`https://${domain}/ap/users/`, '')
|
||||
|
|
@ -289,6 +290,12 @@ export async function handle(
|
|||
|
||||
const fromActor = await actors.getAndCache(actorId, db)
|
||||
|
||||
if (await hasReblog(db, fromActor, obj)) {
|
||||
// A reblog already exists. To avoid dulicated reblog we ignore.
|
||||
console.warn('probably duplicated Announce message')
|
||||
break
|
||||
}
|
||||
|
||||
// notify the user
|
||||
const targetActor = await actors.getActorById(db, new URL(obj[originalActorIdSymbol]))
|
||||
if (targetActor === null) {
|
||||
|
|
|
|||
|
|
@ -39,3 +39,12 @@ export function getReblogs(db: D1Database, obj: APObject): Promise<Array<string>
|
|||
|
||||
return getResultsField(statement, 'actor_id')
|
||||
}
|
||||
|
||||
export async function hasReblog(db: D1Database, actor: Actor, obj: APObject): Promise<boolean> {
|
||||
const query = `
|
||||
SELECT count(*) as count FROM actor_reblogs WHERE object_id=?1 AND actor_id=?2
|
||||
`
|
||||
|
||||
const { count } = await db.prepare(query).bind(obj.id.toString(), actor.id.toString()).first<{ count: number }>()
|
||||
return count > 0
|
||||
}
|
||||
|
|
|
|||
|
|
@ -545,6 +545,53 @@ describe('ActivityPub', () => {
|
|||
assert(outbox_object)
|
||||
assert.equal(outbox_object.actor_id, remoteActorId)
|
||||
})
|
||||
|
||||
test('duplicated announce', async () => {
|
||||
const remoteActorId = 'https://example.com/actor'
|
||||
const objectId = 'https://example.com/some-object'
|
||||
globalThis.fetch = async (input: RequestInfo) => {
|
||||
if (input.toString() === remoteActorId) {
|
||||
return new Response(
|
||||
JSON.stringify({
|
||||
id: remoteActorId,
|
||||
icon: { url: 'img.com' },
|
||||
type: 'Person',
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
if (input.toString() === objectId) {
|
||||
return new Response(
|
||||
JSON.stringify({
|
||||
id: objectId,
|
||||
type: 'Note',
|
||||
content: 'foo',
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
throw new Error('unexpected request to ' + input)
|
||||
}
|
||||
|
||||
const db = await makeDB()
|
||||
await createPerson(domain, db, userKEK, 'sven@cloudflare.com')
|
||||
|
||||
const activity: any = {
|
||||
type: 'Announce',
|
||||
actor: remoteActorId,
|
||||
to: [],
|
||||
cc: [],
|
||||
object: objectId,
|
||||
}
|
||||
await activityHandler.handle(domain, activity, db, userKEK, adminEmail, vapidKeys)
|
||||
|
||||
// Handle the same Activity
|
||||
await activityHandler.handle(domain, activity, db, userKEK, adminEmail, vapidKeys)
|
||||
|
||||
// Ensure only one reblog is kept
|
||||
const { count } = await db.prepare('SELECT count(*) as count FROM outbox_objects').first<{ count: number }>()
|
||||
assert.equal(count, 1)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
|
|||
Ładowanie…
Reference in New Issue