diff --git a/src/ap/blog.ts b/src/ap/blog.ts new file mode 100644 index 0000000..8a4f56f --- /dev/null +++ b/src/ap/blog.ts @@ -0,0 +1,20 @@ +import { BASE_URL } from "~/config.js"; +import { Blog } from "~/db/entities/blog.js"; + +/** Transforms the given Blog into an ActivityPub Person. It is the caller's responsibility to ensure the blog is local. */ +export function blogToActivityPub(blog: Blog) { + return { + "@context": "https://www.w3.org/ns/activitystreams", + type: "Person", + id: `${BASE_URL}/blogs/${blog.username}`, + inbox: `${BASE_URL}/blogs/${blog.username}/inbox`, + outbox: `${BASE_URL}/blogs/${blog.username}/outbox`, + name: blog.username, + preferredUsername: blog.username, + publicKey: { + id: `${BASE_URL}/blogs/${blog.username}#main-key`, + owner: `${BASE_URL}/blogs/${blog.username}`, + publicKeyPem: blog.publicKey, + }, + }; +} diff --git a/src/routes/ap/blog.ts b/src/routes/ap/blog.ts new file mode 100644 index 0000000..3dd13b0 --- /dev/null +++ b/src/routes/ap/blog.ts @@ -0,0 +1,31 @@ +import { RouteOptions } from "fastify"; + +import MercuryDataSource from "~/db/index.js"; +import { Blog } from "~/db/entities/blog.js"; +import { IsNull } from "typeorm"; +import { blogToActivityPub } from "~/ap/blog.js"; + +const route: RouteOptions = { + method: "GET", + url: "/blogs/:username", + handler: async (req, res) => { + const username = (req.params as { username: string }).username; + + // Only respond to ActivityPub requests + if (req.headers.accept && !req.headers.accept.indexOf("application/json")) { + res.redirect(303, `/@${username}`); + } + + const blog = await MercuryDataSource.getRepository(Blog).findOneBy({ + username, + host: IsNull(), + }); + if (!blog) { + return res.status(404).send({ message: "Not found" }); + } + + return res.send(blogToActivityPub(blog)); + }, +}; + +export default route; diff --git a/src/routes/nodeinfo/nodeinfo_2.0.ts b/src/routes/nodeinfo/nodeinfo_2.0.ts index 197a32b..122a60b 100644 --- a/src/routes/nodeinfo/nodeinfo_2.0.ts +++ b/src/routes/nodeinfo/nodeinfo_2.0.ts @@ -9,13 +9,15 @@ const route: RouteOptions = { method: "GET", url: "/nodeinfo/2.0", handler: async (_, res) => { - const userCount = await MercuryDataSource.getRepository(Blog).countBy({ - host: IsNull(), - }); - const postCount = await MercuryDataSource.getRepository(Post).count({ - relations: { blog: true }, - where: { blog: { host: IsNull() } }, - }); + const [userCount, postCount] = await Promise.all([ + MercuryDataSource.getRepository(Blog).countBy({ + host: IsNull(), + }), + MercuryDataSource.getRepository(Post).count({ + relations: { blog: true }, + where: { blog: { host: IsNull() } }, + }), + ]); res.send({ version: "2.0",