summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorMadeline <46743919+MaddyUnderStars@users.noreply.github.com>2023-09-26 13:47:44 +0000
committerMadeline <46743919+MaddyUnderStars@users.noreply.github.com>2023-09-26 13:47:44 +0000
commit0bc905a222eed993f65e41700ddedeec7f0eda63 (patch)
treee2e6a811c47d63ba19b0dea1bbb7791b06f87f01 /src
parentvarious things (diff)
downloadserver-0bc905a222eed993f65e41700ddedeec7f0eda63.tar.xz
switch to own activitypub types library
Diffstat (limited to 'src')
-rw-r--r--src/activitypub/federation/OrderedCollection.ts10
-rw-r--r--src/activitypub/federation/index.ts4
-rw-r--r--src/activitypub/federation/queue.ts6
-rw-r--r--src/activitypub/federation/transforms.ts65
-rw-r--r--src/activitypub/federation/utils.ts32
-rw-r--r--src/activitypub/routes/channels/#channel_id/inbox.ts27
-rw-r--r--src/activitypub/routes/channels/#channel_id/outbox.ts6
-rw-r--r--src/activitypub/routes/users/#user_id/inbox.ts30
8 files changed, 61 insertions, 119 deletions
diff --git a/src/activitypub/federation/OrderedCollection.ts b/src/activitypub/federation/OrderedCollection.ts
index 70a40754..d530deb8 100644
--- a/src/activitypub/federation/OrderedCollection.ts
+++ b/src/activitypub/federation/OrderedCollection.ts
@@ -1,14 +1,14 @@
-import { AP } from "activitypub-core-types";
+import { APOrderedCollection, AnyAPObject } from "activitypub-types";
 import { ACTIVITYSTREAMS_CONTEXT } from "./utils";
 
