From 5fd6a4e43d48d642ebcdc74c5c73441aeeef6f9e Mon Sep 17 00:00:00 2001 From: Jesse Wierzbinski Date: Tue, 14 May 2024 14:35:13 -1000 Subject: [PATCH] feat(federation): :heavy_plus_sign: Replace old types and federation validators with @lysand-org/federation --- bun.lockb | Bin 281636 -> 282164 bytes database/entities/Attachment.ts | 6 ++-- database/entities/Emoji.ts | 12 ++++---- database/entities/Federation.ts | 4 +-- database/entities/Instance.ts | 4 +-- database/entities/Like.ts | 4 +-- database/entities/Status.ts | 10 +++---- database/entities/User.ts | 11 ++++---- drizzle/schema.ts | 6 ++-- package.json | 1 + packages/database-interface/note.ts | 14 ++++++---- packages/database-interface/user.ts | 19 +++++++++---- server/api/objects/[uuid]/index.ts | 4 +-- server/api/users/:uuid/inbox/index.ts | 38 +++++++++++++------------- server/api/well-known/lysand.ts | 4 +-- utils/content_types.ts | 8 ++++-- 16 files changed, 80 insertions(+), 65 deletions(-) diff --git a/bun.lockb b/bun.lockb index bf8a3eb6782d7b35d2d535dea7160af4c53bda8b..7635f41bbb0177606fae0e6ba49146122df8e959 100755 GIT binary patch delta 47307 zcmeFa33yFc_dkB`iOW6au@W&wYZfFD2_e@M^H`LGAd(=J2x6X+m};t_2U`p!)X<{l zd8inQ(xNCawP~wGRp~@q!|(Gsd!Hn{Y2SXI_x=9=zu)upJl0)%efHXWuf6x$YwvUJ zx$(}TBHNc1nOn2!+Abr!U;njA*vpkSE&Zw7+sngOdA{~$-}X1!mODRt-__2K-)Wc2 z#_Rog-O447SfiPO%@#X4A!1OJ%{I)#E zF)@Qml74!jU}UzlAKTt`t4@CPX34&DiP5%7co5km*V ztV#uCxgy{tfZM>CUka75o?!{GF@2$T3r(|p77#MNK^o@7n}x^1}_c% z4){ypPtnP;;Iozdg_6f8{iBenzZSd<_%!fx;0fR~s3$nbCm}j+L^Ost3?RpSbVO`y zOhV!?TPG-Rd?Mpx<2eOQ(JG*%IpF9_QU*Blhsb%ebwh2;KMc0och3{y`%ctYD1=etBT~+ZAot-L50hTm#rknZP-x1 zy6n=+D8Lb^W%%T(Y!5I(bJem}Hm2ulVRsqla+OPLfkCIs|Cu2{$fqHLBZdv|9vD5k zmXa%ha{@iUS$`pL+K~&K296vM6FC6w$0S6LiU!we$t7?RocgLhBi>O&vsE)DIw~a= z^_5l5_mN{0li=MyHomXz3~*LGRpFs^Xf^Ml|tF=Ei@ zxQJnq11?}_UV)tGFS~RKoU3vlcww8Zby9nb7=5lWIBon1;R|k;bORgU{{#R`!BZe|H0$4epw>5i+eC&`|RClr?XHv%87l=zi;@1l7>%@d=y( z+v`f9H#jSfi60sr6_a3FdyBlY8%2TdLI3$cxQd@9#qqC8mr(|N~ zrfSTiBO=+xdrf5BQSouZhsDGu+*bKl!8s>Ok#DXCa2h)j9BVXbK>VP1mT#%a^7lfd zEq=|VJQ|#~jgA;TCKh^H3sWwZ^cyl*(O?+>Y=vqV)tqHLDz=n^(@@n{-!9c6jRvQ$xz`VKlJH=g-{zCGovFs&J8lH`VF-+zwhwa*tj7!+wkEr zX3)CSPC8CpLjS=LkpqWrZ*Pua{iG%xZMGUH@RH)UI@oMAA@2t#J_pkCWlMYLVC%j=Yi9d&!Iξ5X)F8=V|vh>3qvP)%+d3j4F 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 { +): typeof EntityValidator.$ContentFormat => { return { [attachment.mimeType]: { content: attachment.url, @@ -86,7 +86,7 @@ export const attachmentToLysand = ( }; export const attachmentFromLysand = async ( - attachmentToConvert: Lysand.ContentFormat, + attachmentToConvert: typeof EntityValidator.$ContentFormat, ): Promise> => { const key = Object.keys(attachmentToConvert)[0]; const value = attachmentToConvert[key]; diff --git a/database/entities/Emoji.ts b/database/entities/Emoji.ts index 323a26ea..bf4d44f9 100644 --- a/database/entities/Emoji.ts +++ b/database/entities/Emoji.ts @@ -1,7 +1,7 @@ -import { emojiValidator, emojiValidatorWithColons } from "@api"; +import { emojiValidatorWithColons } from "@api"; +import type { EntityValidator } from "@lysand-org/federation"; import { proxyUrl } from "@response"; import { type InferSelectModel, and, eq } from "drizzle-orm"; -import type * as Lysand from "lysand-types"; import { db } from "~drizzle/db"; import { Emojis, Instances } from "~drizzle/schema"; import type { Emoji as APIEmoji } from "~types/mastodon/emoji"; @@ -41,7 +41,7 @@ export const parseEmojis = async (text: string) => { * @returns The emoji */ export const fetchEmoji = async ( - emojiToFetch: Lysand.Emoji, + emojiToFetch: (typeof EntityValidator.$CustomEmojiExtension)["emojis"][0], host?: string, ): Promise => { const existingEmoji = await db @@ -71,7 +71,6 @@ export const fetchEmoji = async ( shortcode: emojiToFetch.name, url: Object.entries(emojiToFetch.url)[0][1].content, alt: - emojiToFetch.alt || Object.entries(emojiToFetch.url)[0][1].description || undefined, contentType: Object.keys(emojiToFetch.url)[0], @@ -103,7 +102,9 @@ export const emojiToAPI = (emoji: EmojiWithInstance): APIEmoji => { }; }; -export const emojiToLysand = (emoji: EmojiWithInstance): Lysand.Emoji => { +export const emojiToLysand = ( + emoji: EmojiWithInstance, +): (typeof EntityValidator.$CustomEmojiExtension)["emojis"][0] => { return { name: emoji.shortcode, url: { @@ -112,6 +113,5 @@ export const emojiToLysand = (emoji: EmojiWithInstance): Lysand.Emoji => { description: emoji.alt || undefined, }, }, - alt: emoji.alt || undefined, }; }; diff --git a/database/entities/Federation.ts b/database/entities/Federation.ts index bb127d3f..5b0ab77f 100644 --- a/database/entities/Federation.ts +++ b/database/entities/Federation.ts @@ -1,11 +1,11 @@ +import type { EntityValidator } from "@lysand-org/federation"; import { config } from "config-manager"; -import type * as Lysand from "lysand-types"; import type { User } from "~packages/database-interface/user"; export const localObjectURI = (id: string) => `/objects/${id}`; export const objectToInboxRequest = async ( - object: Lysand.Entity, + object: typeof EntityValidator.$Entity, author: User, userToSendTo: User, ): Promise => { diff --git a/database/entities/Instance.ts b/database/entities/Instance.ts index 0d1c0902..7532a22e 100644 --- a/database/entities/Instance.ts +++ b/database/entities/Instance.ts @@ -1,4 +1,4 @@ -import type * as Lysand from "lysand-types"; +import type { EntityValidator } from "@lysand-org/federation"; import { db } from "~drizzle/db"; import { Instances } from "~drizzle/schema"; @@ -26,7 +26,7 @@ export const addInstanceIfNotExists = async (url: string) => { // Fetch the instance configuration const metadata = (await fetch(new URL("/.well-known/lysand", origin)).then( (res) => res.json(), - )) as Lysand.ServerMetadata; + )) as typeof EntityValidator.$ServerMetadata; if (metadata.type !== "ServerMetadata") { throw new Error("Invalid instance metadata (wrong type)"); diff --git a/database/entities/Like.ts b/database/entities/Like.ts index 31a300dd..d810bd55 100644 --- a/database/entities/Like.ts +++ b/database/entities/Like.ts @@ -1,6 +1,6 @@ +import type { EntityValidator } from "@lysand-org/federation"; import { config } from "config-manager"; import { type InferSelectModel, and, eq } from "drizzle-orm"; -import type * as Lysand from "lysand-types"; import { db } from "~drizzle/db"; import { Likes, Notifications } from "~drizzle/schema"; import type { Note } from "~packages/database-interface/note"; @@ -11,7 +11,7 @@ export type Like = InferSelectModel; /** * Represents a Like entity in the database. */ -export const likeToLysand = (like: Like): Lysand.Like => { +export const likeToLysand = (like: Like): typeof EntityValidator.$Like => { return { id: like.id, // biome-ignore lint/suspicious/noExplicitAny: to be rewritten diff --git a/database/entities/Status.ts b/database/entities/Status.ts index a7727eee..43d142a5 100644 --- a/database/entities/Status.ts +++ b/database/entities/Status.ts @@ -1,6 +1,7 @@ import { mentionValidator } from "@api"; import markdownItTaskLists from "@hackmd/markdown-it-task-lists"; import { dualLogger } from "@loggers"; +import type { EntityValidator } from "@lysand-org/federation"; import { sanitizeHtml, sanitizeHtmlInline } from "@sanitization"; import { config } from "config-manager"; import { @@ -13,7 +14,6 @@ import { sql, } from "drizzle-orm"; import linkifyHtml from "linkify-html"; -import type * as Lysand from "lysand-types"; import { anyOf, charIn, @@ -253,7 +253,7 @@ export const findManyNotes = async ( export const resolveNote = async ( uri?: string, - providedNote?: Lysand.Note, + providedNote?: typeof EntityValidator.$Note, ): Promise => { if (!uri && !providedNote) { throw new Error("No URI or note provided"); @@ -265,7 +265,7 @@ export const resolveNote = async ( if (foundStatus) return foundStatus; - let note: Lysand.Note | null = providedNote ?? null; + let note = providedNote ?? null; if (uri) { if (!URL.canParse(uri)) { @@ -279,7 +279,7 @@ export const resolveNote = async ( }, }); - note = (await response.json()) as Lysand.Note; + note = (await response.json()) as typeof EntityValidator.$Note; } if (!note) { @@ -484,7 +484,7 @@ export const replaceTextMentions = async (text: string, mentions: User[]) => { }; export const contentToHtml = async ( - content: Lysand.ContentFormat, + content: typeof EntityValidator.$ContentFormat, mentions: User[] = [], inline = false, ): Promise => { diff --git a/database/entities/User.ts b/database/entities/User.ts index 467a1d18..e19de6fd 100644 --- a/database/entities/User.ts +++ b/database/entities/User.ts @@ -1,8 +1,7 @@ import { dualLogger } from "@loggers"; -import { addUserToMeilisearch } from "@meilisearch"; +import type { EntityValidator } from "@lysand-org/federation"; import { config } from "config-manager"; -import { type InferSelectModel, and, eq, inArray, sql } from "drizzle-orm"; -import type * as Lysand from "lysand-types"; +import { type InferSelectModel, and, eq, sql } from "drizzle-orm"; import { db } from "~drizzle/db"; import { Applications, @@ -462,7 +461,7 @@ export const getRelationshipToOtherUser = async ( export const followRequestToLysand = ( follower: User, followee: User, -): Lysand.Follow => { +): typeof EntityValidator.$Follow => { if (follower.isRemote()) { throw new Error("Follower must be a local user"); } @@ -490,7 +489,7 @@ export const followRequestToLysand = ( export const followAcceptToLysand = ( follower: User, followee: User, -): Lysand.FollowAccept => { +): typeof EntityValidator.$FollowAccept => { if (!follower.isRemote()) { throw new Error("Follower must be a remote user"); } @@ -518,7 +517,7 @@ export const followAcceptToLysand = ( export const followRejectToLysand = ( follower: User, followee: User, -): Lysand.FollowReject => { +): typeof EntityValidator.$FollowReject => { return { ...followAcceptToLysand(follower, followee), type: "FollowReject", diff --git a/drizzle/schema.ts b/drizzle/schema.ts index 1f7345f8..62133d07 100644 --- a/drizzle/schema.ts +++ b/drizzle/schema.ts @@ -1,3 +1,4 @@ +import type { EntityValidator } from "@lysand-org/federation"; import { relations, sql } from "drizzle-orm"; import { type AnyPgColumn, @@ -12,7 +13,6 @@ import { uniqueIndex, uuid, } from "drizzle-orm/pg-core"; -import type * as Lysand from "lysand-types"; import type { Source as APISource } from "~types/mastodon/source"; export const Emojis = pgTable("Emojis", { @@ -354,8 +354,8 @@ export const Users = pgTable( isAdmin: boolean("is_admin").default(false).notNull(), fields: jsonb("fields").notNull().default("[]").$type< { - key: Lysand.ContentFormat; - value: Lysand.ContentFormat; + key: typeof EntityValidator.$ContentFormat; + value: typeof EntityValidator.$ContentFormat; }[] >(), endpoints: jsonb("endpoints").$type emojiToLysand(emoji)), diff --git a/packages/database-interface/user.ts b/packages/database-interface/user.ts index 71ac7e8a..122a4358 100644 --- a/packages/database-interface/user.ts +++ b/packages/database-interface/user.ts @@ -1,5 +1,6 @@ import { idValidator } from "@api"; import { getBestContentType, urlToContentFormat } from "@content_types"; +import type { EntityValidator } from "@lysand-org/federation"; import { addUserToMeilisearch } from "@meilisearch"; import { proxyUrl } from "@response"; import { @@ -14,7 +15,6 @@ import { isNull, } from "drizzle-orm"; import { htmlToText } from "html-to-text"; -import type * as Lysand from "lysand-types"; import { emojiToAPI, emojiToLysand, @@ -206,7 +206,9 @@ export class User { }, }); - const data = (await response.json()) as Partial; + const data = (await response.json()) as Partial< + typeof EntityValidator.$User + >; if ( !( @@ -255,7 +257,11 @@ export class User { inbox: data.inbox, outbox: data.outbox, }, - fields: data.fields ?? [], + fields: + data.fields?.map((f) => ({ + key: f.name, + value: f.value, + })) ?? [], updatedAt: new Date(data.created_at).toISOString(), instanceId: instance.id, avatar: data.avatar @@ -467,7 +473,7 @@ export class User { }; } - toLysand(): Lysand.User { + toLysand(): typeof EntityValidator.$User { if (this.isRemote()) { throw new Error("Cannot convert remote user to Lysand format"); } @@ -520,7 +526,10 @@ export class User { avatar: urlToContentFormat(this.getAvatarUrl(config)) ?? undefined, header: urlToContentFormat(this.getHeaderUrl(config)) ?? undefined, display_name: user.displayName, - fields: user.fields, + fields: user.fields.map((f) => ({ + name: f.key, + value: f.value, + })), public_key: { actor: new URL( `/users/${user.id}`, diff --git a/server/api/objects/[uuid]/index.ts b/server/api/objects/[uuid]/index.ts index 9d7525b5..5ded42b1 100644 --- a/server/api/objects/[uuid]/index.ts +++ b/server/api/objects/[uuid]/index.ts @@ -1,9 +1,9 @@ import { applyConfig, handleZodError } from "@api"; import { zValidator } from "@hono/zod-validator"; +import type { EntityValidator } from "@lysand-org/federation"; import { errorResponse, jsonResponse } from "@response"; import { and, eq, inArray, sql } from "drizzle-orm"; import type { Hono } from "hono"; -import type * as Lysand from "lysand-types"; import { z } from "zod"; import { type Like, likeToLysand } from "~database/entities/Like"; import { db } from "~drizzle/db"; @@ -37,7 +37,7 @@ export default (app: Hono) => const { uuid } = context.req.valid("param"); let foundObject: Note | Like | null = null; - let apiObject: Lysand.Entity | null = null; + let apiObject: typeof EntityValidator.$Entity | null = null; foundObject = await Note.fromSql( and( diff --git a/server/api/users/:uuid/inbox/index.ts b/server/api/users/:uuid/inbox/index.ts index ad6800f4..373ecb50 100644 --- a/server/api/users/:uuid/inbox/index.ts +++ b/server/api/users/:uuid/inbox/index.ts @@ -1,10 +1,10 @@ import { applyConfig, handleZodError } from "@api"; import { zValidator } from "@hono/zod-validator"; import { dualLogger } from "@loggers"; +import { EntityValidator, SignatureValidator } from "@lysand-org/federation"; import { errorResponse, jsonResponse, response } from "@response"; import { eq } from "drizzle-orm"; import type { Hono } from "hono"; -import type * as Lysand from "lysand-types"; import { z } from "zod"; import { isValidationError } from "zod-validation-error"; import { resolveNote } from "~database/entities/Status"; @@ -16,7 +16,6 @@ import { db } from "~drizzle/db"; import { Notifications, Relationships } from "~drizzle/schema"; import { User } from "~packages/database-interface/user"; import { LogLevel } from "~packages/log-manager"; -import { EntityValidator, SignatureValidator } from "~packages/lysand-utils"; export const meta = applyConfig({ allowedMethods: ["POST"], @@ -82,29 +81,33 @@ export default (app: Hono) => const validator = await SignatureValidator.fromStringKey( sender.getUser().publicKey, - signature, - date, - context.req.method, - new URL(context.req.url), - await context.req.text(), ); - const isValid = await validator.validate(); + const isValid = await validator + .validate(context.req.raw) + .catch((e) => { + dualLogger.logError( + LogLevel.ERROR, + "Inbox.Signature", + e as Error, + ); + return false; + }); if (!isValid) { return errorResponse("Invalid signature", 400); } } - const validator = new EntityValidator( - (await context.req.json()) as Lysand.Entity, - ); + const validator = new EntityValidator(); + const body: typeof EntityValidator.$Entity = + await context.req.json(); try { // Add sent data to database - switch (validator.getType()) { + switch (body.type) { case "Note": { - const note = await validator.validate(); + const note = await validator.Note(body); const account = await User.resolve(note.author); @@ -131,8 +134,7 @@ export default (app: Hono) => return response("Note created", 201); } case "Follow": { - const follow = - await validator.validate(); + const follow = await validator.Follow(body); const account = await User.resolve(follow.author); @@ -175,8 +177,7 @@ export default (app: Hono) => return response("Follow request sent", 200); } case "FollowAccept": { - const followAccept = - await validator.validate(); + const followAccept = await validator.FollowAccept(body); console.log(followAccept); @@ -211,8 +212,7 @@ export default (app: Hono) => return response("Follow request accepted", 200); } case "FollowReject": { - const followReject = - await validator.validate(); + const followReject = await validator.FollowReject(body); const account = await User.resolve(followReject.author); diff --git a/server/api/well-known/lysand.ts b/server/api/well-known/lysand.ts index 230f6b03..f726eeef 100644 --- a/server/api/well-known/lysand.ts +++ b/server/api/well-known/lysand.ts @@ -1,8 +1,8 @@ import { applyConfig } from "@api"; import { urlToContentFormat } from "@content_types"; +import type { EntityValidator } from "@lysand-org/federation"; import { jsonResponse } from "@response"; import type { Hono } from "hono"; -import type * as Lysand from "lysand-types"; import pkg from "~package.json"; import { config } from "~packages/config-manager"; @@ -29,5 +29,5 @@ export default (app: Hono) => banner: urlToContentFormat(config.instance.banner) ?? undefined, supported_extensions: ["org.lysand:custom_emojis"], website: "https://lysand.org", - } satisfies Lysand.ServerMetadata); + } satisfies typeof EntityValidator.$ServerMetadata); }); diff --git a/utils/content_types.ts b/utils/content_types.ts index 95a39ac6..41bbf0c2 100644 --- a/utils/content_types.ts +++ b/utils/content_types.ts @@ -1,7 +1,9 @@ -import type * as Lysand from "lysand-types"; +import type { EntityValidator } from "@lysand-org/federation"; import { lookup } from "mime-types"; -export const getBestContentType = (content?: Lysand.ContentFormat) => { +export const getBestContentType = ( + content?: typeof EntityValidator.$ContentFormat, +) => { if (!content) return { content: "", format: "text/plain" }; const bestFormatsRanked = [ @@ -21,7 +23,7 @@ export const getBestContentType = (content?: Lysand.ContentFormat) => { export const urlToContentFormat = ( url: string, -): Lysand.ContentFormat | null => { +): typeof EntityValidator.$ContentFormat | null => { if (!url) return null; if (url.startsWith("https://api.dicebear.com/")) { return {