kopia lustrzana https://github.com/cloudflare/wildebeest
Merge pull request #210 from cloudflare/sven/send-delete-to-followers
Deliver Delete activity to followerspull/212/head
commit
9d4415fbb0
|
@ -0,0 +1,16 @@
|
||||||
|
import type { APObject } from '../objects'
|
||||||
|
import type { Actor } from '../actors'
|
||||||
|
import type { Activity } from '.'
|
||||||
|
import * as activity from '.'
|
||||||
|
|
||||||
|
const DELETE = 'Delete'
|
||||||
|
|
||||||
|
export function create(domain: string, actor: Actor, object: APObject): Activity {
|
||||||
|
return {
|
||||||
|
'@context': ['https://www.w3.org/ns/activitystreams'],
|
||||||
|
id: activity.uri(domain),
|
||||||
|
type: DELETE,
|
||||||
|
actor: actor.id,
|
||||||
|
object,
|
||||||
|
}
|
||||||
|
}
|
|
@ -36,6 +36,9 @@ export async function deliverToActor(signingKey: CryptoKey, from: Actor, to: Act
|
||||||
console.log(`${to.inbox} returned 200`)
|
console.log(`${to.inbox} returned 200`)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: eventually move this to the queue worker, the backend can send a message
|
||||||
|
// to a collection (followers) and the worker creates the indivual messages. More
|
||||||
|
// reliable and scalable.
|
||||||
export async function deliverFollowers(
|
export async function deliverFollowers(
|
||||||
db: D1Database,
|
db: D1Database,
|
||||||
userKEK: string,
|
userKEK: string,
|
||||||
|
|
|
@ -813,29 +813,32 @@ describe('Mastodon APIs', () => {
|
||||||
|
|
||||||
test('delete non-existing status', async () => {
|
test('delete non-existing status', async () => {
|
||||||
const db = await makeDB()
|
const db = await makeDB()
|
||||||
|
const queue = makeQueue()
|
||||||
const actor = await createPerson(domain, db, userKEK, 'sven@cloudflare.com')
|
const actor = await createPerson(domain, db, userKEK, 'sven@cloudflare.com')
|
||||||
const mastodonId = 'abcd'
|
const mastodonId = 'abcd'
|
||||||
const res = await statuses_id.handleRequestDelete(db, mastodonId, actor, domain)
|
const res = await statuses_id.handleRequestDelete(db, mastodonId, actor, domain, userKEK, queue)
|
||||||
assert.equal(res.status, 404)
|
assert.equal(res.status, 404)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('delete status from a different actor', async () => {
|
test('delete status from a different actor', async () => {
|
||||||
const db = await makeDB()
|
const db = await makeDB()
|
||||||
|
const queue = makeQueue()
|
||||||
const actor = await createPerson(domain, db, userKEK, 'sven@cloudflare.com')
|
const actor = await createPerson(domain, db, userKEK, 'sven@cloudflare.com')
|
||||||
const actor2 = await createPerson(domain, db, userKEK, 'sven2@cloudflare.com')
|
const actor2 = await createPerson(domain, db, userKEK, 'sven2@cloudflare.com')
|
||||||
const note = await createPublicNote(domain, db, 'note from actor2', actor2)
|
const note = await createPublicNote(domain, db, 'note from actor2', actor2)
|
||||||
|
|
||||||
const res = await statuses_id.handleRequestDelete(db, note[mastodonIdSymbol]!, actor, domain)
|
const res = await statuses_id.handleRequestDelete(db, note[mastodonIdSymbol]!, actor, domain, userKEK, queue)
|
||||||
assert.equal(res.status, 404)
|
assert.equal(res.status, 404)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('delete status', async () => {
|
test('delete status remove DB rows', async () => {
|
||||||
const db = await makeDB()
|
const db = await makeDB()
|
||||||
|
const queue = makeQueue()
|
||||||
const actor = await createPerson(domain, db, userKEK, 'sven@cloudflare.com')
|
const actor = await createPerson(domain, db, userKEK, 'sven@cloudflare.com')
|
||||||
const note = await createPublicNote(domain, db, 'note from actor', actor)
|
const note = await createPublicNote(domain, db, 'note from actor', actor)
|
||||||
await addObjectInOutbox(db, actor, note)
|
await addObjectInOutbox(db, actor, note)
|
||||||
|
|
||||||
const res = await statuses_id.handleRequestDelete(db, note[mastodonIdSymbol]!, actor, domain)
|
const res = await statuses_id.handleRequestDelete(db, note[mastodonIdSymbol]!, actor, domain, userKEK, queue)
|
||||||
assert.equal(res.status, 200)
|
assert.equal(res.status, 200)
|
||||||
|
|
||||||
{
|
{
|
||||||
|
@ -847,5 +850,30 @@ describe('Mastodon APIs', () => {
|
||||||
assert.equal(count, 0)
|
assert.equal(count, 0)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('delete status sends to followers', async () => {
|
||||||
|
const db = await makeDB()
|
||||||
|
const queue = makeQueue()
|
||||||
|
const actor = await createPerson(domain, db, userKEK, 'sven@cloudflare.com')
|
||||||
|
const actor2 = await createPerson(domain, db, userKEK, 'sven2@cloudflare.com')
|
||||||
|
const actor3 = await createPerson(domain, db, userKEK, 'sven3@cloudflare.com')
|
||||||
|
const note = await createPublicNote(domain, db, 'note from actor', actor)
|
||||||
|
|
||||||
|
await addFollowing(db, actor2, actor, 'not needed')
|
||||||
|
await acceptFollowing(db, actor2, actor)
|
||||||
|
await addFollowing(db, actor3, actor, 'not needed')
|
||||||
|
await acceptFollowing(db, actor3, actor)
|
||||||
|
|
||||||
|
const res = await statuses_id.handleRequestDelete(db, note[mastodonIdSymbol]!, actor, domain, userKEK, queue)
|
||||||
|
assert.equal(res.status, 200)
|
||||||
|
|
||||||
|
assert.equal(queue.messages.length, 2)
|
||||||
|
assert.equal(queue.messages[0].activity.type, 'Delete')
|
||||||
|
assert.equal(queue.messages[0].actorId, actor.id.toString())
|
||||||
|
assert.equal(queue.messages[0].toActorId, actor2.id.toString())
|
||||||
|
assert.equal(queue.messages[1].activity.type, 'Delete')
|
||||||
|
assert.equal(queue.messages[1].actorId, actor.id.toString())
|
||||||
|
assert.equal(queue.messages[1].toActorId, actor3.id.toString())
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
// https://docs.joinmastodon.org/methods/statuses/#get
|
// https://docs.joinmastodon.org/methods/statuses/#get
|
||||||
|
|
||||||
import { type Note } from 'wildebeest/backend/src/activitypub/objects/note'
|
import { type Note } from 'wildebeest/backend/src/activitypub/objects/note'
|
||||||
|
import * as activities from 'wildebeest/backend/src/activitypub/activities/delete'
|
||||||
import { cors } from 'wildebeest/backend/src/utils/cors'
|
import { cors } from 'wildebeest/backend/src/utils/cors'
|
||||||
import type { Person } from 'wildebeest/backend/src/activitypub/actors'
|
import type { Person } from 'wildebeest/backend/src/activitypub/actors'
|
||||||
import type { UUID } from 'wildebeest/backend/src/types'
|
import type { UUID } from 'wildebeest/backend/src/types'
|
||||||
|
@ -10,6 +11,8 @@ import type { Env } from 'wildebeest/backend/src/types/env'
|
||||||
import * as errors from 'wildebeest/backend/src/errors'
|
import * as errors from 'wildebeest/backend/src/errors'
|
||||||
import { getObjectByMastodonId } from 'wildebeest/backend/src/activitypub/objects'
|
import { getObjectByMastodonId } from 'wildebeest/backend/src/activitypub/objects'
|
||||||
import { urlToHandle } from 'wildebeest/backend/src/utils/handle'
|
import { urlToHandle } from 'wildebeest/backend/src/utils/handle'
|
||||||
|
import { deliverFollowers } from 'wildebeest/backend/src/activitypub/deliver'
|
||||||
|
import type { Queue, DeliverMessageBody } from 'wildebeest/backend/src/types/queue'
|
||||||
|
|
||||||
export const onRequestGet: PagesFunction<Env, any, ContextData> = async ({ params, env, request }) => {
|
export const onRequestGet: PagesFunction<Env, any, ContextData> = async ({ params, env, request }) => {
|
||||||
const domain = new URL(request.url).hostname
|
const domain = new URL(request.url).hostname
|
||||||
|
@ -18,7 +21,7 @@ export const onRequestGet: PagesFunction<Env, any, ContextData> = async ({ param
|
||||||
|
|
||||||
export const onRequestDelete: PagesFunction<Env, any, ContextData> = async ({ params, env, request, data }) => {
|
export const onRequestDelete: PagesFunction<Env, any, ContextData> = async ({ params, env, request, data }) => {
|
||||||
const domain = new URL(request.url).hostname
|
const domain = new URL(request.url).hostname
|
||||||
return handleRequestDelete(env.DATABASE, params.id as UUID, data.connectedActor, domain)
|
return handleRequestDelete(env.DATABASE, params.id as UUID, data.connectedActor, domain, env.userKEK, env.QUEUE)
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function handleRequestGet(db: D1Database, id: UUID, domain: string): Promise<Response> {
|
export async function handleRequestGet(db: D1Database, id: UUID, domain: string): Promise<Response> {
|
||||||
|
@ -62,7 +65,9 @@ export async function handleRequestDelete(
|
||||||
db: D1Database,
|
db: D1Database,
|
||||||
id: UUID,
|
id: UUID,
|
||||||
connectedActor: Person,
|
connectedActor: Person,
|
||||||
domain: string
|
domain: string,
|
||||||
|
userKEK: string,
|
||||||
|
queue: Queue<DeliverMessageBody>
|
||||||
): Promise<Response> {
|
): Promise<Response> {
|
||||||
const obj = (await getObjectByMastodonId(db, id)) as Note
|
const obj = (await getObjectByMastodonId(db, id)) as Note
|
||||||
if (obj === null) {
|
if (obj === null) {
|
||||||
|
@ -79,7 +84,9 @@ export async function handleRequestDelete(
|
||||||
|
|
||||||
await deleteNote(db, obj)
|
await deleteNote(db, obj)
|
||||||
|
|
||||||
// FIXME: deliver a Delete message to the Actor followers and our peers
|
// FIXME: deliver a Delete message to our peers
|
||||||
|
const activity = activities.create(domain, connectedActor, obj)
|
||||||
|
await deliverFollowers(db, userKEK, connectedActor, activity, queue)
|
||||||
|
|
||||||
const headers = {
|
const headers = {
|
||||||
...cors(),
|
...cors(),
|
||||||
|
|
Ładowanie…
Reference in New Issue