From eb466a0cc7e97ce19b19134bb97cf27e63cc6dda Mon Sep 17 00:00:00 2001 From: Jesse Wierzbinski Date: Mon, 25 Nov 2024 17:05:53 +0100 Subject: [PATCH] feat(federation): :sparkles: Implement Shared Inboxes --- CHANGELOG.md | 1 + api/inbox/index.ts | 73 ++++++++++++++++++++++++++++++++++++++++ api/well-known/versia.ts | 4 +++ 3 files changed, 78 insertions(+) create mode 100644 api/inbox/index.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index c050e7e5..aedfc16a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ Versia Server `0.8.0` is fully backwards compatible with `0.7.0`. - Added an administration UI for managing the queue. - Upgraded Bun to `1.1.36`. - Implemented support for the [Instance Messaging Extension](https://versia.pub/extensions/instance-messaging) +- Implement [Shared Inboxes](https://versia.pub/federation#inboxes) - Allowed `
` and `` tags in Markdown. - Added `--password` flag to the user creation CLI. diff --git a/api/inbox/index.ts b/api/inbox/index.ts new file mode 100644 index 00000000..d7be515f --- /dev/null +++ b/api/inbox/index.ts @@ -0,0 +1,73 @@ +import { apiRoute, applyConfig } from "@/api"; +import { createRoute } from "@hono/zod-openapi"; +import type { Entity } from "@versia/federation/types"; +import { z } from "zod"; +import { InboxJobType, inboxQueue } from "~/worker"; + +export const meta = applyConfig({ + auth: { + required: false, + }, + ratelimits: { + duration: 60, + max: 500, + }, + route: "/inbox", +}); + +export const schemas = { + header: z.object({ + "x-signature": z.string().optional(), + "x-nonce": z.string().optional(), + "x-signed-by": z + .string() + .url() + .or(z.string().startsWith("instance ")) + .optional(), + authorization: z.string().optional(), + }), + body: z.any(), +}; + +const route = createRoute({ + method: "post", + path: "/inbox", + summary: "Instance federation inbox", + request: { + headers: schemas.header, + body: { + content: { + "application/json": { + schema: schemas.body, + }, + }, + }, + }, + responses: { + 200: { + description: "Request processing initiated", + }, + }, +}); + +export default apiRoute((app) => + app.openapi(route, async (context) => { + const body: Entity = await context.req.valid("json"); + + await inboxQueue.add(InboxJobType.ProcessEntity, { + data: body, + headers: context.req.valid("header"), + request: { + body: await context.req.text(), + method: context.req.method, + url: context.req.url, + }, + ip: context.env.ip ?? null, + }); + + return context.newResponse( + "Request processing initiated.\nImplement the Instance Messaging Extension to receive any eventual errors", + 200, + ); + }), +); diff --git a/api/well-known/versia.ts b/api/well-known/versia.ts index 002d29d0..2d765354 100644 --- a/api/well-known/versia.ts +++ b/api/well-known/versia.ts @@ -63,6 +63,10 @@ export default apiRoute((app) => }, banner: urlToContentFormat(config.instance.banner), logo: urlToContentFormat(config.instance.logo), + shared_inbox: new URL( + "/inbox", + config.http.base_url, + ).toString(), created_at: new Date( firstUser?.data.createdAt ?? 0, ).toISOString(),