import type { FastifyReply, FastifyRequest, RouteOptions } from "fastify"; import { IsNull } from "typeorm"; import log from "~/log.js"; import MercuryDataSource from "~/db/index.js"; import { Blog } from "~entities/blog.js"; import { BASE_URL } from "~/config.js"; const route: RouteOptions = { method: "GET", url: "/.well-known/webfinger", handler: async (req, res) => { // TypeScript complains if we just use plain `req.query` :( const encodedResource = (req.query as { resource: string }).resource; if (!encodedResource || typeof encodedResource !== "string") { res.status(400).send({ error: "resource query parameter is missing or invalid", }); return; } const resource = decodeURIComponent(encodedResource); log.debug("Handling WebFinger request for %s", resource); if (resource.startsWith("acct:")) { await handleAcctWebfinger(req, res, resource); return; } res.status(500).send({ error: "Unhandled WebFinger resource type" }); }, }; export default route; async function handleAcctWebfinger( req: FastifyRequest, res: FastifyReply, resource: string ): Promise { const [username, domain] = resource.slice("acct:".length).split("@"); if (domain !== process.env.DOMAIN) { res.status(404).send({ error: "Account not found", }); return; } const blog = await MercuryDataSource.getRepository(Blog).findOneBy({ username: username, host: IsNull(), }); if (!blog) { res.status(404).send({ error: "Account not found", }); return; } res.send({ subject: resource, aliases: [ `${BASE_URL}/@${blog.username}`, `${BASE_URL}/blogs/${blog.username}`, ], links: [ { rel: "http://webfinger.net/rel/profile-page", type: "text/html", href: `${BASE_URL}/@${blog.username}`, }, { rel: "self", type: "application/activity+json", href: `${BASE_URL}/blogs/${blog.username}`, }, ], }); }