refactor(federation): ⬆️ Use @lysand-org/federation v2.0.0

This commit is contained in:
Jesse Wierzbinski 2024-06-19 13:21:02 -10:00
parent 47ce60494a
commit 70cd00cfa8
No known key found for this signature in database
17 changed files with 56 additions and 60 deletions

BIN
bun.lockb

Binary file not shown.

View file

@ -1,8 +1,6 @@
import { debugRequest } from "@/api"; import { debugRequest } from "@/api";
import { import { SignatureConstructor } from "@lysand-org/federation";
type EntityValidator, import type { Entity, Undo } from "@lysand-org/federation/types";
SignatureConstructor,
} from "@lysand-org/federation";
import { config } from "config-manager"; import { config } from "config-manager";
import type { User } from "~/packages/database-interface/user"; import type { User } from "~/packages/database-interface/user";
import { LogLevel, LogManager } from "~/packages/log-manager"; import { LogLevel, LogManager } from "~/packages/log-manager";
@ -11,7 +9,7 @@ export const localObjectUri = (id: string) =>
new URL(`/objects/${id}`, config.http.base_url).toString(); new URL(`/objects/${id}`, config.http.base_url).toString();
export const objectToInboxRequest = async ( export const objectToInboxRequest = async (
object: typeof EntityValidator.$Entity, object: Entity,
author: User, author: User,
userToSendTo: User, userToSendTo: User,
): Promise<Request> => { ): Promise<Request> => {
@ -68,10 +66,7 @@ export const objectToInboxRequest = async (
return signed; return signed;
}; };
export const undoFederationRequest = ( export const undoFederationRequest = (undoer: User, uri: string): Undo => {
undoer: User,
uri: string,
): typeof EntityValidator.$Undo => {
const id = crypto.randomUUID(); const id = crypto.randomUUID();
return { return {
type: "Undo", type: "Undo",

View file

@ -1,4 +1,4 @@
import type { EntityValidator } from "@lysand-org/federation"; import type { ServerMetadata } from "@lysand-org/federation/types";
import { db } from "~/drizzle/db"; import { db } from "~/drizzle/db";
import { Instances } from "~/drizzle/schema"; import { Instances } from "~/drizzle/schema";
@ -26,7 +26,7 @@ export const addInstanceIfNotExists = async (url: string) => {
// Fetch the instance configuration // Fetch the instance configuration
const metadata = (await fetch(new URL("/.well-known/lysand", origin)).then( const metadata = (await fetch(new URL("/.well-known/lysand", origin)).then(
(res) => res.json(), (res) => res.json(),
)) as typeof EntityValidator.$ServerMetadata; )) as ServerMetadata;
if (metadata.type !== "ServerMetadata") { if (metadata.type !== "ServerMetadata") {
throw new Error("Invalid instance metadata (wrong type)"); throw new Error("Invalid instance metadata (wrong type)");

View file

@ -1,4 +1,4 @@
import type { EntityValidator } from "@lysand-org/federation"; import type { Like } from "@lysand-org/federation/types";
import { config } from "config-manager"; import { config } from "config-manager";
import { type InferSelectModel, and, eq } from "drizzle-orm"; import { type InferSelectModel, and, eq } from "drizzle-orm";
import { db } from "~/drizzle/db"; import { db } from "~/drizzle/db";
@ -6,12 +6,12 @@ import { Likes, Notifications } from "~/drizzle/schema";
import type { Note } from "~/packages/database-interface/note"; import type { Note } from "~/packages/database-interface/note";
import type { User } from "~/packages/database-interface/user"; import type { User } from "~/packages/database-interface/user";
export type Like = InferSelectModel<typeof Likes>; export type LikeType = InferSelectModel<typeof Likes>;
/** /**
* Represents a Like entity in the database. * Represents a Like entity in the database.
*/ */
export const likeToLysand = (like: Like): typeof EntityValidator.$Like => { export const likeToLysand = (like: LikeType): Like => {
return { return {
id: like.id, id: like.id,
// biome-ignore lint/suspicious/noExplicitAny: to be rewritten // biome-ignore lint/suspicious/noExplicitAny: to be rewritten

View file

@ -2,7 +2,7 @@ import { mentionValidator } from "@/api";
import { dualLogger } from "@/loggers"; import { dualLogger } from "@/loggers";
import { sanitizeHtml, sanitizeHtmlInline } from "@/sanitization"; import { sanitizeHtml, sanitizeHtmlInline } from "@/sanitization";
import markdownItTaskLists from "@hackmd/markdown-it-task-lists"; import markdownItTaskLists from "@hackmd/markdown-it-task-lists";
import type { EntityValidator } from "@lysand-org/federation"; import type { ContentFormat } from "@lysand-org/federation/types";
import { config } from "config-manager"; import { config } from "config-manager";
import { import {
type InferSelectModel, type InferSelectModel,
@ -373,7 +373,7 @@ export const replaceTextMentions = (text: string, mentions: User[]) => {
}; };
export const contentToHtml = async ( export const contentToHtml = async (
content: typeof EntityValidator.$ContentFormat, content: ContentFormat,
mentions: User[] = [], mentions: User[] = [],
inline = false, inline = false,
): Promise<string> => { ): Promise<string> => {

View file

@ -1,5 +1,9 @@
import { dualLogger } from "@/loggers"; import { dualLogger } from "@/loggers";
import type { EntityValidator } from "@lysand-org/federation"; import type {
Follow,
FollowAccept,
FollowReject,
} from "@lysand-org/federation/types";
import { config } from "config-manager"; import { config } from "config-manager";
import { type InferSelectModel, and, eq, sql } from "drizzle-orm"; import { type InferSelectModel, and, eq, sql } from "drizzle-orm";
import { db } from "~/drizzle/db"; import { db } from "~/drizzle/db";
@ -505,7 +509,7 @@ export const getRelationshipToOtherUser = async (
export const followRequestToLysand = ( export const followRequestToLysand = (
follower: User, follower: User,
followee: User, followee: User,
): typeof EntityValidator.$Follow => { ): Follow => {
if (follower.isRemote()) { if (follower.isRemote()) {
throw new Error("Follower must be a local user"); throw new Error("Follower must be a local user");
} }
@ -533,7 +537,7 @@ export const followRequestToLysand = (
export const followAcceptToLysand = ( export const followAcceptToLysand = (
follower: User, follower: User,
followee: User, followee: User,
): typeof EntityValidator.$FollowAccept => { ): FollowAccept => {
if (!follower.isRemote()) { if (!follower.isRemote()) {
throw new Error("Follower must be a remote user"); throw new Error("Follower must be a remote user");
} }
@ -561,7 +565,7 @@ export const followAcceptToLysand = (
export const followRejectToLysand = ( export const followRejectToLysand = (
follower: User, follower: User,
followee: User, followee: User,
): typeof EntityValidator.$FollowReject => { ): FollowReject => {
return { return {
...followAcceptToLysand(follower, followee), ...followAcceptToLysand(follower, followee),
type: "FollowReject", type: "FollowReject",

View file

@ -1,4 +1,4 @@
import type { EntityValidator } from "@lysand-org/federation"; import type { ContentFormat } from "@lysand-org/federation/types";
import type { Challenge } from "altcha-lib/types"; import type { Challenge } from "altcha-lib/types";
import { relations, sql } from "drizzle-orm"; import { relations, sql } from "drizzle-orm";
import { import {
@ -375,8 +375,8 @@ export const Users = pgTable(
passwordResetToken: text("password_reset_token"), passwordResetToken: text("password_reset_token"),
fields: jsonb("fields").notNull().default("[]").$type< fields: jsonb("fields").notNull().default("[]").$type<
{ {
key: typeof EntityValidator.$ContentFormat; key: ContentFormat;
value: typeof EntityValidator.$ContentFormat; value: ContentFormat;
}[] }[]
>(), >(),
endpoints: jsonb("endpoints").$type<Partial<{ endpoints: jsonb("endpoints").$type<Partial<{

View file

@ -100,7 +100,7 @@
"@inquirer/confirm": "^3.1.10", "@inquirer/confirm": "^3.1.10",
"@inquirer/input": "^2.1.10", "@inquirer/input": "^2.1.10",
"@json2csv/plainjs": "^7.0.6", "@json2csv/plainjs": "^7.0.6",
"@lysand-org/federation": "^1.2.0", "@lysand-org/federation": "^2.0.0",
"@oclif/core": "^4.0.6", "@oclif/core": "^4.0.6",
"@tufjs/canonical-json": "^2.0.0", "@tufjs/canonical-json": "^2.0.0",
"altcha-lib": "^0.3.0", "altcha-lib": "^0.3.0",

View file

@ -1,5 +1,5 @@
import { proxyUrl } from "@/response"; import { proxyUrl } from "@/response";
import type { EntityValidator } from "@lysand-org/federation"; import type { ContentFormat } from "@lysand-org/federation/types";
import { import {
type InferInsertModel, type InferInsertModel,
type InferSelectModel, type InferSelectModel,
@ -183,7 +183,7 @@ export class Attachment extends BaseInterface<typeof Attachments> {
}; };
} }
public toLysand(): typeof EntityValidator.$ContentFormat { public toLysand(): ContentFormat {
return { return {
[this.data.mimeType]: { [this.data.mimeType]: {
content: this.data.url, content: this.data.url,
@ -204,7 +204,7 @@ export class Attachment extends BaseInterface<typeof Attachments> {
} }
public static fromLysand( public static fromLysand(
attachmentToConvert: typeof EntityValidator.$ContentFormat, attachmentToConvert: ContentFormat,
): Promise<Attachment> { ): Promise<Attachment> {
const key = Object.keys(attachmentToConvert)[0]; const key = Object.keys(attachmentToConvert)[0];
const value = attachmentToConvert[key]; const value = attachmentToConvert[key];

View file

@ -1,5 +1,5 @@
import { proxyUrl } from "@/response"; import { proxyUrl } from "@/response";
import type { EntityValidator } from "@lysand-org/federation"; import type { CustomEmojiExtension } from "@lysand-org/federation/types";
import { import {
type InferInsertModel, type InferInsertModel,
type SQL, type SQL,
@ -118,7 +118,7 @@ export class Emoji extends BaseInterface<typeof Emojis, EmojiWithInstance> {
} }
public static async fetchFromRemote( public static async fetchFromRemote(
emojiToFetch: (typeof EntityValidator.$CustomEmojiExtension)["emojis"][0], emojiToFetch: CustomEmojiExtension["emojis"][0],
host?: string, host?: string,
): Promise<Emoji> { ): Promise<Emoji> {
const existingEmoji = await db const existingEmoji = await db
@ -164,7 +164,7 @@ export class Emoji extends BaseInterface<typeof Emojis, EmojiWithInstance> {
}; };
} }
public toLysand(): (typeof EntityValidator.$CustomEmojiExtension)["emojis"][0] { public toLysand(): CustomEmojiExtension["emojis"][0] {
return { return {
name: this.data.shortcode, name: this.data.shortcode,
url: { url: {
@ -177,7 +177,7 @@ export class Emoji extends BaseInterface<typeof Emojis, EmojiWithInstance> {
} }
public static fromLysand( public static fromLysand(
emoji: (typeof EntityValidator.$CustomEmojiExtension)["emojis"][0], emoji: CustomEmojiExtension["emojis"][0],
instanceId: string | null, instanceId: string | null,
): Promise<Emoji> { ): Promise<Emoji> {
return Emoji.insert({ return Emoji.insert({

View file

@ -3,6 +3,10 @@ import { dualLogger } from "@/loggers";
import { proxyUrl } from "@/response"; import { proxyUrl } from "@/response";
import { sanitizedHtmlStrip } from "@/sanitization"; import { sanitizedHtmlStrip } from "@/sanitization";
import { EntityValidator } from "@lysand-org/federation"; import { EntityValidator } from "@lysand-org/federation";
import type {
ContentFormat,
Note as LysandNote,
} from "@lysand-org/federation/types";
import { import {
type InferInsertModel, type InferInsertModel,
type SQL, type SQL,
@ -302,7 +306,7 @@ export class Note extends BaseInterface<typeof Notes, StatusWithRelations> {
*/ */
static async fromData(data: { static async fromData(data: {
author: User; author: User;
content: typeof EntityValidator.$ContentFormat; content: ContentFormat;
visibility: APIStatus["visibility"]; visibility: APIStatus["visibility"];
isSensitive: boolean; isSensitive: boolean;
spoilerText: string; spoilerText: string;
@ -392,7 +396,7 @@ export class Note extends BaseInterface<typeof Notes, StatusWithRelations> {
*/ */
async updateFromData(data: { async updateFromData(data: {
author?: User; author?: User;
content?: typeof EntityValidator.$ContentFormat; content?: ContentFormat;
visibility?: APIStatus["visibility"]; visibility?: APIStatus["visibility"];
isSensitive?: boolean; isSensitive?: boolean;
spoilerText?: string; spoilerText?: string;
@ -579,7 +583,7 @@ export class Note extends BaseInterface<typeof Notes, StatusWithRelations> {
* @returns The saved note, or null if the note could not be fetched * @returns The saved note, or null if the note could not be fetched
*/ */
static async saveFromRemote(uri: string): Promise<Note | null> { static async saveFromRemote(uri: string): Promise<Note | null> {
let note: typeof EntityValidator.$Note | null = null; let note: LysandNote | null = null;
if (uri) { if (uri) {
if (!URL.canParse(uri)) { if (!URL.canParse(uri)) {
@ -615,10 +619,7 @@ export class Note extends BaseInterface<typeof Notes, StatusWithRelations> {
* @param author Author of the note * @param author Author of the note
* @returns The saved note * @returns The saved note
*/ */
static async fromLysand( static async fromLysand(note: LysandNote, author: User): Promise<Note> {
note: typeof EntityValidator.$Note,
author: User,
): Promise<Note> {
const emojis: Emoji[] = []; const emojis: Emoji[] = [];
for (const emoji of note.extensions?.["org.lysand:custom_emojis"] for (const emoji of note.extensions?.["org.lysand:custom_emojis"]
@ -878,7 +879,7 @@ export class Note extends BaseInterface<typeof Notes, StatusWithRelations> {
* Convert a note to the Lysand format * Convert a note to the Lysand format
* @returns The note in the Lysand format * @returns The note in the Lysand format
*/ */
toLysand(): typeof EntityValidator.$Note { toLysand(): LysandNote {
const status = this.data; const status = this.data;
return { return {
type: "Note", type: "Note",

View file

@ -4,6 +4,7 @@ import { randomString } from "@/math";
import { addUserToMeilisearch } from "@/meilisearch"; import { addUserToMeilisearch } from "@/meilisearch";
import { proxyUrl } from "@/response"; import { proxyUrl } from "@/response";
import { EntityValidator } from "@lysand-org/federation"; import { EntityValidator } from "@lysand-org/federation";
import type { Entity, User as LysandUser } from "@lysand-org/federation/types";
import { import {
type InferInsertModel, type InferInsertModel,
type InferSelectModel, type InferSelectModel,
@ -254,9 +255,7 @@ export class User extends BaseInterface<typeof Users, UserWithRelations> {
}, },
}); });
const json = (await response.json()) as Partial< const json = (await response.json()) as Partial<LysandUser>;
typeof EntityValidator.$User
>;
const validator = new EntityValidator(); const validator = new EntityValidator();
@ -299,7 +298,7 @@ export class User extends BaseInterface<typeof Users, UserWithRelations> {
} }
static async fromLysand( static async fromLysand(
user: typeof EntityValidator.$User, user: LysandUser,
instance: InferSelectModel<typeof Instances>, instance: InferSelectModel<typeof Instances>,
): Promise<User> { ): Promise<User> {
const data = { const data = {
@ -535,7 +534,7 @@ export class User extends BaseInterface<typeof Users, UserWithRelations> {
return updated.data; return updated.data;
} }
async federateToFollowers(object: typeof EntityValidator.$Entity) { async federateToFollowers(object: Entity) {
// Get followers // Get followers
const followers = await User.manyFromSql( const followers = await User.manyFromSql(
and( and(
@ -625,7 +624,7 @@ export class User extends BaseInterface<typeof Users, UserWithRelations> {
}; };
} }
toLysand(): typeof EntityValidator.$User { toLysand(): LysandUser {
if (this.isRemote()) { if (this.isRemote()) {
throw new Error("Cannot convert remote user to Lysand format"); throw new Error("Cannot convert remote user to Lysand format");
} }

View file

@ -1,11 +1,11 @@
import { applyConfig, handleZodError } from "@/api"; import { applyConfig, handleZodError } from "@/api";
import { errorResponse, jsonResponse } from "@/response"; import { errorResponse, jsonResponse } from "@/response";
import { zValidator } from "@hono/zod-validator"; import { zValidator } from "@hono/zod-validator";
import type { EntityValidator } from "@lysand-org/federation"; import type { Entity } from "@lysand-org/federation/types";
import { and, eq, inArray, sql } from "drizzle-orm"; import { and, eq, inArray, sql } from "drizzle-orm";
import type { Hono } from "hono"; import type { Hono } from "hono";
import { z } from "zod"; import { z } from "zod";
import { type Like, likeToLysand } from "~/database/entities/like"; import { type LikeType, likeToLysand } from "~/database/entities/like";
import { db } from "~/drizzle/db"; import { db } from "~/drizzle/db";
import { Notes } from "~/drizzle/schema"; import { Notes } from "~/drizzle/schema";
import { Note } from "~/packages/database-interface/note"; import { Note } from "~/packages/database-interface/note";
@ -36,8 +36,8 @@ export default (app: Hono) =>
async (context) => { async (context) => {
const { id } = context.req.valid("param"); const { id } = context.req.valid("param");
let foundObject: Note | Like | null = null; let foundObject: Note | LikeType | null = null;
let apiObject: typeof EntityValidator.$Entity | null = null; let apiObject: Entity | null = null;
foundObject = await Note.fromSql( foundObject = await Note.fromSql(
and( and(

View file

@ -7,6 +7,7 @@ import {
RequestParserHandler, RequestParserHandler,
SignatureValidator, SignatureValidator,
} from "@lysand-org/federation"; } from "@lysand-org/federation";
import type { Entity } from "@lysand-org/federation/types";
import type { SocketAddress } from "bun"; import type { SocketAddress } from "bun";
import { and, eq } from "drizzle-orm"; import { and, eq } from "drizzle-orm";
import type { Hono } from "hono"; import type { Hono } from "hono";
@ -72,8 +73,7 @@ export default (app: Hono) =>
return response(null, 201); return response(null, 201);
} }
const body: typeof EntityValidator.$Entity = const body: Entity = await context.req.valid("json");
await context.req.valid("json");
if (config.debug.federation) { if (config.debug.federation) {
// Debug request // Debug request

View file

@ -1,7 +1,7 @@
import { applyConfig } from "@/api"; import { applyConfig } from "@/api";
import { urlToContentFormat } from "@/content_types"; import { urlToContentFormat } from "@/content_types";
import { jsonResponse } from "@/response"; import { jsonResponse } from "@/response";
import type { EntityValidator } from "@lysand-org/federation"; import type { ServerMetadata } from "@lysand-org/federation/types";
import type { Hono } from "hono"; import type { Hono } from "hono";
import pkg from "~/package.json"; import pkg from "~/package.json";
import { config } from "~/packages/config-manager"; import { config } from "~/packages/config-manager";
@ -29,5 +29,5 @@ export default (app: Hono) =>
banner: urlToContentFormat(config.instance.banner) ?? undefined, banner: urlToContentFormat(config.instance.banner) ?? undefined,
supported_extensions: ["org.lysand:custom_emojis"], supported_extensions: ["org.lysand:custom_emojis"],
website: "https://lysand.org", website: "https://lysand.org",
} satisfies typeof EntityValidator.$ServerMetadata); } satisfies ServerMetadata);
}); });

View file

@ -28,6 +28,7 @@
}, },
"noUnusedLocals": true "noUnusedLocals": true
}, },
"exclude": ["node_modules"],
"include": [ "include": [
"*.ts", "*.ts",
"*.d.ts", "*.d.ts",

View file

@ -1,9 +1,7 @@
import type { EntityValidator } from "@lysand-org/federation"; import type { ContentFormat } from "@lysand-org/federation/types";
import { lookup } from "mime-types"; import { lookup } from "mime-types";
export const getBestContentType = ( export const getBestContentType = (content?: ContentFormat) => {
content?: typeof EntityValidator.$ContentFormat,
) => {
if (!content) { if (!content) {
return { content: "", format: "text/plain" }; return { content: "", format: "text/plain" };
} }
@ -24,9 +22,7 @@ export const getBestContentType = (
return { content: "", format: "text/plain" }; return { content: "", format: "text/plain" };
}; };
export const urlToContentFormat = ( export const urlToContentFormat = (url?: string): ContentFormat | null => {
url?: string,
): typeof EntityValidator.$ContentFormat | null => {
if (!url) { if (!url) {
return null; return null;
} }