mirror of
https://github.com/versia-pub/server.git
synced 2025-12-06 08:28:19 +01:00
Fix conversion between database and Lysand types
This commit is contained in:
parent
6d0a8a6478
commit
8563c97403
2
build.ts
2
build.ts
|
|
@ -36,7 +36,7 @@ await $`sed -i 's|import("node_modules/|import("./node_modules/|g' dist/*.js`;
|
|||
// Copy generated Prisma client to dist
|
||||
await $`mkdir -p dist/node_modules/@prisma`;
|
||||
await $`cp -r ${process.cwd()}/node_modules/@prisma dist/node_modules/`;
|
||||
await $`cp -r ${process.cwd()}/node_modules/.prisma dist/node_modules`;
|
||||
//await $`cp -r ${process.cwd()}/node_modules/.prisma dist/node_modules`;
|
||||
await $`mkdir -p dist/node_modules/.bin`;
|
||||
await $`cp -r ${process.cwd()}/node_modules/.bin/prisma dist/node_modules/.bin`;
|
||||
await $`cp -r ${process.cwd()}/node_modules/prisma dist/node_modules/`;
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import type { Config } from "config-manager";
|
|||
import { MediaBackendType } from "media-manager";
|
||||
import type { APIAsyncAttachment } from "~types/entities/async_attachment";
|
||||
import type { APIAttachment } from "~types/entities/attachment";
|
||||
import type * as Lysand from "lysand-types";
|
||||
|
||||
export const attachmentToAPI = (
|
||||
attachment: Attachment,
|
||||
|
|
@ -57,6 +58,28 @@ export const attachmentToAPI = (
|
|||
};
|
||||
};
|
||||
|
||||
export const attachmentToLysand = (
|
||||
attachment: Attachment,
|
||||
): Lysand.ContentFormat => {
|
||||
return {
|
||||
[attachment.mime_type]: {
|
||||
content: attachment.url,
|
||||
blurhash: attachment.blurhash ?? undefined,
|
||||
description: attachment.description ?? undefined,
|
||||
duration: attachment.duration ?? undefined,
|
||||
fps: attachment.fps ?? undefined,
|
||||
height: attachment.height ?? undefined,
|
||||
size: attachment.size ?? undefined,
|
||||
hash: attachment.sha256
|
||||
? {
|
||||
sha256: attachment.sha256,
|
||||
}
|
||||
: undefined,
|
||||
width: attachment.width ?? undefined,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export const getUrl = (name: string, config: Config) => {
|
||||
if (config.media.backend === MediaBackendType.LOCAL) {
|
||||
return new URL(`/media/${name}`, config.http.base_url).toString();
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import type { Emoji } from "@prisma/client";
|
||||
import { client } from "~database/datasource";
|
||||
import type { APIEmoji } from "~types/entities/emoji";
|
||||
import type { Emoji as LysandEmoji } from "~types/lysand/extensions/org.lysand/custom_emojis";
|
||||
import type * as Lysand from "lysand-types";
|
||||
|
||||
/**
|
||||
* Represents an emoji entity in the database.
|
||||
|
|
@ -29,7 +29,7 @@ export const parseEmojis = async (text: string): Promise<Emoji[]> => {
|
|||
});
|
||||
};
|
||||
|
||||
export const addEmojiIfNotExists = async (emoji: LysandEmoji) => {
|
||||
export const addEmojiIfNotExists = async (emoji: Lysand.Emoji) => {
|
||||
const existingEmoji = await client.emoji.findFirst({
|
||||
where: {
|
||||
shortcode: emoji.name,
|
||||
|
|
@ -43,8 +43,8 @@ export const addEmojiIfNotExists = async (emoji: LysandEmoji) => {
|
|||
data: {
|
||||
shortcode: emoji.name,
|
||||
url: emoji.url[0].content,
|
||||
alt: emoji.alt || null,
|
||||
content_type: emoji.url[0].content_type,
|
||||
alt: emoji.alt || emoji.url[0].description || undefined,
|
||||
content_type: Object.keys(emoji.url)[0],
|
||||
visible_in_picker: true,
|
||||
},
|
||||
});
|
||||
|
|
@ -64,34 +64,15 @@ export const emojiToAPI = (emoji: Emoji): APIEmoji => {
|
|||
};
|
||||
};
|
||||
|
||||
export const emojiToLysand = (emoji: Emoji): LysandEmoji => {
|
||||
export const emojiToLysand = (emoji: Emoji): Lysand.Emoji => {
|
||||
return {
|
||||
name: emoji.shortcode,
|
||||
url: [
|
||||
{
|
||||
url: {
|
||||
[emoji.content_type]: {
|
||||
content: emoji.url,
|
||||
content_type: emoji.content_type,
|
||||
description: emoji.alt || undefined,
|
||||
},
|
||||
},
|
||||
],
|
||||
alt: emoji.alt || undefined,
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Converts the emoji to an ActivityPub object.
|
||||
* @returns The ActivityPub object.
|
||||
*/
|
||||
export const emojiToActivityPub = (emoji: Emoji): object => {
|
||||
// replace any with your ActivityPub Emoji type
|
||||
return {
|
||||
type: "Emoji",
|
||||
name: `:${emoji.shortcode}:`,
|
||||
updated: new Date().toISOString(),
|
||||
icon: {
|
||||
type: "Image",
|
||||
url: emoji.url,
|
||||
mediaType: emoji.content_type,
|
||||
alt: emoji.alt || undefined,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,14 +1,14 @@
|
|||
import type { Like } from "@prisma/client";
|
||||
import { config } from "config-manager";
|
||||
import { client } from "~database/datasource";
|
||||
import type { Like as LysandLike } from "~types/lysand/Object";
|
||||
import type { StatusWithRelations } from "./Status";
|
||||
import type { UserWithRelations } from "./User";
|
||||
import type * as Lysand from "lysand-types";
|
||||
|
||||
/**
|
||||
* Represents a Like entity in the database.
|
||||
*/
|
||||
export const toLysand = (like: Like): LysandLike => {
|
||||
export const toLysand = (like: Like): Lysand.Like => {
|
||||
return {
|
||||
id: like.id,
|
||||
// biome-ignore lint/suspicious/noExplicitAny: to be rewritten
|
||||
|
|
@ -17,7 +17,10 @@ export const toLysand = (like: Like): LysandLike => {
|
|||
created_at: new Date(like.createdAt).toISOString(),
|
||||
// biome-ignore lint/suspicious/noExplicitAny: to be rewritten
|
||||
object: (like as any).liked?.uri,
|
||||
uri: new URL(`/actions/${like.id}`, config.http.base_url).toString(),
|
||||
uri: new URL(
|
||||
`/objects/like/${like.id}`,
|
||||
config.http.base_url,
|
||||
).toString(),
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -18,8 +18,9 @@ import { client } from "~database/datasource";
|
|||
import type { APIAttachment } from "~types/entities/attachment";
|
||||
import type { APIStatus } from "~types/entities/status";
|
||||
import type { LysandPublication, Note } from "~types/lysand/Object";
|
||||
import type * as Lysand from "lysand-types";
|
||||
import { applicationToAPI } from "./Application";
|
||||
import { attachmentToAPI } from "./Attachment";
|
||||
import { attachmentToAPI, attachmentToLysand } from "./Attachment";
|
||||
import { emojiToAPI, emojiToLysand, parseEmojis } from "./Emoji";
|
||||
import type { UserWithRelations } from "./User";
|
||||
import { fetchRemoteUser, parseMentionsUris, userToAPI } from "./User";
|
||||
|
|
@ -533,78 +534,33 @@ export const statusToAPI = async (
|
|||
};
|
||||
};
|
||||
|
||||
/* export const statusToActivityPub = async (
|
||||
status: StatusWithRelations
|
||||
// user?: UserWithRelations
|
||||
): Promise<any> => {
|
||||
// replace any with your ActivityPub type
|
||||
return {
|
||||
"@context": [
|
||||
"https://www.w3.org/ns/activitystreams",
|
||||
"https://mastodon.social/schemas/litepub-0.1.jsonld",
|
||||
],
|
||||
id: `${config.http.base_url}/users/${status.authorId}/statuses/${status.id}`,
|
||||
type: "Note",
|
||||
summary: status.spoilerText,
|
||||
content: status.content,
|
||||
published: new Date(status.createdAt).toISOString(),
|
||||
url: `${config.http.base_url}/users/${status.authorId}/statuses/${status.id}`,
|
||||
attributedTo: `${config.http.base_url}/users/${status.authorId}`,
|
||||
to: ["https://www.w3.org/ns/activitystreams#Public"],
|
||||
cc: [], // add recipients here
|
||||
sensitive: status.sensitive,
|
||||
attachment: (status.attachments ?? []).map(
|
||||
a => attachmentToActivityPub(a) as ActivityPubAttachment // replace with your function
|
||||
),
|
||||
tag: [], // add tags here
|
||||
replies: {
|
||||
id: `${config.http.base_url}/users/${status.authorId}/statuses/${status.id}/replies`,
|
||||
type: "Collection",
|
||||
totalItems: status._count.replies,
|
||||
},
|
||||
likes: {
|
||||
id: `${config.http.base_url}/users/${status.authorId}/statuses/${status.id}/likes`,
|
||||
type: "Collection",
|
||||
totalItems: status._count.likes,
|
||||
},
|
||||
shares: {
|
||||
id: `${config.http.base_url}/users/${status.authorId}/statuses/${status.id}/shares`,
|
||||
type: "Collection",
|
||||
totalItems: status._count.reblogs,
|
||||
},
|
||||
inReplyTo: status.inReplyToPostId
|
||||
? `${config.http.base_url}/users/${status.inReplyToPost?.authorId}/statuses/${status.inReplyToPostId}`
|
||||
: null,
|
||||
visibility: "public", // adjust as needed
|
||||
// add more fields as needed
|
||||
};
|
||||
}; */
|
||||
|
||||
export const statusToLysand = (status: StatusWithRelations): Note => {
|
||||
export const statusToLysand = (status: StatusWithRelations): Lysand.Note => {
|
||||
return {
|
||||
type: "Note",
|
||||
created_at: new Date(status.createdAt).toISOString(),
|
||||
id: status.id,
|
||||
author: status.authorId,
|
||||
uri: new URL(`/statuses/${status.id}`, config.http.base_url).toString(),
|
||||
contents: [
|
||||
{
|
||||
uri: new URL(
|
||||
`/objects/note/${status.id}`,
|
||||
config.http.base_url,
|
||||
).toString(),
|
||||
content: {
|
||||
"text/html": {
|
||||
content: status.content,
|
||||
content_type: "text/html",
|
||||
},
|
||||
{
|
||||
// Content converted to plaintext
|
||||
"text/plain": {
|
||||
content: htmlToText(status.content),
|
||||
content_type: "text/plain",
|
||||
},
|
||||
],
|
||||
// TODO: Add attachments
|
||||
attachments: [],
|
||||
},
|
||||
attachments: status.attachments.map((attachment) =>
|
||||
attachmentToLysand(attachment),
|
||||
),
|
||||
is_sensitive: status.sensitive,
|
||||
mentions: status.mentions.map((mention) => mention.uri || ""),
|
||||
quotes: status.quotingPost ? [status.quotingPost.uri || ""] : [],
|
||||
replies_to: status.inReplyToPostId ? [status.inReplyToPostId] : [],
|
||||
quotes: status.quotingPost?.uri ?? undefined,
|
||||
replies_to: status.inReplyToPost?.uri ?? undefined,
|
||||
subject: status.spoilerText,
|
||||
visibility: status.visibility as Lysand.Visibility,
|
||||
extensions: {
|
||||
"org.lysand:custom_emojis": {
|
||||
emojis: status.emojis.map((emoji) => emojiToLysand(emoji)),
|
||||
|
|
|
|||
|
|
@ -6,11 +6,10 @@ import { htmlToText } from "html-to-text";
|
|||
import { client } from "~database/datasource";
|
||||
import type { APIAccount } from "~types/entities/account";
|
||||
import type { APISource } from "~types/entities/source";
|
||||
import type { LysandUser } from "~types/lysand/Object";
|
||||
import type * as Lysand from "lysand-types";
|
||||
import { addEmojiIfNotExists, emojiToAPI, emojiToLysand } from "./Emoji";
|
||||
import { addInstanceIfNotExists } from "./Instance";
|
||||
import { userRelations } from "./relations";
|
||||
import { getUrl } from "./Attachment";
|
||||
import { createNewRelationship } from "./Relationship";
|
||||
|
||||
export interface AuthData {
|
||||
|
|
@ -147,7 +146,7 @@ export const fetchRemoteUser = async (uri: string) => {
|
|||
},
|
||||
});
|
||||
|
||||
const data = (await response.json()) as Partial<LysandUser>;
|
||||
const data = (await response.json()) as Partial<Lysand.User>;
|
||||
|
||||
if (
|
||||
!(
|
||||
|
|
@ -155,9 +154,9 @@ export const fetchRemoteUser = async (uri: string) => {
|
|||
data.username &&
|
||||
data.uri &&
|
||||
data.created_at &&
|
||||
data.disliked &&
|
||||
data.dislikes &&
|
||||
data.featured &&
|
||||
data.liked &&
|
||||
data.likes &&
|
||||
data.followers &&
|
||||
data.following &&
|
||||
data.inbox &&
|
||||
|
|
@ -178,9 +177,9 @@ export const fetchRemoteUser = async (uri: string) => {
|
|||
uri: data.uri,
|
||||
createdAt: new Date(data.created_at),
|
||||
endpoints: {
|
||||
disliked: data.disliked,
|
||||
dislikes: data.dislikes,
|
||||
featured: data.featured,
|
||||
liked: data.liked,
|
||||
likes: data.likes,
|
||||
followers: data.followers,
|
||||
following: data.following,
|
||||
inbox: data.inbox,
|
||||
|
|
@ -444,7 +443,7 @@ export const userToAPI = (
|
|||
/**
|
||||
* Should only return local users
|
||||
*/
|
||||
export const userToLysand = (user: UserWithRelations): LysandUser => {
|
||||
export const userToLysand = (user: UserWithRelations): Lysand.User => {
|
||||
if (user.instanceId !== null) {
|
||||
throw new Error("Cannot convert remote user to Lysand format");
|
||||
}
|
||||
|
|
@ -452,29 +451,28 @@ export const userToLysand = (user: UserWithRelations): LysandUser => {
|
|||
return {
|
||||
id: user.id,
|
||||
type: "User",
|
||||
uri: user.uri || "",
|
||||
bio: [
|
||||
{
|
||||
uri:
|
||||
user.uri ||
|
||||
new URL(`/users/${user.id}`, config.http.base_url).toString(),
|
||||
bio: {
|
||||
"text/html": {
|
||||
content: user.note,
|
||||
content_type: "text/html",
|
||||
},
|
||||
{
|
||||
"text/plain": {
|
||||
content: htmlToText(user.note),
|
||||
content_type: "text/plain",
|
||||
},
|
||||
],
|
||||
},
|
||||
created_at: new Date(user.createdAt).toISOString(),
|
||||
|
||||
disliked: new URL(
|
||||
`/users/${user.id}/disliked`,
|
||||
dislikes: new URL(
|
||||
`/users/${user.id}/dislikes`,
|
||||
config.http.base_url,
|
||||
).toString(),
|
||||
featured: new URL(
|
||||
`/users/${user.id}/featured`,
|
||||
config.http.base_url,
|
||||
).toString(),
|
||||
liked: new URL(
|
||||
`/users/${user.id}/liked`,
|
||||
likes: new URL(
|
||||
`/users/${user.id}/likes`,
|
||||
config.http.base_url,
|
||||
).toString(),
|
||||
followers: new URL(
|
||||
|
|
@ -495,40 +493,35 @@ export const userToLysand = (user: UserWithRelations): LysandUser => {
|
|||
).toString(),
|
||||
indexable: false,
|
||||
username: user.username,
|
||||
avatar: [
|
||||
{
|
||||
content: getAvatarUrl(user, config) || "",
|
||||
content_type: `image/${user.avatar.split(".")[1]}`,
|
||||
avatar: {
|
||||
[user.avatar.split(".")[1]]: {
|
||||
content: getAvatarUrl(user, config),
|
||||
},
|
||||
},
|
||||
header: {
|
||||
[user.header.split(".")[1]]: {
|
||||
content: getHeaderUrl(user, config),
|
||||
},
|
||||
],
|
||||
header: [
|
||||
{
|
||||
content: getHeaderUrl(user, config) || "",
|
||||
content_type: `image/${user.header.split(".")[1]}`,
|
||||
},
|
||||
],
|
||||
display_name: user.displayName,
|
||||
|
||||
fields: (user.source as APISource).fields.map((field) => ({
|
||||
key: [
|
||||
{
|
||||
key: {
|
||||
"text/html": {
|
||||
content: field.name,
|
||||
content_type: "text/html",
|
||||
},
|
||||
{
|
||||
"text/plain": {
|
||||
content: htmlToText(field.name),
|
||||
content_type: "text/plain",
|
||||
},
|
||||
],
|
||||
value: [
|
||||
{
|
||||
},
|
||||
value: {
|
||||
"text/html": {
|
||||
content: field.value,
|
||||
content_type: "text/html",
|
||||
},
|
||||
{
|
||||
"text/plain": {
|
||||
content: htmlToText(field.value),
|
||||
content_type: "text/plain",
|
||||
},
|
||||
],
|
||||
},
|
||||
})),
|
||||
public_key: {
|
||||
actor: new URL(
|
||||
|
|
|
|||
|
|
@ -44,9 +44,19 @@ export interface Entity {
|
|||
created_at: string;
|
||||
uri: string;
|
||||
type: string;
|
||||
extensions?: {
|
||||
"org.lysand:custom_emojis"?: {
|
||||
emojis: Emoji[];
|
||||
};
|
||||
[key: string]: object | undefined;
|
||||
};
|
||||
}
|
||||
|
||||
export interface Publication {
|
||||
export interface InlineCustomEmojis {
|
||||
[key: string]: Emoji;
|
||||
}
|
||||
|
||||
export interface Publication extends Entity {
|
||||
type: "Note" | "Patch";
|
||||
author: string;
|
||||
content?: ContentFormat;
|
||||
|
|
@ -57,6 +67,19 @@ export interface Publication {
|
|||
subject?: string;
|
||||
is_sensitive?: boolean;
|
||||
visibility: Visibility;
|
||||
extensions?: Entity["extensions"] & {
|
||||
"org.lysand:reactions"?: {
|
||||
reactions: string;
|
||||
};
|
||||
"org.lysand:polls"?: {
|
||||
poll: {
|
||||
options: ContentFormat[];
|
||||
votes: number[];
|
||||
multiple_choice?: boolean;
|
||||
expires_at: string;
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export enum Visibility {
|
||||
|
|
@ -96,6 +119,9 @@ export interface User extends Entity {
|
|||
dislikes: string;
|
||||
inbox: string;
|
||||
outbox: string;
|
||||
extensions?: Entity["extensions"] & {
|
||||
"org.lysand:vanity"?: VanityExtension;
|
||||
};
|
||||
}
|
||||
|
||||
export interface Field {
|
||||
|
|
|
|||
Loading…
Reference in a new issue