From 4acc04cd9337a4ecaea72480124f88384602fbb7 Mon Sep 17 00:00:00 2001 From: Jesse Wierzbinski Date: Tue, 9 Apr 2024 19:13:13 -1000 Subject: [PATCH] Make WebFinger resolve work --- database/entities/Instance.ts | 18 +++++------ database/entities/User.ts | 51 +++++++++++++++---------------- server/api/api/v2/search/index.ts | 18 +++++++++-- 3 files changed, 48 insertions(+), 39 deletions(-) diff --git a/database/entities/Instance.ts b/database/entities/Instance.ts index 7d52b4e3..3f7aa21c 100644 --- a/database/entities/Instance.ts +++ b/database/entities/Instance.ts @@ -1,6 +1,6 @@ import type { Instance } from "@prisma/client"; import { client } from "~database/datasource"; -import type { ServerMetadata } from "~types/lysand/Object"; +import type * as Lysand from "lysand-types"; /** * Represents an instance in the database. @@ -15,32 +15,32 @@ export const addInstanceIfNotExists = async ( url: string, ): Promise => { const origin = new URL(url).origin; - const hostname = new URL(url).hostname; + const host = new URL(url).host; const found = await client.instance.findFirst({ where: { - base_url: hostname, + base_url: host, }, }); if (found) return found; // Fetch the instance configuration - const metadata = (await fetch(`${origin}/.well-known/lysand`).then((res) => - res.json(), - )) as Partial; + const metadata = (await fetch(new URL("/.well-known/lysand", origin)).then( + (res) => res.json(), + )) as Lysand.ServerMetadata; if (metadata.type !== "ServerMetadata") { - throw new Error("Invalid instance metadata"); + throw new Error("Invalid instance metadata (wrong type)"); } if (!(metadata.name && metadata.version)) { - throw new Error("Invalid instance metadata"); + throw new Error("Invalid instance metadata (missing name or version)"); } return await client.instance.create({ data: { - base_url: hostname, + base_url: host, name: metadata.name, version: metadata.version, logo: metadata.logo, diff --git a/database/entities/User.ts b/database/entities/User.ts index c5a79b73..661ac1e6 100644 --- a/database/entities/User.ts +++ b/database/entities/User.ts @@ -11,7 +11,7 @@ import { addEmojiIfNotExists, emojiToAPI, emojiToLysand } from "./Emoji"; import { addInstanceIfNotExists } from "./Instance"; import { userRelations } from "./relations"; import { createNewRelationship } from "./Relationship"; -import { urlToContentFormat } from "@content_types"; +import { getBestContentType, urlToContentFormat } from "@content_types"; export interface AuthData { user: UserWithRelations | null; @@ -172,6 +172,14 @@ export const resolveUser = async (uri: string) => { const userEmojis = data.extensions?.["org.lysand:custom_emojis"]?.emojis ?? []; + const instance = await addInstanceIfNotExists(data.uri); + + const emojis = []; + + for (const emoji of userEmojis) { + emojis.push(await addEmojiIfNotExists(emoji)); + } + const user = await client.user.create({ data: { username: data.username, @@ -186,10 +194,20 @@ export const resolveUser = async (uri: string) => { inbox: data.inbox, outbox: data.outbox, }, - avatar: data.avatar?.[0].content || "", - header: data.header?.[0].content || "", + emojis: { + connect: emojis.map((emoji) => ({ + id: emoji.id, + })), + }, + instanceId: instance.id, + avatar: data.avatar + ? Object.entries(data.avatar)[0][1].content + : "", + header: data.header + ? Object.entries(data.header)[0][1].content + : "", displayName: data.display_name ?? "", - note: data.bio?.[0].content ?? "", + note: getBestContentType(data.bio).content, publicKey: data.public_key.public_key, source: { language: null, @@ -199,33 +217,13 @@ export const resolveUser = async (uri: string) => { fields: [], }, }, + include: userRelations, }); // Add to Meilisearch await addUserToMeilisearch(user); - const emojis = []; - - for (const emoji of userEmojis) { - emojis.push(await addEmojiIfNotExists(emoji)); - } - - const uriData = new URL(data.uri); - - return await client.user.update({ - where: { - id: user.id, - }, - data: { - emojis: { - connect: emojis.map((emoji) => ({ - id: emoji.id, - })), - }, - instanceId: (await addInstanceIfNotExists(uriData.origin)).id, - }, - include: userRelations, - }); + return user; }; /** @@ -258,7 +256,6 @@ export const resolveWebFinger = async (identifier: string, host: string) => { { method: "GET", headers: { - "Content-Type": "application/json", Accept: "application/json", }, }, diff --git a/server/api/api/v2/search/index.ts b/server/api/api/v2/search/index.ts index 0da2b68b..00b6291a 100644 --- a/server/api/api/v2/search/index.ts +++ b/server/api/api/v2/search/index.ts @@ -76,8 +76,17 @@ export default apiRoute<{ if (!type || type === "accounts") { // Check if q is matching format username@domain.com or @username@domain.com - if (q?.trim().match(/@?[a-zA-Z0-9_]+(@[a-zA-Z0-9_.:]+)/g)) { - const [username, domain] = q.trim().split("@"); + const accountMatches = q + ?.trim() + .match(/@?[a-zA-Z0-9_]+(@[a-zA-Z0-9_.:]+)/g); + if (accountMatches) { + // Remove leading @ if it exists + if (accountMatches[0].startsWith("@")) { + accountMatches[0] = accountMatches[0].slice(1); + } + + const [username, domain] = accountMatches[0].split("@"); + const account = await client.user.findFirst({ where: { username, @@ -98,7 +107,10 @@ export default apiRoute<{ if (resolve) { const newUser = await resolveWebFinger(username, domain).catch( - () => null, + (e) => { + console.error(e); + return null; + }, ); if (newUser) {