diff --git a/backend/src/accounts/alias.ts b/backend/src/accounts/alias.ts index 999f70e..a117262 100644 --- a/backend/src/accounts/alias.ts +++ b/backend/src/accounts/alias.ts @@ -1,10 +1,13 @@ import { setActorAlias } from 'wildebeest/backend/src/activitypub/actors' +import { deliverToActor } from 'wildebeest/backend/src/activitypub/deliver' +import { getSigningKey } from 'wildebeest/backend/src/mastodon/account' +import * as follow from 'wildebeest/backend/src/activitypub/activities/follow' import type { Actor } from 'wildebeest/backend/src/activitypub/actors' import { parseHandle } from 'wildebeest/backend/src/utils/parse' import { queryAcct } from 'wildebeest/backend/src/webfinger' import { type Database } from 'wildebeest/backend/src/database' -export async function addAlias(db: Database, alias: string, connectedActor: Actor) { +export async function addAlias(db: Database, alias: string, connectedActor: Actor, userKEK: string, domain: string) { const handle = parseHandle(alias) const acct = `${handle.localPart}@${handle.domain}` if (handle.domain === null) { @@ -17,4 +20,12 @@ export async function addAlias(db: Database, alias: string, connectedActor: Acto } await setActorAlias(db, connectedActor.id, actor.id) + + // For Mastodon to deliver the Move Activity we need to be following the + // "moving from" actor. + { + const activity = follow.create(connectedActor, actor) + const signingKey = await getSigningKey(userKEK, db, connectedActor) + await deliverToActor(signingKey, connectedActor, actor, activity, domain) + } } diff --git a/backend/src/activitypub/activities/handle.ts b/backend/src/activitypub/activities/handle.ts index c2841fd..7d9b3b6 100644 --- a/backend/src/activitypub/activities/handle.ts +++ b/backend/src/activitypub/activities/handle.ts @@ -387,6 +387,8 @@ export async function handle( break } + // FIXME: Requires alsoKnownAs to be set in both directions + // move followers { const collection = await getMetadata(fromActor.followers) diff --git a/backend/test/wildebeest/settings.spec.ts b/backend/test/wildebeest/settings.spec.ts index b7608ae..12cd9f6 100644 --- a/backend/test/wildebeest/settings.spec.ts +++ b/backend/test/wildebeest/settings.spec.ts @@ -12,6 +12,8 @@ describe('Wildebeest', () => { const db = await makeDB() const actor = await createPerson(domain, db, userKEK, 'sven@cloudflare.com') + let receivedActivity: any = null + globalThis.fetch = async (input: RequestInfo) => { if (input.toString() === 'https://example.com/.well-known/webfinger?resource=acct%3Atest%40example.com') { return new Response( @@ -32,14 +34,23 @@ describe('Wildebeest', () => { JSON.stringify({ id: 'https://social.com/someone', type: 'Person', + inbox: 'https://social.com/someone/inbox', }) ) } + const request = new Request(input) + if (request.url === 'https://social.com/someone/inbox') { + assert.equal(request.method, 'POST') + const data = await request.json() + receivedActivity = data + return new Response('') + } + throw new Error('unexpected request to ' + input) } - await alias.addAlias(db, 'test@example.com', actor) + await alias.addAlias(db, 'test@example.com', actor, userKEK, domain) // Ensure the actor has the alias set const newActor = await getActorById(db, actor.id) @@ -47,6 +58,9 @@ describe('Wildebeest', () => { assert(newActor.alsoKnownAs) assert.equal(newActor.alsoKnownAs.length, 1) assert.equal(newActor.alsoKnownAs[0], 'https://social.com/someone') + + assert(receivedActivity) + assert.equal(receivedActivity.type, 'Follow') }) }) }) diff --git a/frontend/src/routes/(admin)/settings/(auth)/aliases/index.tsx b/frontend/src/routes/(admin)/settings/(auth)/aliases/index.tsx index f2fdfd3..d376cda 100644 --- a/frontend/src/routes/(admin)/settings/(auth)/aliases/index.tsx +++ b/frontend/src/routes/(admin)/settings/(auth)/aliases/index.tsx @@ -8,7 +8,9 @@ const zodSchema = zod$({ alias: z.string().min(1), }) -export const action = action$(async (data, { platform, json }) => { +export const action = action$(async (data, { platform, json, request }) => { + const url = new URL(request.url) + const domain = url.hostname const db = await getDatabase(platform) const connectedActor = platform.data.connectedActor if (connectedActor === null) { @@ -16,7 +18,7 @@ export const action = action$(async (data, { platform, json }) => { } try { - await addAlias(db, data.alias, connectedActor) + await addAlias(db, data.alias, connectedActor, platform.userKEK, domain) } catch (e: unknown) { const error = e as { stack: string; cause: string } console.error(error.stack, error.cause)