mirror of
https://github.com/versia-pub/server.git
synced 2025-12-06 16:38:19 +01:00
feat(federation): ➕ Replace old types and federation validators with @lysand-org/federation
This commit is contained in:
parent
25d087a54b
commit
5fd6a4e43d
|
|
@ -1,7 +1,7 @@
|
||||||
|
import type { EntityValidator } from "@lysand-org/federation";
|
||||||
import { proxyUrl } from "@response";
|
import { proxyUrl } from "@response";
|
||||||
import type { Config } from "config-manager";
|
import type { Config } from "config-manager";
|
||||||
import type { InferSelectModel } from "drizzle-orm";
|
import type { InferSelectModel } from "drizzle-orm";
|
||||||
import type * as Lysand from "lysand-types";
|
|
||||||
import { MediaBackendType } from "media-manager";
|
import { MediaBackendType } from "media-manager";
|
||||||
import { db } from "~drizzle/db";
|
import { db } from "~drizzle/db";
|
||||||
import { Attachments } from "~drizzle/schema";
|
import { Attachments } from "~drizzle/schema";
|
||||||
|
|
@ -65,7 +65,7 @@ export const attachmentToAPI = (
|
||||||
|
|
||||||
export const attachmentToLysand = (
|
export const attachmentToLysand = (
|
||||||
attachment: Attachment,
|
attachment: Attachment,
|
||||||
): Lysand.ContentFormat => {
|
): typeof EntityValidator.$ContentFormat => {
|
||||||
return {
|
return {
|
||||||
[attachment.mimeType]: {
|
[attachment.mimeType]: {
|
||||||
content: attachment.url,
|
content: attachment.url,
|
||||||
|
|
@ -86,7 +86,7 @@ export const attachmentToLysand = (
|
||||||
};
|
};
|
||||||
|
|
||||||
export const attachmentFromLysand = async (
|
export const attachmentFromLysand = async (
|
||||||
attachmentToConvert: Lysand.ContentFormat,
|
attachmentToConvert: typeof EntityValidator.$ContentFormat,
|
||||||
): Promise<InferSelectModel<typeof Attachments>> => {
|
): Promise<InferSelectModel<typeof Attachments>> => {
|
||||||
const key = Object.keys(attachmentToConvert)[0];
|
const key = Object.keys(attachmentToConvert)[0];
|
||||||
const value = attachmentToConvert[key];
|
const value = attachmentToConvert[key];
|
||||||
|
|
|
||||||
|
|
@ -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 { proxyUrl } from "@response";
|
||||||
import { type InferSelectModel, and, eq } from "drizzle-orm";
|
import { type InferSelectModel, and, eq } from "drizzle-orm";
|
||||||
import type * as Lysand from "lysand-types";
|
|
||||||
import { db } from "~drizzle/db";
|
import { db } from "~drizzle/db";
|
||||||
import { Emojis, Instances } from "~drizzle/schema";
|
import { Emojis, Instances } from "~drizzle/schema";
|
||||||
import type { Emoji as APIEmoji } from "~types/mastodon/emoji";
|
import type { Emoji as APIEmoji } from "~types/mastodon/emoji";
|
||||||
|
|
@ -41,7 +41,7 @@ export const parseEmojis = async (text: string) => {
|
||||||
* @returns The emoji
|
* @returns The emoji
|
||||||
*/
|
*/
|
||||||
export const fetchEmoji = async (
|
export const fetchEmoji = async (
|
||||||
emojiToFetch: Lysand.Emoji,
|
emojiToFetch: (typeof EntityValidator.$CustomEmojiExtension)["emojis"][0],
|
||||||
host?: string,
|
host?: string,
|
||||||
): Promise<EmojiWithInstance> => {
|
): Promise<EmojiWithInstance> => {
|
||||||
const existingEmoji = await db
|
const existingEmoji = await db
|
||||||
|
|
@ -71,7 +71,6 @@ export const fetchEmoji = async (
|
||||||
shortcode: emojiToFetch.name,
|
shortcode: emojiToFetch.name,
|
||||||
url: Object.entries(emojiToFetch.url)[0][1].content,
|
url: Object.entries(emojiToFetch.url)[0][1].content,
|
||||||
alt:
|
alt:
|
||||||
emojiToFetch.alt ||
|
|
||||||
Object.entries(emojiToFetch.url)[0][1].description ||
|
Object.entries(emojiToFetch.url)[0][1].description ||
|
||||||
undefined,
|
undefined,
|
||||||
contentType: Object.keys(emojiToFetch.url)[0],
|
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 {
|
return {
|
||||||
name: emoji.shortcode,
|
name: emoji.shortcode,
|
||||||
url: {
|
url: {
|
||||||
|
|
@ -112,6 +113,5 @@ export const emojiToLysand = (emoji: EmojiWithInstance): Lysand.Emoji => {
|
||||||
description: emoji.alt || undefined,
|
description: emoji.alt || undefined,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
alt: emoji.alt || undefined,
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
|
import type { EntityValidator } from "@lysand-org/federation";
|
||||||
import { config } from "config-manager";
|
import { config } from "config-manager";
|
||||||
import type * as Lysand from "lysand-types";
|
|
||||||
import type { User } from "~packages/database-interface/user";
|
import type { User } from "~packages/database-interface/user";
|
||||||
|
|
||||||
export const localObjectURI = (id: string) => `/objects/${id}`;
|
export const localObjectURI = (id: string) => `/objects/${id}`;
|
||||||
|
|
||||||
export const objectToInboxRequest = async (
|
export const objectToInboxRequest = async (
|
||||||
object: Lysand.Entity,
|
object: typeof EntityValidator.$Entity,
|
||||||
author: User,
|
author: User,
|
||||||
userToSendTo: User,
|
userToSendTo: User,
|
||||||
): Promise<Request> => {
|
): Promise<Request> => {
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import type * as Lysand from "lysand-types";
|
import type { EntityValidator } from "@lysand-org/federation";
|
||||||
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 Lysand.ServerMetadata;
|
)) as typeof EntityValidator.$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)");
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
|
import type { EntityValidator } from "@lysand-org/federation";
|
||||||
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 type * as Lysand from "lysand-types";
|
|
||||||
import { db } from "~drizzle/db";
|
import { db } from "~drizzle/db";
|
||||||
import { Likes, Notifications } from "~drizzle/schema";
|
import { Likes, Notifications } from "~drizzle/schema";
|
||||||
import type { Note } from "~packages/database-interface/note";
|
import type { Note } from "~packages/database-interface/note";
|
||||||
|
|
@ -11,7 +11,7 @@ export type Like = InferSelectModel<typeof Likes>;
|
||||||
/**
|
/**
|
||||||
* Represents a Like entity in the database.
|
* Represents a Like entity in the database.
|
||||||
*/
|
*/
|
||||||
export const likeToLysand = (like: Like): Lysand.Like => {
|
export const likeToLysand = (like: Like): typeof EntityValidator.$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
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import { mentionValidator } from "@api";
|
import { mentionValidator } from "@api";
|
||||||
import markdownItTaskLists from "@hackmd/markdown-it-task-lists";
|
import markdownItTaskLists from "@hackmd/markdown-it-task-lists";
|
||||||
import { dualLogger } from "@loggers";
|
import { dualLogger } from "@loggers";
|
||||||
|
import type { EntityValidator } from "@lysand-org/federation";
|
||||||
import { sanitizeHtml, sanitizeHtmlInline } from "@sanitization";
|
import { sanitizeHtml, sanitizeHtmlInline } from "@sanitization";
|
||||||
import { config } from "config-manager";
|
import { config } from "config-manager";
|
||||||
import {
|
import {
|
||||||
|
|
@ -13,7 +14,6 @@ import {
|
||||||
sql,
|
sql,
|
||||||
} from "drizzle-orm";
|
} from "drizzle-orm";
|
||||||
import linkifyHtml from "linkify-html";
|
import linkifyHtml from "linkify-html";
|
||||||
import type * as Lysand from "lysand-types";
|
|
||||||
import {
|
import {
|
||||||
anyOf,
|
anyOf,
|
||||||
charIn,
|
charIn,
|
||||||
|
|
@ -253,7 +253,7 @@ export const findManyNotes = async (
|
||||||
|
|
||||||
export const resolveNote = async (
|
export const resolveNote = async (
|
||||||
uri?: string,
|
uri?: string,
|
||||||
providedNote?: Lysand.Note,
|
providedNote?: typeof EntityValidator.$Note,
|
||||||
): Promise<Note> => {
|
): Promise<Note> => {
|
||||||
if (!uri && !providedNote) {
|
if (!uri && !providedNote) {
|
||||||
throw new Error("No URI or note provided");
|
throw new Error("No URI or note provided");
|
||||||
|
|
@ -265,7 +265,7 @@ export const resolveNote = async (
|
||||||
|
|
||||||
if (foundStatus) return foundStatus;
|
if (foundStatus) return foundStatus;
|
||||||
|
|
||||||
let note: Lysand.Note | null = providedNote ?? null;
|
let note = providedNote ?? null;
|
||||||
|
|
||||||
if (uri) {
|
if (uri) {
|
||||||
if (!URL.canParse(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) {
|
if (!note) {
|
||||||
|
|
@ -484,7 +484,7 @@ export const replaceTextMentions = async (text: string, mentions: User[]) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
export const contentToHtml = async (
|
export const contentToHtml = async (
|
||||||
content: Lysand.ContentFormat,
|
content: typeof EntityValidator.$ContentFormat,
|
||||||
mentions: User[] = [],
|
mentions: User[] = [],
|
||||||
inline = false,
|
inline = false,
|
||||||
): Promise<string> => {
|
): Promise<string> => {
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,7 @@
|
||||||
import { dualLogger } from "@loggers";
|
import { dualLogger } from "@loggers";
|
||||||
import { addUserToMeilisearch } from "@meilisearch";
|
import type { EntityValidator } from "@lysand-org/federation";
|
||||||
import { config } from "config-manager";
|
import { config } from "config-manager";
|
||||||
import { type InferSelectModel, and, eq, inArray, sql } from "drizzle-orm";
|
import { type InferSelectModel, and, eq, sql } from "drizzle-orm";
|
||||||
import type * as Lysand from "lysand-types";
|
|
||||||
import { db } from "~drizzle/db";
|
import { db } from "~drizzle/db";
|
||||||
import {
|
import {
|
||||||
Applications,
|
Applications,
|
||||||
|
|
@ -462,7 +461,7 @@ export const getRelationshipToOtherUser = async (
|
||||||
export const followRequestToLysand = (
|
export const followRequestToLysand = (
|
||||||
follower: User,
|
follower: User,
|
||||||
followee: User,
|
followee: User,
|
||||||
): Lysand.Follow => {
|
): typeof EntityValidator.$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");
|
||||||
}
|
}
|
||||||
|
|
@ -490,7 +489,7 @@ export const followRequestToLysand = (
|
||||||
export const followAcceptToLysand = (
|
export const followAcceptToLysand = (
|
||||||
follower: User,
|
follower: User,
|
||||||
followee: User,
|
followee: User,
|
||||||
): Lysand.FollowAccept => {
|
): typeof EntityValidator.$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");
|
||||||
}
|
}
|
||||||
|
|
@ -518,7 +517,7 @@ export const followAcceptToLysand = (
|
||||||
export const followRejectToLysand = (
|
export const followRejectToLysand = (
|
||||||
follower: User,
|
follower: User,
|
||||||
followee: User,
|
followee: User,
|
||||||
): Lysand.FollowReject => {
|
): typeof EntityValidator.$FollowReject => {
|
||||||
return {
|
return {
|
||||||
...followAcceptToLysand(follower, followee),
|
...followAcceptToLysand(follower, followee),
|
||||||
type: "FollowReject",
|
type: "FollowReject",
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
import type { EntityValidator } from "@lysand-org/federation";
|
||||||
import { relations, sql } from "drizzle-orm";
|
import { relations, sql } from "drizzle-orm";
|
||||||
import {
|
import {
|
||||||
type AnyPgColumn,
|
type AnyPgColumn,
|
||||||
|
|
@ -12,7 +13,6 @@ import {
|
||||||
uniqueIndex,
|
uniqueIndex,
|
||||||
uuid,
|
uuid,
|
||||||
} from "drizzle-orm/pg-core";
|
} from "drizzle-orm/pg-core";
|
||||||
import type * as Lysand from "lysand-types";
|
|
||||||
import type { Source as APISource } from "~types/mastodon/source";
|
import type { Source as APISource } from "~types/mastodon/source";
|
||||||
|
|
||||||
export const Emojis = pgTable("Emojis", {
|
export const Emojis = pgTable("Emojis", {
|
||||||
|
|
@ -354,8 +354,8 @@ export const Users = pgTable(
|
||||||
isAdmin: boolean("is_admin").default(false).notNull(),
|
isAdmin: boolean("is_admin").default(false).notNull(),
|
||||||
fields: jsonb("fields").notNull().default("[]").$type<
|
fields: jsonb("fields").notNull().default("[]").$type<
|
||||||
{
|
{
|
||||||
key: Lysand.ContentFormat;
|
key: typeof EntityValidator.$ContentFormat;
|
||||||
value: Lysand.ContentFormat;
|
value: typeof EntityValidator.$ContentFormat;
|
||||||
}[]
|
}[]
|
||||||
>(),
|
>(),
|
||||||
endpoints: jsonb("endpoints").$type<Partial<{
|
endpoints: jsonb("endpoints").$type<Partial<{
|
||||||
|
|
|
||||||
|
|
@ -98,6 +98,7 @@
|
||||||
"@inquirer/confirm": "^3.1.6",
|
"@inquirer/confirm": "^3.1.6",
|
||||||
"@inquirer/input": "^2.1.6",
|
"@inquirer/input": "^2.1.6",
|
||||||
"@json2csv/plainjs": "^7.0.6",
|
"@json2csv/plainjs": "^7.0.6",
|
||||||
|
"@lysand-org/federation": "^1.1.3",
|
||||||
"@oclif/core": "^3.26.6",
|
"@oclif/core": "^3.26.6",
|
||||||
"@tufjs/canonical-json": "^2.0.0",
|
"@tufjs/canonical-json": "^2.0.0",
|
||||||
"blurhash": "^2.0.5",
|
"blurhash": "^2.0.5",
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
import type { EntityValidator } from "@lysand-org/federation";
|
||||||
import { proxyUrl } from "@response";
|
import { proxyUrl } from "@response";
|
||||||
import { sanitizedHtmlStrip } from "@sanitization";
|
import { sanitizedHtmlStrip } from "@sanitization";
|
||||||
import {
|
import {
|
||||||
|
|
@ -12,7 +13,6 @@ import {
|
||||||
sql,
|
sql,
|
||||||
} from "drizzle-orm";
|
} from "drizzle-orm";
|
||||||
import { htmlToText } from "html-to-text";
|
import { htmlToText } from "html-to-text";
|
||||||
import type * as Lysand from "lysand-types";
|
|
||||||
import { createRegExp, exactly, global } from "magic-regexp";
|
import { createRegExp, exactly, global } from "magic-regexp";
|
||||||
import {
|
import {
|
||||||
type Application,
|
type Application,
|
||||||
|
|
@ -210,7 +210,7 @@ export class Note {
|
||||||
|
|
||||||
static async fromData(
|
static async fromData(
|
||||||
author: User,
|
author: User,
|
||||||
content: Lysand.ContentFormat,
|
content: typeof EntityValidator.$ContentFormat,
|
||||||
visibility: APIStatus["visibility"],
|
visibility: APIStatus["visibility"],
|
||||||
is_sensitive: boolean,
|
is_sensitive: boolean,
|
||||||
spoiler_text: string,
|
spoiler_text: string,
|
||||||
|
|
@ -303,7 +303,7 @@ export class Note {
|
||||||
}
|
}
|
||||||
|
|
||||||
async updateFromData(
|
async updateFromData(
|
||||||
content?: Lysand.ContentFormat,
|
content?: typeof EntityValidator.$ContentFormat,
|
||||||
visibility?: APIStatus["visibility"],
|
visibility?: APIStatus["visibility"],
|
||||||
is_sensitive?: boolean,
|
is_sensitive?: boolean,
|
||||||
spoiler_text?: string,
|
spoiler_text?: string,
|
||||||
|
|
@ -539,7 +539,7 @@ export class Note {
|
||||||
return `/@${this.getAuthor().getUser().username}/${this.id}`;
|
return `/@${this.getAuthor().getUser().username}/${this.id}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
toLysand(): Lysand.Note {
|
toLysand(): typeof EntityValidator.$Note {
|
||||||
const status = this.getStatus();
|
const status = this.getStatus();
|
||||||
return {
|
return {
|
||||||
type: "Note",
|
type: "Note",
|
||||||
|
|
@ -563,7 +563,11 @@ export class Note {
|
||||||
quotes: Note.getURI(status.quotingId) ?? undefined,
|
quotes: Note.getURI(status.quotingId) ?? undefined,
|
||||||
replies_to: Note.getURI(status.replyId) ?? undefined,
|
replies_to: Note.getURI(status.replyId) ?? undefined,
|
||||||
subject: status.spoilerText,
|
subject: status.spoilerText,
|
||||||
visibility: status.visibility as Lysand.Visibility,
|
visibility: status.visibility as
|
||||||
|
| "public"
|
||||||
|
| "unlisted"
|
||||||
|
| "private"
|
||||||
|
| "direct",
|
||||||
extensions: {
|
extensions: {
|
||||||
"org.lysand:custom_emojis": {
|
"org.lysand:custom_emojis": {
|
||||||
emojis: status.emojis.map((emoji) => emojiToLysand(emoji)),
|
emojis: status.emojis.map((emoji) => emojiToLysand(emoji)),
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import { idValidator } from "@api";
|
import { idValidator } from "@api";
|
||||||
import { getBestContentType, urlToContentFormat } from "@content_types";
|
import { getBestContentType, urlToContentFormat } from "@content_types";
|
||||||
|
import type { EntityValidator } from "@lysand-org/federation";
|
||||||
import { addUserToMeilisearch } from "@meilisearch";
|
import { addUserToMeilisearch } from "@meilisearch";
|
||||||
import { proxyUrl } from "@response";
|
import { proxyUrl } from "@response";
|
||||||
import {
|
import {
|
||||||
|
|
@ -14,7 +15,6 @@ import {
|
||||||
isNull,
|
isNull,
|
||||||
} from "drizzle-orm";
|
} from "drizzle-orm";
|
||||||
import { htmlToText } from "html-to-text";
|
import { htmlToText } from "html-to-text";
|
||||||
import type * as Lysand from "lysand-types";
|
|
||||||
import {
|
import {
|
||||||
emojiToAPI,
|
emojiToAPI,
|
||||||
emojiToLysand,
|
emojiToLysand,
|
||||||
|
|
@ -206,7 +206,9 @@ export class User {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const data = (await response.json()) as Partial<Lysand.User>;
|
const data = (await response.json()) as Partial<
|
||||||
|
typeof EntityValidator.$User
|
||||||
|
>;
|
||||||
|
|
||||||
if (
|
if (
|
||||||
!(
|
!(
|
||||||
|
|
@ -255,7 +257,11 @@ export class User {
|
||||||
inbox: data.inbox,
|
inbox: data.inbox,
|
||||||
outbox: data.outbox,
|
outbox: data.outbox,
|
||||||
},
|
},
|
||||||
fields: data.fields ?? [],
|
fields:
|
||||||
|
data.fields?.map((f) => ({
|
||||||
|
key: f.name,
|
||||||
|
value: f.value,
|
||||||
|
})) ?? [],
|
||||||
updatedAt: new Date(data.created_at).toISOString(),
|
updatedAt: new Date(data.created_at).toISOString(),
|
||||||
instanceId: instance.id,
|
instanceId: instance.id,
|
||||||
avatar: data.avatar
|
avatar: data.avatar
|
||||||
|
|
@ -467,7 +473,7 @@ export class User {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
toLysand(): Lysand.User {
|
toLysand(): typeof EntityValidator.$User {
|
||||||
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");
|
||||||
}
|
}
|
||||||
|
|
@ -520,7 +526,10 @@ export class User {
|
||||||
avatar: urlToContentFormat(this.getAvatarUrl(config)) ?? undefined,
|
avatar: urlToContentFormat(this.getAvatarUrl(config)) ?? undefined,
|
||||||
header: urlToContentFormat(this.getHeaderUrl(config)) ?? undefined,
|
header: urlToContentFormat(this.getHeaderUrl(config)) ?? undefined,
|
||||||
display_name: user.displayName,
|
display_name: user.displayName,
|
||||||
fields: user.fields,
|
fields: user.fields.map((f) => ({
|
||||||
|
name: f.key,
|
||||||
|
value: f.value,
|
||||||
|
})),
|
||||||
public_key: {
|
public_key: {
|
||||||
actor: new URL(
|
actor: new URL(
|
||||||
`/users/${user.id}`,
|
`/users/${user.id}`,
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
import { applyConfig, handleZodError } from "@api";
|
import { applyConfig, handleZodError } from "@api";
|
||||||
import { zValidator } from "@hono/zod-validator";
|
import { zValidator } from "@hono/zod-validator";
|
||||||
|
import type { EntityValidator } from "@lysand-org/federation";
|
||||||
import { errorResponse, jsonResponse } from "@response";
|
import { errorResponse, jsonResponse } from "@response";
|
||||||
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 type * as Lysand from "lysand-types";
|
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { type Like, likeToLysand } from "~database/entities/Like";
|
import { type Like, likeToLysand } from "~database/entities/Like";
|
||||||
import { db } from "~drizzle/db";
|
import { db } from "~drizzle/db";
|
||||||
|
|
@ -37,7 +37,7 @@ export default (app: Hono) =>
|
||||||
const { uuid } = context.req.valid("param");
|
const { uuid } = context.req.valid("param");
|
||||||
|
|
||||||
let foundObject: Note | Like | null = null;
|
let foundObject: Note | Like | null = null;
|
||||||
let apiObject: Lysand.Entity | null = null;
|
let apiObject: typeof EntityValidator.$Entity | null = null;
|
||||||
|
|
||||||
foundObject = await Note.fromSql(
|
foundObject = await Note.fromSql(
|
||||||
and(
|
and(
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
import { applyConfig, handleZodError } from "@api";
|
import { applyConfig, handleZodError } from "@api";
|
||||||
import { zValidator } from "@hono/zod-validator";
|
import { zValidator } from "@hono/zod-validator";
|
||||||
import { dualLogger } from "@loggers";
|
import { dualLogger } from "@loggers";
|
||||||
|
import { EntityValidator, SignatureValidator } from "@lysand-org/federation";
|
||||||
import { errorResponse, jsonResponse, response } from "@response";
|
import { errorResponse, jsonResponse, response } from "@response";
|
||||||
import { eq } from "drizzle-orm";
|
import { eq } from "drizzle-orm";
|
||||||
import type { Hono } from "hono";
|
import type { Hono } from "hono";
|
||||||
import type * as Lysand from "lysand-types";
|
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { isValidationError } from "zod-validation-error";
|
import { isValidationError } from "zod-validation-error";
|
||||||
import { resolveNote } from "~database/entities/Status";
|
import { resolveNote } from "~database/entities/Status";
|
||||||
|
|
@ -16,7 +16,6 @@ import { db } from "~drizzle/db";
|
||||||
import { Notifications, Relationships } from "~drizzle/schema";
|
import { Notifications, Relationships } from "~drizzle/schema";
|
||||||
import { User } from "~packages/database-interface/user";
|
import { User } from "~packages/database-interface/user";
|
||||||
import { LogLevel } from "~packages/log-manager";
|
import { LogLevel } from "~packages/log-manager";
|
||||||
import { EntityValidator, SignatureValidator } from "~packages/lysand-utils";
|
|
||||||
|
|
||||||
export const meta = applyConfig({
|
export const meta = applyConfig({
|
||||||
allowedMethods: ["POST"],
|
allowedMethods: ["POST"],
|
||||||
|
|
@ -82,29 +81,33 @@ export default (app: Hono) =>
|
||||||
|
|
||||||
const validator = await SignatureValidator.fromStringKey(
|
const validator = await SignatureValidator.fromStringKey(
|
||||||
sender.getUser().publicKey,
|
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) {
|
if (!isValid) {
|
||||||
return errorResponse("Invalid signature", 400);
|
return errorResponse("Invalid signature", 400);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const validator = new EntityValidator(
|
const validator = new EntityValidator();
|
||||||
(await context.req.json()) as Lysand.Entity,
|
const body: typeof EntityValidator.$Entity =
|
||||||
);
|
await context.req.json();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Add sent data to database
|
// Add sent data to database
|
||||||
switch (validator.getType()) {
|
switch (body.type) {
|
||||||
case "Note": {
|
case "Note": {
|
||||||
const note = await validator.validate<Lysand.Note>();
|
const note = await validator.Note(body);
|
||||||
|
|
||||||
const account = await User.resolve(note.author);
|
const account = await User.resolve(note.author);
|
||||||
|
|
||||||
|
|
@ -131,8 +134,7 @@ export default (app: Hono) =>
|
||||||
return response("Note created", 201);
|
return response("Note created", 201);
|
||||||
}
|
}
|
||||||
case "Follow": {
|
case "Follow": {
|
||||||
const follow =
|
const follow = await validator.Follow(body);
|
||||||
await validator.validate<Lysand.Follow>();
|
|
||||||
|
|
||||||
const account = await User.resolve(follow.author);
|
const account = await User.resolve(follow.author);
|
||||||
|
|
||||||
|
|
@ -175,8 +177,7 @@ export default (app: Hono) =>
|
||||||
return response("Follow request sent", 200);
|
return response("Follow request sent", 200);
|
||||||
}
|
}
|
||||||
case "FollowAccept": {
|
case "FollowAccept": {
|
||||||
const followAccept =
|
const followAccept = await validator.FollowAccept(body);
|
||||||
await validator.validate<Lysand.FollowAccept>();
|
|
||||||
|
|
||||||
console.log(followAccept);
|
console.log(followAccept);
|
||||||
|
|
||||||
|
|
@ -211,8 +212,7 @@ export default (app: Hono) =>
|
||||||
return response("Follow request accepted", 200);
|
return response("Follow request accepted", 200);
|
||||||
}
|
}
|
||||||
case "FollowReject": {
|
case "FollowReject": {
|
||||||
const followReject =
|
const followReject = await validator.FollowReject(body);
|
||||||
await validator.validate<Lysand.FollowReject>();
|
|
||||||
|
|
||||||
const account = await User.resolve(followReject.author);
|
const account = await User.resolve(followReject.author);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
import { applyConfig } from "@api";
|
import { applyConfig } from "@api";
|
||||||
import { urlToContentFormat } from "@content_types";
|
import { urlToContentFormat } from "@content_types";
|
||||||
|
import type { EntityValidator } from "@lysand-org/federation";
|
||||||
import { jsonResponse } from "@response";
|
import { jsonResponse } from "@response";
|
||||||
import type { Hono } from "hono";
|
import type { Hono } from "hono";
|
||||||
import type * as Lysand from "lysand-types";
|
|
||||||
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 Lysand.ServerMetadata);
|
} satisfies typeof EntityValidator.$ServerMetadata);
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
import type * as Lysand from "lysand-types";
|
import type { EntityValidator } from "@lysand-org/federation";
|
||||||
import { lookup } from "mime-types";
|
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" };
|
if (!content) return { content: "", format: "text/plain" };
|
||||||
|
|
||||||
const bestFormatsRanked = [
|
const bestFormatsRanked = [
|
||||||
|
|
@ -21,7 +23,7 @@ export const getBestContentType = (content?: Lysand.ContentFormat) => {
|
||||||
|
|
||||||
export const urlToContentFormat = (
|
export const urlToContentFormat = (
|
||||||
url: string,
|
url: string,
|
||||||
): Lysand.ContentFormat | null => {
|
): typeof EntityValidator.$ContentFormat | null => {
|
||||||
if (!url) return null;
|
if (!url) return null;
|
||||||
if (url.startsWith("https://api.dicebear.com/")) {
|
if (url.startsWith("https://api.dicebear.com/")) {
|
||||||
return {
|
return {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue