mirror of
https://github.com/versia-pub/server.git
synced 2026-03-13 05:49:16 +01:00
refactor(federation): ♻️ Replace WebFinger code with @lysand-org/federation logic, add new debug command
This commit is contained in:
parent
38c8ea24a9
commit
cea9452127
15 changed files with 256 additions and 99 deletions
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue