refactor(federation): ♻️ Replace WebFinger code with @lysand-org/federation logic, add new debug command

This commit is contained in:
Jesse Wierzbinski 2024-06-29 22:24:10 -10:00
parent 38c8ea24a9
commit cea9452127
No known key found for this signature in database
15 changed files with 256 additions and 99 deletions

View file

@ -2,6 +2,8 @@ import { mentionValidator } from "@/api";
import { sanitizeHtml, sanitizeHtmlInline } from "@/sanitization";
import markdownItTaskLists from "@hackmd/markdown-it-task-lists";
import { getLogger } from "@logtape/logtape";
import { SignatureConstructor } from "@lysand-org/federation";
import { FederationRequester } from "@lysand-org/federation/requester";
import type { ContentFormat } from "@lysand-org/federation/types";
import { config } from "config-manager";
import {
@ -41,7 +43,6 @@ import { objectToInboxRequest } from "./federation";
import {
type UserWithInstance,
type UserWithRelations,
resolveWebFinger,
transformOutputToUserWithRelations,
userExtrasTemplate,
userRelations,
@ -255,7 +256,10 @@ export const findManyNotes = async (
* @param text The text to parse mentions from.
* @returns An array of users mentioned in the text.
*/
export const parseTextMentions = async (text: string): Promise<User[]> => {
export const parseTextMentions = async (
text: string,
author: User,
): Promise<User[]> => {
const mentionedPeople = [...text.matchAll(mentionValidator)] ?? [];
if (mentionedPeople.length === 0) {
return [];
@ -310,10 +314,18 @@ export const parseTextMentions = async (text: string): Promise<User[]> => {
// Attempt to resolve mentions that were not found
for (const person of notFoundRemoteUsers) {
const user = await resolveWebFinger(
person?.[1] ?? "",
person?.[2] ?? "",
const signatureConstructor = await SignatureConstructor.fromStringKey(
author.data.privateKey ?? "",
author.getUri(),
);
const manager = new FederationRequester(
new URL(`https://${person?.[2] ?? ""}`),
signatureConstructor,
);
const uri = await manager.webFinger(person?.[1] ?? "");
const user = await User.resolve(uri);
if (user) {
finalList.push(user);

View file

@ -9,12 +9,12 @@ import { type InferSelectModel, and, eq, sql } from "drizzle-orm";
import { db } from "~/drizzle/db";
import {
Applications,
Instances,
type Instances,
Notifications,
Relationships,
type Roles,
Tokens,
Users,
type Users,
} from "~/drizzle/schema";
import { User } from "~/packages/database-interface/user";
import type { Application } from "./application";
@ -319,74 +319,6 @@ export const findManyUsers = async (
return output.map((user) => transformOutputToUserWithRelations(user));
};
/**
* Resolves a WebFinger identifier to a user.
* @param identifier Either a UUID or a username
*/
export const resolveWebFinger = async (
identifier: string,
host: string,
): Promise<User | null> => {
// Check if user not already in database
const foundUser = await db
.select()
.from(Users)
.innerJoin(Instances, eq(Users.instanceId, Instances.id))
.where(and(eq(Users.username, identifier), eq(Instances.baseUrl, host)))
.limit(1);
if (foundUser[0]) {
return await User.fromId(foundUser[0].Users.id);
}
const hostWithProtocol = host.startsWith("http") ? host : `https://${host}`;
const response = await fetch(
new URL(
`/.well-known/webfinger?${new URLSearchParams({
resource: `acct:${identifier}@${host}`,
})}`,
hostWithProtocol,
),
{
method: "GET",
headers: {
Accept: "application/json",
},
proxy: config.http.proxy.address,
},
);
if (response.status === 404) {
return null;
}
const data = (await response.json()) as {
subject: string;
links: {
rel: string;
type: string;
href: string;
}[];
};
if (!(data.subject && data.links)) {
throw new Error(
"Invalid WebFinger data (missing subject or links from response)",
);
}
const relevantLink = data.links.find((link) => link.rel === "self");
if (!relevantLink) {
throw new Error(
"Invalid WebFinger data (missing link with rel: 'self')",
);
}
return User.resolve(relevantLink.href);
};
/**
* Retrieves a user from a token.
* @param access_token The access token to retrieve the user from.