diff options
Diffstat (limited to '')
-rw-r--r-- | src/activitypub/README.md | 2 | ||||
-rw-r--r-- | src/activitypub/federation/OrderedCollection.ts | 6 | ||||
-rw-r--r-- | src/activitypub/federation/inbox/index.ts | 2 | ||||
-rw-r--r-- | src/activitypub/federation/transforms.ts | 17 | ||||
-rw-r--r-- | src/activitypub/federation/utils.ts | 6 | ||||
-rw-r--r-- | src/activitypub/routes/guilds/#guild_id/followers.ts | 41 | ||||
-rw-r--r-- | src/util/entities/FederationKeys.ts | 20 |
7 files changed, 79 insertions, 15 deletions
diff --git a/src/activitypub/README.md b/src/activitypub/README.md index 836741ba..14451b39 100644 --- a/src/activitypub/README.md +++ b/src/activitypub/README.md @@ -97,7 +97,7 @@ Follows/is followed by it's corresponding Guild, if applicable. ## Guild Federation -An automated actor. Follows and is followed by it's corresponding Channels. +An automated actor. Follows its Channels. Is followed by guild members. Also contains a collection of [roles](#role-federation). ### Supported Activities diff --git a/src/activitypub/federation/OrderedCollection.ts b/src/activitypub/federation/OrderedCollection.ts index f3b8feea..aa462e64 100644 --- a/src/activitypub/federation/OrderedCollection.ts +++ b/src/activitypub/federation/OrderedCollection.ts @@ -1,7 +1,9 @@ -import { APOrderedCollection, AnyAPObject } from "activitypub-types"; +import { APOrderedCollection, CollectionCurrentField } from "activitypub-types"; import { ACTIVITYSTREAMS_CONTEXT } from "./utils"; -export const makeOrderedCollection = async <T extends AnyAPObject>(opts: { +export const makeOrderedCollection = async < + T extends CollectionCurrentField, +>(opts: { page: boolean; min_id?: string; max_id?: string; diff --git a/src/activitypub/federation/inbox/index.ts b/src/activitypub/federation/inbox/index.ts index 079c13de..3fbdfc4a 100644 --- a/src/activitypub/federation/inbox/index.ts +++ b/src/activitypub/federation/inbox/index.ts @@ -192,7 +192,7 @@ const addRemoteUserToGuild = async ( }, }); - const { entity, keys } = await fetchFederatedUser(actor); + const { entity } = await fetchFederatedUser(actor); await Member.addToGuild(entity.id, guild.actorId); diff --git a/src/activitypub/federation/transforms.ts b/src/activitypub/federation/transforms.ts index a11cc62b..b11005eb 100644 --- a/src/activitypub/federation/transforms.ts +++ b/src/activitypub/federation/transforms.ts @@ -211,13 +211,13 @@ export const transformUserToPerson = async (user: User): Promise<APPerson> => { const { host, accountDomain } = Config.get().federation; const keys = await FederationKey.findOneOrFail({ - where: { actorId: user.id, domain: accountDomain }, + where: { actorId: user.id }, }); return { "@context": ACTIVITYSTREAMS_CONTEXT, type: "Person", - id: `https://${host}/federation/users/${user.id}`, + id: keys.federatedId, name: user.username, preferredUsername: user.id, @@ -232,9 +232,10 @@ export const transformUserToPerson = async (user: User): Promise<APPerson> => { ] : undefined, - inbox: `https://${host}/federation/users/${user.id}/inbox`, - outbox: `https://${host}/federation/users/${user.id}/outbox`, - followers: `https://${host}/federation/users/${user.id}/followers`, + inbox: keys.inbox, + outbox: keys.outbox, + followers: keys.followers, + following: keys.following, publicKey: { id: `https://${host}/federation/users/${user.id}#main-key`, owner: `https://${host}/federation/users/${user.id}`, @@ -266,6 +267,8 @@ export const transformPersonToUser = async (person: APPerson) => { type: ActorType.USER, inbox: person.inbox.toString(), outbox: person.outbox.toString(), + followers: person.followers?.toString(), + following: person.following?.toString(), }).save(); return await User.create({ @@ -332,6 +335,8 @@ export const transformOrganisationToGuild = async (org: APOrganization) => { type: ActorType.GUILD, inbox: org.inbox.toString(), outbox: org.outbox.toString(), + followers: org.followers?.toString(), + following: org.following?.toString(), }); if (typeof org.attributedTo != "string") @@ -423,6 +428,8 @@ export const transformGroupToChannel = async ( type: ActorType.CHANNEL, inbox: group.inbox.toString(), outbox: group.outbox.toString(), + followers: group.followers?.toString(), + following: group.following?.toString(), }); const channel = Channel.create({ diff --git a/src/activitypub/federation/utils.ts b/src/activitypub/federation/utils.ts index e879e863..4a56c67d 100644 --- a/src/activitypub/federation/utils.ts +++ b/src/activitypub/federation/utils.ts @@ -191,8 +191,10 @@ export const fetchFederatedUser = async ( domain: mention.domain, publicKey: remoteActor.publicKey?.publicKeyPem, type, - inbox: remoteActor.inbox, - outbox: remoteActor.outbox, + inbox: remoteActor.inbox?.toString(), + outbox: remoteActor.outbox?.toString(), + following: remoteActor.following?.toString(), + followers: remoteActor.followers?.toString(), }); let entity: BaseClass | undefined = undefined; diff --git a/src/activitypub/routes/guilds/#guild_id/followers.ts b/src/activitypub/routes/guilds/#guild_id/followers.ts new file mode 100644 index 00000000..7f3b9e31 --- /dev/null +++ b/src/activitypub/routes/guilds/#guild_id/followers.ts @@ -0,0 +1,41 @@ +import { makeOrderedCollection } from "@spacebar/ap"; +import { route } from "@spacebar/api"; +import { + Channel, + Config, + Datasource, + FederationKey, + Member, +} from "@spacebar/util"; +import { Request, Response, Router } from "express"; +const router = Router(); + +router.get("/", route({}), async (req: Request, res: Response) => { + const { guild_id } = req.params; + const { page, min_id, max_id } = req.query; + + const { host } = Config.get().federation; + + const ret = await makeOrderedCollection({ + page: page != undefined, + min_id: min_id?.toString(), + max_id: max_id?.toString(), + id: `https://${host}/federation/guilds/${guild_id}/following`, + getTotalElements: () => Channel.count({ where: { guild_id } }), + getElements: async (before, after): Promise<string[]> => { + const members = await Datasource.getRepository(FederationKey) + .createQueryBuilder("key") + .leftJoin(Member, "member", "member.id == key.actorId") + .where("member.guild_id = :guild_id", { guild_id }) + .getMany(); + + // TODO: actual pagination + + return members.map((x) => x.federatedId); + }, + }); + + return res.json(ret); +}); + +export default router; diff --git a/src/util/entities/FederationKeys.ts b/src/util/entities/FederationKeys.ts index 332e13e1..86e72bd2 100644 --- a/src/util/entities/FederationKeys.ts +++ b/src/util/entities/FederationKeys.ts @@ -34,12 +34,20 @@ export class FederationKey extends BaseClassWithoutId { federatedId: string; /** The inbox of the remote user */ - @Column({ nullable: true, type: String }) - inbox: string | null; + @Column() + inbox: string; /** The outbox of the remote user */ - @Column({ nullable: true, type: String }) - outbox: string | null; + @Column() + outbox: string; + + /** The following collection of this user */ + @Column() + following: string; + + /** The followers collection of this user */ + @Column() + followers: string; /** The public key of this actor. Public keys of remote actors are cached. */ @Column() @@ -63,6 +71,10 @@ export class FederationKey extends BaseClassWithoutId { actorId, type, federatedId: `https://${host}/federation/${type}/${actorId}`, + inbox: `https://${host}/federation/${type}/${actorId}/inbox`, + outbox: `https://${host}/federation/${type}/${actorId}/outbox`, + followers: `https://${host}/federation/${type}/${actorId}/followers`, + following: `https://${host}/federation/${type}/${actorId}/following`, domain: accountDomain, ...(await generateKeyPair("rsa", { modulusLength: 4096, |