-export const makeOrderedCollection = async <T extends AP.CoreObject>(opts: {
+export const makeOrderedCollection = async <T extends AnyAPObject>(opts: {
 	page: boolean;
 	min_id?: string;
 	max_id?: string;
-	id: URL;
+	id: string;
 	getTotalElements: () => Promise<number>;
 	getElements: (before?: string, after?: string) => Promise<T[]>;
-}): Promise<AP.OrderedCollection> => {
+}): Promise<APOrderedCollection> => {
 	const { page, min_id, max_id, id, getTotalElements, getElements } = opts;
 
 	if (!page)
@@ -28,7 +28,7 @@ export const makeOrderedCollection = async <T extends AP.CoreObject>(opts: {
 
 	return {
 		"@context": ACTIVITYSTREAMS_CONTEXT,
-		id: new URL(`${id}?page=true`),
+		id: `${id}?page=true`,
 		type: "OrderedCollection",
 		first: new URL(`${id}?page=true`),
 		last: new URL(`${id}?page=true&min_id=0`),
diff --git a/src/activitypub/federation/index.ts b/src/activitypub/federation/index.ts
index 62c93b18..072bc878 100644
--- a/src/activitypub/federation/index.ts
+++ b/src/activitypub/federation/index.ts
@@ -3,7 +3,7 @@
  * Responsible for dispatching activitypub events to external instances
  */
 
-import { AP } from "activitypub-core-types";
+import { APActivity } from "activitypub-types";
 import { federationQueue } from "./queue";
 
 export * from "./OrderedCollection";
@@ -11,7 +11,7 @@ export * from "./transforms";
 export * from "./utils";
 
 export class Federation {
-	static async distribute(activity: AP.Activity) {
+	static async distribute(activity: APActivity) {
 		await federationQueue.distribute(activity);
 	}
 }
diff --git a/src/activitypub/federation/queue.ts b/src/activitypub/federation/queue.ts
index dff08e37..3bee2365 100644
--- a/src/activitypub/federation/queue.ts
+++ b/src/activitypub/federation/queue.ts
@@ -1,16 +1,16 @@
 import { Config, FederationKey } from "@spacebar/util";
-import { AP } from "activitypub-core-types";
 import fetch from "node-fetch";
 import { APError, signActivity, splitQualifiedMention } from "./utils";
+import { APActivity } from "activitypub-types";
 
 //
 type Instance = string;
 
 class FederationQueue {
 	// TODO: queue messages and send them to shared inbox
-	private queue: Map<Instance, Array<AP.Activity>> = new Map();
+	private queue: Map<Instance, Array<APActivity>> = new Map();
 
-	public async distribute(activity: AP.Activity) {
+	public async distribute(activity: APActivity) {
 		let { to, actor } = activity;
 
 		if (!to)
diff --git a/src/activitypub/federation/transforms.ts b/src/activitypub/federation/transforms.ts
index d2c05c24..3718d7df 100644
--- a/src/activitypub/federation/transforms.ts
+++ b/src/activitypub/federation/transforms.ts
@@ -10,20 +10,19 @@ import {
 	User,
 	UserSettings,
 } from "@spacebar/util";
-import { AP } from "activitypub-core-types";
 import TurndownService from "turndown";
 import { In } from "typeorm";
 import {
 	ACTIVITYSTREAMS_CONTEXT,
 	APError,
 	APObjectIsPerson,
-	APObjectIsSpacebarActor,
 	resolveAPObject,
 } from "./utils";
+import { APAnnounce, APGroup, APNote, APPerson } from "activitypub-types";
 
 export const transformMessageToAnnounceNoce = async (
 	message: Message,
-): Promise<AP.Announce> => {
+): Promise<APAnnounce> => {
 	const { host } = Config.get().federation;
 
 	const channel = await Channel.findOneOrFail({
@@ -34,10 +33,9 @@ export const transformMessageToAnnounceNoce = async (
 	});
 
 	let to = [
-		new URL(
-			`https://${host}/federation/channels/${message.channel_id}/followers`,
-		),
+		`https://${host}/federation/channels/${message.channel_id}/followers`,
 	];
+
 	if (channel.isDm()) {
 		const otherUsers = channel.recipients?.filter(
 			(x) => x.user_id != message.author_id,
@@ -48,27 +46,25 @@ export const transformMessageToAnnounceNoce = async (
 		});
 
 		to = remoteUsersKeys.map((x) =>
-			x.inbox ? new URL(x.inbox!) : new URL(`${x.federatedId}/inbox`),
+			x.inbox ? x.inbox! : `${x.federatedId}/inbox`,
 		);
 	}
 
 	return {
 		"@context": ACTIVITYSTREAMS_CONTEXT,
 		type: "Announce",
-		id: new URL(
-			`https://${host}/federation/channels/${message.channel_id}/messages/${message.id}`,
-		),
+		id: `https://${host}/federation/channels/${message.channel_id}/messages/${message.id}`,
 		// this is wrong for remote users
-		actor: new URL(`https://${host}/federation/users/${message.author_id}`),
+		actor: `https://${host}/federation/users/${message.author_id}`,
 		published: message.timestamp,
 		to,
 		object: await transformMessageToNote(message),
-	};
+	} as APAnnounce;
 };
 
 export const transformMessageToNote = async (
 	message: Message,
-): Promise<AP.Note> => {
+): Promise<APNote> => {
 	const { host } = Config.get().federation;
 
 	const referencedMessage = message.message_reference
@@ -78,23 +74,18 @@ export const transformMessageToNote = async (
 		: null;
 
 	return {
-		id: new URL(`https://${host}/federation/messages/${message.id}`),
+		id: `https://${host}/federation/messages/${message.id}`,
 		type: "Note",
 		content: message.content, // TODO: convert markdown to html
 		inReplyTo: referencedMessage
 			? await transformMessageToNote(referencedMessage)
 			: undefined,
 		published: message.timestamp,
-		attributedTo: new URL(
-			`https://${host}/federation/users/${message.author_id}`,
-		),
-		to: [
-			new URL(
-				`https://${host}/federation/channels/${message.channel_id}`,
-			),
-		],
+		attributedTo: `https://${host}/federation/users/${message.author_id}`,
+
+		to: [`https://${host}/federation/channels/${message.channel_id}`],
 		tag: message.mentions?.map(
-			(x) => new URL(`https://${host}/federation/users/${x.id}`),
+			(x) => `https://${host}/federation/users/${x.id}`,
 		),
 		attachment: [],
 		// replies: [],
@@ -105,7 +96,7 @@ export const transformMessageToNote = async (
 };
 
 // TODO: this was copied from the previous implemention. refactor it.
-export const transformNoteToMessage = async (note: AP.Note) => {
+export const transformNoteToMessage = async (note: APNote) => {
 	if (!note.id) throw new APError("Note must have ID");
 	if (note.type != "Note") throw new APError("Message must be Note");
 
@@ -177,7 +168,7 @@ export const transformNoteToMessage = async (note: AP.Note) => {
 
 export const transformChannelToGroup = async (
 	channel: Channel,
-): Promise<AP.Group> => {
+): Promise<APGroup> => {
 	const { host, accountDomain } = Config.get().federation;
 
 	const keys = await FederationKey.findOneOrFail({
@@ -187,7 +178,7 @@ export const transformChannelToGroup = async (
 	return {
 		"@context": "https://www.w3.org/ns/activitystreams",
 		type: "Group",
-		id: new URL(`https://${host}/fed/channels/${channel.id}`),
+		id: `https://${host}/fed/channels/${channel.id}`,
 		name: channel.name,
 		preferredUsername: channel.id,
 		summary: channel.topic,
@@ -200,15 +191,13 @@ export const transformChannelToGroup = async (
 			publicKeyPem: keys.publicKey,
 		},
 
-		inbox: new URL(`https://${host}/fed/channels/${channel.id}/inbox`),
-		outbox: new URL(`https://${host}/fed/channels/${channel.id}/outbox`),
-		followers: new URL(
-			`https://${host}/fed/channels/${channel.id}/followers`,
-		),
+		inbox: `https://${host}/fed/channels/${channel.id}/inbox`,
+		outbox: `https://${host}/fed/channels/${channel.id}/outbox`,
+		followers: `https://${host}/fed/channels/${channel.id}/followers`,
 	};
 };
 
-export const transformUserToPerson = async (user: User): Promise<AP.Person> => {
+export const transformUserToPerson = async (user: User): Promise<APPerson> => {
 	const { host, accountDomain } = Config.get().federation;
 
 	const keys = await FederationKey.findOneOrFail({
@@ -218,7 +207,7 @@ export const transformUserToPerson = async (user: User): Promise<AP.Person> => {
 	return {
 		"@context": ACTIVITYSTREAMS_CONTEXT,
 		type: "Person",
-		id: new URL(`https://${host}/federation/users/${user.id}`),
+		id: `https://${host}/federation/users/${user.id}`,
 
 		name: user.username,
 		preferredUsername: user.id,
@@ -233,11 +222,9 @@ export const transformUserToPerson = async (user: User): Promise<AP.Person> => {
 			  ]
 			: undefined,
 
-		inbox: new URL(`https://${host}/federation/users/${user.id}/inbox`),
-		outbox: new URL(`https://${host}/federation/users/${user.id}/outbox`),
-		followers: new URL(
-			`https://${host}/federation/users/${user.id}/followers`,
-		),
+		inbox: `https://${host}/federation/users/${user.id}/inbox`,
+		outbox: `https://${host}/federation/users/${user.id}/outbox`,
+		followers: `https://${host}/federation/users/${user.id}/followers`,
 		publicKey: {
 			id: `https://${host}/federation/users/${user.id}#main-key`,
 			owner: `https://${host}/federation/users/${user.id}`,
@@ -247,7 +234,7 @@ export const transformUserToPerson = async (user: User): Promise<AP.Person> => {
 };
 
 // TODO: this was copied from previous implementation. refactor.
-export const transformPersonToUser = async (person: AP.Person) => {
+export const transformPersonToUser = async (person: APPerson) => {
 	if (!person.id) throw new APError("User must have ID");
 
 	const url = new URL(person.id.toString());
diff --git a/src/activitypub/federation/utils.ts b/src/activitypub/federation/utils.ts
index 10a8346e..0ff596a2 100644
--- a/src/activitypub/federation/utils.ts
+++ b/src/activitypub/federation/utils.ts
@@ -5,7 +5,13 @@ import {
 	OrmUtils,
 	WebfingerResponse,
 } from "@spacebar/util";
-import { AP } from "activitypub-core-types";
+import {
+	APActivity,
+	APActor,
+	APObject,
+	APPerson,
+	AnyAPObject,
+} from "activitypub-types";
 import crypto from "crypto";
 import { HTTPError } from "lambert-server";
 import fetch from "node-fetch";
@@ -30,7 +36,7 @@ export const hasAPContext = (data: object) => {
 	return context == activitystreams;
 };
 
-export const resolveAPObject = async <T extends object>(
+export const resolveAPObject = async <T extends AnyAPObject>(
 	data: string | T,
 ): Promise<T> => {
 	// we were already given an AP object
@@ -79,7 +85,7 @@ export const splitQualifiedMention = (lookup: string) => {
 
 export const resolveWebfinger = async (
 	lookup: string,
-): Promise<AP.CoreObject> => {
+): Promise<AnyAPObject> => {
 	const { domain } = splitQualifiedMention(lookup);
 
 	const agent = new ProxyAgent();
@@ -94,7 +100,7 @@ export const resolveWebfinger = async (
 	const link = wellknown.links.find((x) => x.rel == "self");
 	if (!link) throw new APError(".well-known did not contain rel=self link");
 
-	return await resolveAPObject<AP.CoreObject>(link.href);
+	return await resolveAPObject<AnyAPObject>(link.href);
 };
 
 /**
@@ -107,7 +113,7 @@ export const resolveWebfinger = async (
 export const signActivity = async (
 	inbox: string,
 	sender: FederationKey,
-	message: AP.Activity,
+	message: APActivity,
 ) => {
 	if (!sender.privateKey)
 		throw new APError("cannot sign without private key");
@@ -152,27 +158,23 @@ export const signActivity = async (
 };
 
 // fetch from remote server?
-export const APObjectIsPerson = (
-	object: AP.EntityReference,
-): object is AP.Person => {
+export const APObjectIsPerson = (object: AnyAPObject): object is APPerson => {
 	return "type" in object && object.type == "Person";
 };
 
-export const APObjectIsGroup = (
-	object: AP.EntityReference,
-): object is AP.Person => {
+export const APObjectIsGroup = (object: AnyAPObject): object is APPerson => {
 	return "type" in object && object.type == "Group";
 };
 
 export const APObjectIsOrganisation = (
-	object: AP.EntityReference,
-): object is AP.Person => {
+	object: AnyAPObject,
+): object is APPerson => {
 	return "type" in object && object.type == "Organization";
 };
 
 export const APObjectIsSpacebarActor = (
-	object: AP.EntityReference,
-): object is AP.Person => {
+	object: AnyAPObject,
+): object is APPerson => {
 	return (
 		APObjectIsGroup(object) ||
 		APObjectIsOrganisation(object) ||
diff --git a/src/activitypub/routes/channels/#channel_id/inbox.ts b/src/activitypub/routes/channels/#channel_id/inbox.ts
index 5414ab48..896522b7 100644
--- a/src/activitypub/routes/channels/#channel_id/inbox.ts
+++ b/src/activitypub/routes/channels/#channel_id/inbox.ts
@@ -1,35 +1,14 @@
 import { transformNoteToMessage } from "@spacebar/ap";
 import { route } from "@spacebar/api";
 import { Message, emitEvent } from "@spacebar/util";
-import { AP } from "activitypub-core-types";
+import { APCreate, APNote } from "activitypub-types";
 import { Request, Response, Router } from "express";
 import { HTTPError } from "lambert-server";
 const router = Router();
 
-// TODO: check if the activity exists on the remote server
 router.post("/", route({}), async (req: Request, res: Response) => {
-	const body = req.body as AP.Create;
-
-	if (body.type != "Create") throw new HTTPError("not implemented");
-
-	const object = Array.isArray(body.object) ? body.object[0] : body.object;
-	if (!object) return res.status(400);
-	if (!("type" in object) || object.type != "Note")
-		throw new HTTPError("must be Note");
-	const message = await transformNoteToMessage(object as AP.Note);
-
-	if ((await Message.count({ where: { nonce: object.id!.toString() } })) != 0)
-		return res.status(200);
-
-	await message.save();
-
-	await emitEvent({
-		event: "MESSAGE_CREATE",
-		channel_id: message.channel_id,
-		data: message.toJSON(),
-	});
-
-	return res.status(200);
+	// TODO: check if the activity exists on the remote server
+	// TODO: refactor
 });
 
 export default router;
diff --git a/src/activitypub/routes/channels/#channel_id/outbox.ts b/src/activitypub/routes/channels/#channel_id/outbox.ts
index 4eedb210..c6388fda 100644
--- a/src/activitypub/routes/channels/#channel_id/outbox.ts
+++ b/src/activitypub/routes/channels/#channel_id/outbox.ts
@@ -4,7 +4,7 @@ import {
 } from "@spacebar/ap";
 import { route } from "@spacebar/api";
 import { Config, Message, Snowflake } from "@spacebar/util";
-import { AP } from "activitypub-core-types";
+import { APAnnounce } from "activitypub-types";
 import { Request, Response, Router } from "express";
 import { FindManyOptions, FindOperator, LessThan, MoreThan } from "typeorm";
 const router = Router();
@@ -19,9 +19,9 @@ router.get("/", route({}), async (req: Request, res: Response) => {
 		page: page != undefined,
 		min_id: min_id?.toString(),
 		max_id: max_id?.toString(),
-		id: new URL(`https://${host}/federation/channels/${channel_id}/outbox`),
+		id: `https://${host}/federation/channels/${channel_id}/outbox`,
 		getTotalElements: () => Message.count({ where: { channel_id } }),
-		getElements: async (before, after): Promise<AP.Announce[]> => {
+		getElements: async (before, after): Promise<APAnnounce[]> => {
 			const query: FindManyOptions<Message> & {
 				where: { id?: FindOperator<string> | FindOperator<string>[] };
 			} = {
diff --git a/src/activitypub/routes/users/#user_id/inbox.ts b/src/activitypub/routes/users/#user_id/inbox.ts
index ad1ed8c1..6734f37c 100644
--- a/src/activitypub/routes/users/#user_id/inbox.ts
+++ b/src/activitypub/routes/users/#user_id/inbox.ts
@@ -1,36 +1,10 @@
-import { transformNoteToMessage } from "@spacebar/ap";
 import { route } from "@spacebar/api";
-import { Message, emitEvent } from "@spacebar/util";
-import { AP } from "activitypub-core-types";
 import { Request, Response, Router } from "express";
-import { HTTPError } from "lambert-server";
 const router = Router();
 
-// TODO: check if the activity exists on the remote server
-// TODO: support lemmy ChatMessage type?
 router.post("/", route({}), async (req: Request, res: Response) => {
-	const body = req.body as AP.Create;
-
-	if (body.type != "Create") throw new HTTPError("not implemented");
-
-	const object = Array.isArray(body.object) ? body.object[0] : body.object;
-	if (!object) return res.status(400);
-	if (!("type" in object) || object.type != "Note")
-		throw new HTTPError("must be Note");
-	const message = await transformNoteToMessage(object as AP.Note);
-
-	if ((await Message.count({ where: { nonce: object.id!.toString() } })) != 0)
-		return res.status(200);
-
-	await message.save();
-
-	await emitEvent({
-		event: "MESSAGE_CREATE",
-		channel_id: message.channel_id,
-		data: message.toJSON(),
-	});
-
-	return res.status(200);
+	// TODO: support lemmy ChatMessage type?
+	// TODO: check if the activity exists on the remote server
 });
 
 export default router;