From 5eed8374cd50a41b2e5c30e204f2aabb01fe8caf Mon Sep 17 00:00:00 2001 From: Jesse Wierzbinski Date: Fri, 10 Nov 2023 16:36:06 -1000 Subject: [PATCH] HUGE rewrite to use Prisma instead of TypeORM (not finished yet) --- benchmarks/posting.ts | 8 +- bun.lockb | Bin 459724 -> 316704 bytes database/datasource.ts | 7 +- database/entities/Application.ts | 100 +- database/entities/Emoji.ts | 195 ++-- database/entities/Instance.ts | 119 +-- database/entities/Like.ts | 50 +- database/entities/Object.ts | 204 ++-- database/entities/Relationship.ts | 193 +--- database/entities/Status.ts | 963 ++++++++--------- database/entities/Token.ts | 51 - database/entities/User.ts | 991 +++++++----------- index.ts | 17 +- package.json | 5 +- prisma/schema.prisma | 166 +++ server/api/.well-known/webfinger/index.ts | 5 +- server/api/api/v1/accounts/[id]/block.ts | 6 +- server/api/api/v1/accounts/[id]/follow.ts | 6 +- server/api/api/v1/accounts/[id]/index.ts | 8 +- server/api/api/v1/accounts/[id]/mute.ts | 6 +- server/api/api/v1/accounts/[id]/note.ts | 6 +- server/api/api/v1/accounts/[id]/pin.ts | 6 +- .../v1/accounts/[id]/remove_from_followers.ts | 6 +- server/api/api/v1/accounts/[id]/statuses.ts | 4 +- server/api/api/v1/accounts/[id]/unblock.ts | 6 +- server/api/api/v1/accounts/[id]/unfollow.ts | 6 +- server/api/api/v1/accounts/[id]/unmute.ts | 6 +- server/api/api/v1/accounts/[id]/unpin.ts | 6 +- .../v1/accounts/familiar_followers/index.ts | 6 +- server/api/api/v1/accounts/index.ts | 6 +- .../api/v1/accounts/relationships/index.ts | 6 +- .../v1/accounts/update_credentials/index.ts | 10 +- .../v1/accounts/verify_credentials/index.ts | 4 +- server/api/api/v1/apps/index.ts | 4 +- .../api/v1/apps/verify_credentials/index.ts | 8 +- server/api/api/v1/custom_emojis/index.ts | 4 +- server/api/api/v1/instance/index.ts | 4 +- server/api/api/v1/statuses/[id]/context.ts | 4 +- server/api/api/v1/statuses/[id]/favourite.ts | 4 +- .../api/api/v1/statuses/[id]/favourited_by.ts | 4 +- server/api/api/v1/statuses/[id]/index.ts | 4 +- .../api/api/v1/statuses/[id]/reblogged_by.ts | 4 +- .../api/api/v1/statuses/[id]/unfavourite.ts | 4 +- server/api/api/v1/statuses/index.ts | 17 +- server/api/api/v1/timelines/home.ts | 11 +- server/api/api/v1/timelines/public.ts | 77 +- server/api/auth/login/index.ts | 8 +- server/api/users/[uuid]/inbox/index.ts | 12 +- server/api/users/[uuid]/index.ts | 4 +- tests/api.test.ts | 20 +- tests/api/accounts.test.ts | 14 +- tests/api/statuses.test.ts | 22 +- tests/oauth.test.ts | 12 +- 53 files changed, 1452 insertions(+), 1967 deletions(-) create mode 100644 prisma/schema.prisma diff --git a/benchmarks/posting.ts b/benchmarks/posting.ts index fdb405ab..f3e90d46 100644 --- a/benchmarks/posting.ts +++ b/benchmarks/posting.ts @@ -2,10 +2,10 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ import { getConfig } from "@config"; import { AppDataSource } from "~database/datasource"; -import { Application } from "~database/entities/Application"; +import { ApplicationAction } from "~database/entities/Application"; import { RawActivity } from "~database/entities/RawActivity"; import { Token, TokenType } from "~database/entities/Token"; -import { User } from "~database/entities/User"; +import { UserAction } from "~database/entities/User"; const config = getConfig(); @@ -13,14 +13,14 @@ let token: Token; if (!AppDataSource.isInitialized) await AppDataSource.initialize(); // Initialize test user -const user = await User.createNewLocal({ +const user = await UserAction.createNewLocal({ email: "test@test.com", username: "test", password: "test", display_name: "", }); -const app = new Application(); +const app = new ApplicationAction(); app.name = "Test Application"; app.website = "https://example.com"; diff --git a/bun.lockb b/bun.lockb index 9fb7e5c1a7639b7e416302af61029f4f80d7e6a2..50befd6c108eb6bd4dfaa58583855fd52970a3dd 100755 GIT binary patch delta 82589 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 bq@RV~;l%0QalYTztUM^4e2t|8y%zo#4$rf2 literal 459724 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%0A|oC|3Zo5aXpov*b%B7)}^I-u)2$?bo4UpudG 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 diff --git a/database/datasource.ts b/database/datasource.ts index 70447cac..1bec9a7b 100644 --- a/database/datasource.ts +++ b/database/datasource.ts @@ -1,5 +1,6 @@ import { DataSource } from "typeorm"; import { getConfig } from "../utils/config"; +import { PrismaClient } from "@prisma/client"; const config = getConfig(); @@ -14,4 +15,8 @@ const AppDataSource = new DataSource({ entities: [process.cwd() + "/database/entities/*.ts"], }); -export { AppDataSource }; +const client = new PrismaClient({ + datasourceUrl: `postgresql://${config.database.username}:${config.database.password}@${config.database.host}:${config.database.port}/${config.database.database}`, +}); + +export { AppDataSource, client }; diff --git a/database/entities/Application.ts b/database/entities/Application.ts index 16eb7689..94a79cf6 100644 --- a/database/entities/Application.ts +++ b/database/entities/Application.ts @@ -1,76 +1,42 @@ -import { BaseEntity, Column, Entity, PrimaryGeneratedColumn } from "typeorm"; import { APIApplication } from "~types/entities/application"; -import { Token } from "./Token"; +import { Application } from "@prisma/client"; +import { client } from "~database/datasource"; /** * Represents an application that can authenticate with the API. */ -@Entity({ - name: "applications", -}) -export class Application extends BaseEntity { - /** The unique identifier for this application. */ - @PrimaryGeneratedColumn("uuid") - id!: string; - /** The name of this application. */ - @Column("varchar") - name!: string; +/** + * Retrieves the application associated with the given access token. + * @param token The access token to retrieve the application for. + * @returns The application associated with the given access token, or null if no such application exists. + */ +export const getFromToken = async ( + token: string +): Promise => { + const dbToken = await client.token.findFirst({ + where: { + access_token: token, + }, + include: { + application: true, + }, + }); - /** The website associated with this application, if any. */ - @Column("varchar", { - nullable: true, - }) - website!: string | null; + return dbToken?.application || null; +}; - /** The VAPID key associated with this application, if any. */ - @Column("varchar", { - nullable: true, - }) - vapid_key!: string | null; - - /** The client ID associated with this application. */ - @Column("varchar") - client_id!: string; - - /** The secret associated with this application. */ - @Column("varchar") - secret!: string; - - /** The scopes associated with this application. */ - @Column("varchar") - scopes = "read"; - - /** The redirect URIs associated with this application. */ - @Column("varchar") - redirect_uris = "urn:ietf:wg:oauth:2.0:oob"; - - /** - * Retrieves the application associated with the given access token. - * @param token The access token to retrieve the application for. - * @returns The application associated with the given access token, or null if no such application exists. - */ - static async getFromToken(token: string): Promise { - const dbToken = await Token.findOne({ - where: { - access_token: token, - }, - relations: ["application"], - }); - - return dbToken?.application || null; - } - - /** - * Converts this application to an API application. - * @returns The API application representation of this application. - */ +/** + * Converts this application to an API application. + * @returns The API application representation of this application. + */ +export const applicationToAPI = async ( + app: Application // eslint-disable-next-line @typescript-eslint/require-await - async toAPI(): Promise { - return { - name: this.name, - website: this.website, - vapid_key: this.vapid_key, - }; - } -} +): Promise => { + return { + name: app.name, + website: app.website, + vapid_key: app.vapid_key, + }; +}; diff --git a/database/entities/Emoji.ts b/database/entities/Emoji.ts index 45d3e5ef..c2359dfc 100644 --- a/database/entities/Emoji.ts +++ b/database/entities/Emoji.ts @@ -1,137 +1,78 @@ -import { - BaseEntity, - Column, - Entity, - IsNull, - ManyToOne, - PrimaryGeneratedColumn, -} from "typeorm"; import { APIEmoji } from "~types/entities/emoji"; -import { Instance } from "./Instance"; import { Emoji as LysandEmoji } from "~types/lysand/extensions/org.lysand/custom_emojis"; +import { client } from "~database/datasource"; +import { Emoji } from "@prisma/client"; /** * Represents an emoji entity in the database. */ -@Entity({ - name: "emojis", -}) -export class Emoji extends BaseEntity { - /** - * The unique identifier for the emoji. - */ - @PrimaryGeneratedColumn("uuid") - id!: string; - /** - * The shortcode for the emoji. - */ - @Column("varchar") - shortcode!: string; - - /** - * The instance that the emoji is from. - * If is null, the emoji is from the server's instance - */ - @ManyToOne(() => Instance, { - nullable: true, - }) - instance!: Instance | null; - - /** - * The URL for the emoji. - */ - @Column("varchar") - url!: string; - - /** - * The alt text for the emoji. - */ - @Column("varchar", { - nullable: true, - }) - alt!: string | null; - - /** - * The content type of the emoji. - */ - @Column("varchar") - content_type!: string; - - /** - * Whether the emoji is visible in the picker. - */ - @Column("boolean") - visible_in_picker!: boolean; - - /** - * Used for parsing emojis from local text - * @param text The text to parse - * @returns An array of emojis - */ - static async parseEmojis(text: string): Promise { - const regex = /:[a-zA-Z0-9_]+:/g; - const matches = text.match(regex); - if (!matches) return []; - return ( - await Promise.all( - matches.map(match => - Emoji.findOne({ - where: { - shortcode: match.slice(1, -1), - instance: IsNull(), - }, - relations: ["instance"], - }) - ) - ) - ).filter(emoji => emoji !== null) as Emoji[]; - } - - static async addIfNotExists(emoji: LysandEmoji) { - const existingEmoji = await Emoji.findOne({ - where: { - shortcode: emoji.name, - instance: IsNull(), +/** + * Used for parsing emojis from local text + * @param text The text to parse + * @returns An array of emojis + */ +export const parseEmojis = async (text: string): Promise => { + const regex = /:[a-zA-Z0-9_]+:/g; + const matches = text.match(regex); + if (!matches) return []; + return await client.emoji.findMany({ + where: { + shortcode: { + in: matches.map(match => match.replace(/:/g, "")), }, - }); - if (existingEmoji) return existingEmoji; - const newEmoji = new Emoji(); - newEmoji.shortcode = emoji.name; - // TODO: Content types - newEmoji.url = emoji.url[0].content; - newEmoji.alt = emoji.alt || null; - newEmoji.content_type = emoji.url[0].content_type; - newEmoji.visible_in_picker = true; - await newEmoji.save(); - return newEmoji; - } + }, + include: { + instance: true, + }, + }); +}; - /** - * Converts the emoji to an APIEmoji object. - * @returns The APIEmoji object. - */ - // eslint-disable-next-line @typescript-eslint/require-await - async toAPI(): Promise { - return { - shortcode: this.shortcode, - static_url: this.url, // TODO: Add static version - url: this.url, - visible_in_picker: this.visible_in_picker, - category: undefined, - }; - } +export const addEmojiIfNotExists = async (emoji: LysandEmoji) => { + const existingEmoji = await client.emoji.findFirst({ + where: { + shortcode: emoji.name, + instance: null, + }, + }); - toLysand(): LysandEmoji { - return { - name: this.shortcode, - url: [ - { - content: this.url, - content_type: this.content_type, - }, - ], - alt: this.alt || undefined, - }; - } -} + if (existingEmoji) return existingEmoji; + + return await client.emoji.create({ + data: { + shortcode: emoji.name, + url: emoji.url[0].content, + alt: emoji.alt || null, + content_type: emoji.url[0].content_type, + visible_in_picker: true, + }, + }); +}; + +/** + * Converts the emoji to an APIEmoji object. + * @returns The APIEmoji object. + */ +// eslint-disable-next-line @typescript-eslint/require-await +export const emojiToAPI = async (emoji: Emoji): Promise => { + return { + shortcode: emoji.shortcode, + static_url: emoji.url, // TODO: Add static version + url: emoji.url, + visible_in_picker: emoji.visible_in_picker, + category: undefined, + }; +}; + +export const emojiToLysand = (emoji: Emoji): LysandEmoji => { + return { + name: emoji.shortcode, + url: [ + { + content: emoji.url, + content_type: emoji.content_type, + }, + ], + alt: emoji.alt || undefined, + }; +}; diff --git a/database/entities/Instance.ts b/database/entities/Instance.ts index 36ee42fb..2d6d7152 100644 --- a/database/entities/Instance.ts +++ b/database/entities/Instance.ts @@ -1,90 +1,49 @@ -import { BaseEntity, Column, Entity, PrimaryGeneratedColumn } from "typeorm"; -import { ContentFormat, ServerMetadata } from "~types/lysand/Object"; +import { Instance } from "@prisma/client"; +import { client } from "~database/datasource"; +import { ServerMetadata } from "~types/lysand/Object"; /** * Represents an instance in the database. */ -@Entity({ - name: "instances", -}) -export class Instance extends BaseEntity { - /** - * The unique identifier of the instance. - */ - @PrimaryGeneratedColumn("uuid") - id!: string; - /** - * The base URL of the instance. - * Must not have the https:// or http:// prefix. - */ - @Column("varchar") - base_url!: string; +/** + * Adds an instance to the database if it doesn't already exist. + * @param url + * @returns Either the database instance if it already exists, or a newly created instance. + */ +export const addInstanceIfNotExists = async ( + url: string +): Promise => { + const origin = new URL(url).origin; + const hostname = new URL(url).hostname; - /** - * The name of the instance. - */ - @Column("varchar") - name!: string; + const found = await client.instance.findFirst({ + where: { + base_url: hostname, + }, + }); - /** - * The description of the instance. - */ - @Column("varchar") - version!: string; + if (found) return found; - /** - * The logo of the instance. - */ - @Column("jsonb") - logo?: ContentFormat[]; + // Fetch the instance configuration + const metadata = (await fetch(`${origin}/.well-known/lysand`).then(res => + res.json() + )) as Partial; - /** - * The banner of the instance. - */ - banner?: ContentFormat[]; - - /** - * Adds an instance to the database if it doesn't already exist. - * @param url - * @returns Either the database instance if it already exists, or a newly created instance. - */ - static async addIfNotExists(url: string): Promise { - const origin = new URL(url).origin; - const hostname = new URL(url).hostname; - - const found = await Instance.findOne({ - where: { - base_url: hostname, - }, - }); - - if (found) return found; - - const instance = new Instance(); - - instance.base_url = hostname; - - // Fetch the instance configuration - const metadata = (await fetch(`${origin}/.well-known/lysand`).then( - res => res.json() - )) as Partial; - - if (metadata.type !== "ServerMetadata") { - throw new Error("Invalid instance metadata"); - } - - if (!(metadata.name && metadata.version)) { - throw new Error("Invalid instance metadata"); - } - - instance.name = metadata.name; - instance.version = metadata.version; - instance.logo = metadata.logo; - instance.banner = metadata.banner; - - await instance.save(); - - return instance; + if (metadata.type !== "ServerMetadata") { + throw new Error("Invalid instance metadata"); } -} + + if (!(metadata.name && metadata.version)) { + throw new Error("Invalid instance metadata"); + } + + return await client.instance.create({ + data: { + base_url: hostname, + name: metadata.name, + version: metadata.version, + logo: metadata.logo as any, + }, + }); +}; diff --git a/database/entities/Like.ts b/database/entities/Like.ts index a4bb72d1..1d0d7ceb 100644 --- a/database/entities/Like.ts +++ b/database/entities/Like.ts @@ -1,45 +1,19 @@ -import { - BaseEntity, - CreateDateColumn, - Entity, - ManyToOne, - PrimaryGeneratedColumn, -} from "typeorm"; -import { User } from "./User"; -import { Status } from "./Status"; +/* eslint-disable @typescript-eslint/no-unsafe-member-access */ import { Like as LysandLike } from "~types/lysand/Object"; import { getConfig } from "@config"; +import { Like } from "@prisma/client"; /** * Represents a Like entity in the database. */ -@Entity({ - name: "likes", -}) -export class Like extends BaseEntity { - /** The unique identifier of the Like. */ - @PrimaryGeneratedColumn("uuid") - id!: string; - /** The User who liked the Status. */ - @ManyToOne(() => User) - liker!: User; - - /** The Status that was liked. */ - @ManyToOne(() => Status) - liked!: Status; - - @CreateDateColumn() - created_at!: Date; - - toLysand(): LysandLike { - return { - id: this.id, - author: this.liker.uri, - type: "Like", - created_at: new Date(this.created_at).toISOString(), - object: this.liked.toLysand().uri, - uri: `${getConfig().http.base_url}/actions/${this.id}`, - }; - } -} +export const toLysand = (like: Like): LysandLike => { + return { + id: like.id, + author: (like as any).liker?.uri, + type: "Like", + created_at: new Date(like.createdAt).toISOString(), + object: (like as any).liked?.uri, + uri: `${getConfig().http.base_url}/actions/${like.id}`, + }; +}; diff --git a/database/entities/Object.ts b/database/entities/Object.ts index a2ab1ee3..aae884d2 100644 --- a/database/entities/Object.ts +++ b/database/entities/Object.ts @@ -1,147 +1,87 @@ -import { - BaseEntity, - Column, - Entity, - ManyToOne, - PrimaryGeneratedColumn, -} from "typeorm"; +/* eslint-disable @typescript-eslint/no-unsafe-return */ +/* eslint-disable @typescript-eslint/no-unsafe-member-access */ +import { LysandObject } from "@prisma/client"; +import { client } from "~database/datasource"; import { LysandObjectType } from "~types/lysand/Object"; /** * Represents a Lysand object in the database. */ -@Entity({ - name: "objects", -}) -export class LysandObject extends BaseEntity { - /** - * The unique identifier for the object. If local, same as `remote_id` - */ - @PrimaryGeneratedColumn("uuid") - id!: string; - /** - * UUID of the object across the network. If the object is local, same as `id` - */ - remote_id!: string; +export const createFromObject = async (object: LysandObjectType) => { + const foundObject = await client.lysandObject.findFirst({ + where: { remote_id: object.id }, + include: { + author: true, + }, + }); - /** - * Any valid Lysand type, such as `Note`, `Like`, `Follow`, etc. - */ - @Column("varchar") - type!: string; - - /** - * Remote URI for the object - * Example: `https://example.com/publications/ef235cc6-d68c-4756-b0df-4e6623c4d51c` - */ - @Column("varchar") - uri!: string; - - @Column("timestamp") - created_at!: Date; - - /** - * References an Actor object - */ - @ManyToOne(() => LysandObject, object => object.uri, { - nullable: true, - }) - author!: LysandObject | null; - - @Column("jsonb") - extra_data!: Omit< - Omit, "id">, "uri">, - "type" - >; - - @Column("jsonb") - extensions!: Record; - - static new(type: string, uri: string): LysandObject { - const object = new LysandObject(); - object.type = type; - object.uri = uri; - object.created_at = new Date(); - return object; + if (foundObject) { + return foundObject; } - static async createFromObject(object: LysandObjectType) { - let newObject: LysandObject; + const author = await client.lysandObject.findFirst({ + where: { uri: (object as any).author }, + }); - const foundObject = await LysandObject.findOne({ - where: { remote_id: object.id }, - relations: ["author"], - }); + return await client.lysandObject.create({ + data: { + authorId: author?.id, + created_at: new Date(object.created_at), + extensions: object.extensions || {}, + remote_id: object.id, + type: object.type, + uri: object.uri, + // Rest of data (remove id, author, created_at, extensions, type, uri) + extra_data: Object.fromEntries( + Object.entries(object).filter( + ([key]) => + ![ + "id", + "author", + "created_at", + "extensions", + "type", + "uri", + ].includes(key) + ) + ), + }, + }); +}; - if (foundObject) { - newObject = foundObject; - } else { - newObject = new LysandObject(); - } +export const toLysand = (lyObject: LysandObject): LysandObjectType => { + return { + id: lyObject.remote_id || lyObject.id, + created_at: new Date(lyObject.created_at).toISOString(), + type: lyObject.type, + uri: lyObject.uri, + // @ts-expect-error This works, I promise + ...lyObject.extra_data, + extensions: lyObject.extensions, + }; +}; - const author = await LysandObject.findOne({ - // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access - where: { uri: (object as any).author }, - }); +export const isPublication = (lyObject: LysandObject): boolean => { + return lyObject.type === "Note" || lyObject.type === "Patch"; +}; - newObject.author = author; - newObject.created_at = new Date(object.created_at); - newObject.extensions = object.extensions || {}; - newObject.remote_id = object.id; - newObject.type = object.type; - newObject.uri = object.uri; - // Rest of data (remove id, author, created_at, extensions, type, uri) - newObject.extra_data = Object.fromEntries( - Object.entries(object).filter( - ([key]) => - ![ - "id", - "author", - "created_at", - "extensions", - "type", - "uri", - ].includes(key) - ) - ); +export const isAction = (lyObject: LysandObject): boolean => { + return [ + "Like", + "Follow", + "Dislike", + "FollowAccept", + "FollowReject", + "Undo", + "Announce", + ].includes(lyObject.type); +}; - await newObject.save(); - return newObject; - } +export const isActor = (lyObject: LysandObject): boolean => { + return lyObject.type === "User"; +}; - toLysand(): LysandObjectType { - return { - id: this.remote_id || this.id, - created_at: new Date(this.created_at).toISOString(), - type: this.type, - uri: this.uri, - ...this.extra_data, - extensions: this.extensions, - }; - } - - isPublication(): boolean { - return this.type === "Note" || this.type === "Patch"; - } - - isAction(): boolean { - return [ - "Like", - "Follow", - "Dislike", - "FollowAccept", - "FollowReject", - "Undo", - "Announce", - ].includes(this.type); - } - - isActor(): boolean { - return this.type === "User"; - } - - isExtension(): boolean { - return this.type === "Extension"; - } -} +export const isExtension = (lyObject: LysandObject): boolean => { + return lyObject.type === "Extension"; +}; diff --git a/database/entities/Relationship.ts b/database/entities/Relationship.ts index 5bfe53bd..7b3162c3 100644 --- a/database/entities/Relationship.ts +++ b/database/entities/Relationship.ts @@ -1,144 +1,63 @@ -import { - BaseEntity, - Column, - CreateDateColumn, - Entity, - ManyToOne, - PrimaryGeneratedColumn, - UpdateDateColumn, -} from "typeorm"; -import { User } from "./User"; +import { Relationship, User } from "@prisma/client"; import { APIRelationship } from "~types/entities/relationship"; +import { client } from "~database/datasource"; /** * Stores Mastodon API relationships */ -@Entity({ - name: "relationships", -}) -export class Relationship extends BaseEntity { - /** The unique identifier for the relationship. */ - @PrimaryGeneratedColumn("uuid") - id!: string; - /** The user who owns the relationship. */ - @ManyToOne(() => User, user => user.relationships) - owner!: User; +/** + * Creates a new relationship between two users. + * @param owner The user who owns the relationship. + * @param other The user who is the subject of the relationship. + * @returns The newly created relationship. + */ +export const createNew = async ( + owner: User, + other: User +): Promise => { + return await client.relationship.create({ + data: { + ownerId: owner.id, + subjectId: other.id, + languages: [], + following: false, + showingReblogs: false, + notifying: false, + followedBy: false, + blocking: false, + blockedBy: false, + muting: false, + mutingNotifications: false, + requested: false, + domainBlocking: false, + endorsed: false, + note: "", + }, + }); +}; - /** The user who is the subject of the relationship. */ - @ManyToOne(() => User) - subject!: User; - - /** Whether the owner is following the subject. */ - @Column("boolean") - following!: boolean; - - /** Whether the owner is showing reblogs from the subject. */ - @Column("boolean") - showing_reblogs!: boolean; - - /** Whether the owner is receiving notifications from the subject. */ - @Column("boolean") - notifying!: boolean; - - /** Whether the owner is followed by the subject. */ - @Column("boolean") - followed_by!: boolean; - - /** Whether the owner is blocking the subject. */ - @Column("boolean") - blocking!: boolean; - - /** Whether the owner is blocked by the subject. */ - @Column("boolean") - blocked_by!: boolean; - - /** Whether the owner is muting the subject. */ - @Column("boolean") - muting!: boolean; - - /** Whether the owner is muting notifications from the subject. */ - @Column("boolean") - muting_notifications!: boolean; - - /** Whether the owner has requested to follow the subject. */ - @Column("boolean") - requested!: boolean; - - /** Whether the owner is blocking the subject's domain. */ - @Column("boolean") - domain_blocking!: boolean; - - /** Whether the owner has endorsed the subject. */ - @Column("boolean") - endorsed!: boolean; - - /** The languages the owner has specified for the subject. */ - @Column("jsonb") - languages!: string[]; - - /** A note the owner has added for the subject. */ - @Column("varchar") - note!: string; - - /** The date the relationship was created. */ - @CreateDateColumn() - created_at!: Date; - - /** The date the relationship was last updated. */ - @UpdateDateColumn() - updated_at!: Date; - - /** - * Creates a new relationship between two users. - * @param owner The user who owns the relationship. - * @param other The user who is the subject of the relationship. - * @returns The newly created relationship. - */ - static async createNew(owner: User, other: User): Promise { - const newRela = new Relationship(); - newRela.owner = owner; - newRela.subject = other; - newRela.languages = []; - newRela.following = false; - newRela.showing_reblogs = false; - newRela.notifying = false; - newRela.followed_by = false; - newRela.blocking = false; - newRela.blocked_by = false; - newRela.muting = false; - newRela.muting_notifications = false; - newRela.requested = false; - newRela.domain_blocking = false; - newRela.endorsed = false; - newRela.note = ""; - - await newRela.save(); - - return newRela; - } - - /** - * Converts the relationship to an API-friendly format. - * @returns The API-friendly relationship. - */ - // eslint-disable-next-line @typescript-eslint/require-await - async toAPI(): Promise { - return { - blocked_by: this.blocked_by, - blocking: this.blocking, - domain_blocking: this.domain_blocking, - endorsed: this.endorsed, - followed_by: this.followed_by, - following: this.following, - id: this.subject.id, - muting: this.muting, - muting_notifications: this.muting_notifications, - notifying: this.notifying, - requested: this.requested, - showing_reblogs: this.showing_reblogs, - languages: this.languages, - note: this.note, - }; - } -} +/** + * Converts the relationship to an API-friendly format. + * @returns The API-friendly relationship. + */ +// eslint-disable-next-line @typescript-eslint/require-await +export const toAPI = async (rel: Relationship): Promise => { + return { + blocked_by: rel.blockedBy, + blocking: rel.blocking, + domain_blocking: rel.domainBlocking, + endorsed: rel.endorsed, + followed_by: rel.followedBy, + following: rel.following, + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + id: (rel as any).subject.id, + muting: rel.muting, + muting_notifications: rel.mutingNotifications, + notifying: rel.notifying, + requested: rel.requested, + showing_reblogs: rel.showingReblogs, + languages: rel.languages, + note: rel.note, + }; +}; diff --git a/database/entities/Status.ts b/database/entities/Status.ts index 69791eaa..2cecbe3c 100644 --- a/database/entities/Status.ts +++ b/database/entities/Status.ts @@ -1,583 +1,456 @@ +/* eslint-disable @typescript-eslint/no-unsafe-member-access */ import { getConfig } from "@config"; import { - BaseEntity, - Column, - CreateDateColumn, - Entity, - JoinTable, - ManyToMany, - ManyToOne, - PrimaryGeneratedColumn, - RemoveOptions, - Tree, - TreeChildren, - TreeParent, - UpdateDateColumn, -} from "typeorm"; -import { APIStatus } from "~types/entities/status"; -import { User, userRelations } from "./User"; -import { Application } from "./Application"; -import { Emoji } from "./Emoji"; -import { Instance } from "./Instance"; -import { Like } from "./Like"; -import { AppDataSource } from "~database/datasource"; + UserWithRelations, + fetchRemoteUser, + parseMentionsUris, + userRelations, + userToAPI, +} from "./User"; +import { client } from "~database/datasource"; import { LysandPublication, Note } from "~types/lysand/Object"; import { htmlToText } from "html-to-text"; import { getBestContentType } from "@content_types"; +import { + Application, + Emoji, + Instance, + Like, + Relationship, + Status, + User, +} from "@prisma/client"; +import { emojiToAPI, emojiToLysand, parseEmojis } from "./Emoji"; +import { APIStatus } from "~types/entities/status"; +import { applicationToAPI } from "./Application"; const config = getConfig(); -export const statusRelations = [ - "account", - "reblog", - "in_reply_to_post", - "instance", - "in_reply_to_post.account", - "application", - "emojis", - "mentions", -]; +export const statusAndUserRelations = { + author: { + include: userRelations, + }, + application: true, + emojis: true, + inReplyToPost: { + include: { + author: { + include: userRelations, + }, + application: true, + emojis: true, + inReplyToPost: { + include: { + author: true, + }, + }, + instance: true, + mentions: true, + pinnedBy: true, + replies: { + include: { + _count: true, + }, + }, + }, + }, + instance: true, + mentions: true, + pinnedBy: true, + replies: { + include: { + _count: true, + }, + }, + reblog: { + include: { + author: { + include: userRelations, + }, + application: true, + emojis: true, + inReplyToPost: { + include: { + author: true, + }, + }, + instance: true, + mentions: true, + pinnedBy: true, + replies: { + include: { + _count: true, + }, + }, + }, + }, + quotingPost: { + include: { + author: { + include: userRelations, + }, + application: true, + emojis: true, + inReplyToPost: { + include: { + author: true, + }, + }, + instance: true, + mentions: true, + pinnedBy: true, + replies: { + include: { + _count: true, + }, + }, + }, + }, + likes: { + include: { + liker: true, + }, + }, +}; -export const statusAndUserRelations = [ - ...statusRelations, - // Can't directly map to userRelations as variable isnt yet initialized - ...[ - "account.relationships", - "account.pinned_notes", - "account.instance", - "account.emojis", - ], -]; +type StatusWithRelations = Status & { + author: UserWithRelations; + application: Application | null; + emojis: Emoji[]; + inReplyToPost: + | (Status & { + author: UserWithRelations; + application: Application | null; + emojis: Emoji[]; + inReplyToPost: Status | null; + instance: Instance | null; + mentions: User[]; + pinnedBy: User[]; + replies: Status[] & { + _count: number; + }; + }) + | null; + instance: Instance | null; + mentions: User[]; + pinnedBy: User[]; + replies: Status[] & { + _count: number; + }; + reblog: + | (Status & { + author: UserWithRelations; + application: Application | null; + emojis: Emoji[]; + inReplyToPost: Status | null; + instance: Instance | null; + mentions: User[]; + pinnedBy: User[]; + replies: Status[] & { + _count: number; + }; + }) + | null; + quotingPost: + | (Status & { + author: UserWithRelations; + application: Application | null; + emojis: Emoji[]; + inReplyToPost: Status | null; + instance: Instance | null; + mentions: User[]; + pinnedBy: User[]; + replies: Status[] & { + _count: number; + }; + }) + | null; + likes: (Like & { + liker: User; + })[]; +}; /** * Represents a status (i.e. a post) */ -@Entity({ - name: "statuses", -}) -@Tree("closure-table") -export class Status extends BaseEntity { - /** - * The unique identifier for this status. - */ - @PrimaryGeneratedColumn("uuid") - id!: string; - /** - * The URI for this status. - */ - @Column("varchar") - uri!: string; - - /** - * The user account that created this status. - */ - @ManyToOne(() => User, user => user.id) - account!: User; - - /** - * The date and time when this status was created. - */ - @CreateDateColumn() - created_at!: Date; - - /** - * The date and time when this status was last updated. - */ - @UpdateDateColumn() - updated_at!: Date; - - /** - * The status that this status is a reblog of, if any. - */ - @ManyToOne(() => Status, status => status.id, { - nullable: true, - onDelete: "SET NULL", - }) - reblog?: Status | null; - - /** - * Whether this status is a reblog. - */ - @Column("boolean") - isReblog!: boolean; - - /** - * The content of this status. - */ - @Column("varchar", { - default: "", - }) - content!: string; - - /** - * The content type of this status. - */ - @Column("varchar", { - default: "text/plain", - }) - content_type!: string; - - /** - * The visibility of this status. - */ - @Column("varchar") - visibility!: APIStatus["visibility"]; - - /** - * The raw object that this status is a reply to, if any. - */ - @TreeParent({ - onDelete: "SET NULL", - }) - in_reply_to_post!: Status | null; - - /** - * The status that this status is quoting, if any - */ - @ManyToOne(() => Status, status => status.id, { - nullable: true, - }) - quoting_post!: Status | null; - - @TreeChildren() - replies!: Status[]; - - /** - * The status' instance - */ - @ManyToOne(() => Instance, { - nullable: true, - }) - instance!: Instance | null; - - /** - * Whether this status is sensitive. - */ - @Column("boolean") - sensitive!: boolean; - - /** - * The spoiler text for this status. - */ - @Column("varchar", { - default: "", - }) - spoiler_text!: string; - - /** - * The application associated with this status, if any. - */ - @ManyToOne(() => Application, app => app.id, { - nullable: true, - }) - application!: Application | null; - - /** - * The emojis associated with this status. - */ - @ManyToMany(() => Emoji, emoji => emoji.id) - @JoinTable() - emojis!: Emoji[]; - - /** - * The users mentioned (excluding followers and such) - */ - @ManyToMany(() => User, user => user.id) - @JoinTable() - mentions!: User[]; - - /** - * Removes this status from the database. - * @param options The options for removing this status. - * @returns A promise that resolves when the status has been removed. - */ - async remove(options?: RemoveOptions | undefined) { - // Delete object - - // Get all associated Likes and remove them as well - await Like.delete({ - liked: { - id: this.id, - }, - }); - - return await super.remove(options); - } - - async parseEmojis(string: string) { - const emojis = [...string.matchAll(/:([a-zA-Z0-9_]+):/g)].map( - match => match[1] +/** + * Returns whether this status is viewable by a user. + * @param user The user to check. + * @returns Whether this status is viewable by the user. + */ +export const isViewableByUser = (status: Status, user: User | null) => { + if (status.visibility === "public") return true; + else if (status.visibility === "unlisted") return true; + else if (status.visibility === "private") { + // @ts-expect-error Prisma TypeScript types dont include relations + return !!(user?.relationships as Relationship[]).find( + rel => rel.id === status.authorId ); + } else { + // @ts-expect-error Prisma TypeScript types dont include relations + return user && (status.mentions as User[]).includes(user); + } +}; - const emojiObjects = await Promise.all( - emojis.map(async emoji => { - const emojiObject = await Emoji.findOne({ - where: { - shortcode: emoji, - }, - }); +export const fetchFromRemote = async (uri: string): Promise => { + // Check if already in database - return emojiObject; - }) - ); + const existingStatus = await client.status.findFirst({ + where: { + uri: uri, + }, + include: statusAndUserRelations, + }); - return emojiObjects.filter(emoji => emoji !== null) as Emoji[]; + if (existingStatus) return existingStatus; + + const status = await fetch(uri); + + if (status.status === 404) return null; + + const body = (await status.json()) as LysandPublication; + + const content = getBestContentType(body.contents); + + const emojis = await parseEmojis(content?.content || ""); + + const author = await fetchRemoteUser(body.author); + + let replyStatus: Status | null = null; + let quotingStatus: Status | null = null; + + if (body.replies_to.length > 0) { + replyStatus = await fetchFromRemote(body.replies_to[0]); } - /** - * Returns whether this status is viewable by a user. - * @param user The user to check. - * @returns Whether this status is viewable by the user. - */ - isViewableByUser(user: User | null) { - const relationship = user?.relationships.find( - rel => rel.id === this.account.id - ); - - if (this.visibility === "public") return true; - else if (this.visibility === "unlisted") return true; - else if (this.visibility === "private") { - return !!relationship?.following; - } else { - return user && this.mentions.includes(user); - } + if (body.quotes.length > 0) { + quotingStatus = await fetchFromRemote(body.quotes[0]); } - static async fetchFromRemote(uri: string) { - // Check if already in database + return await createNew({ + account: author, + content: content?.content || "", + content_type: content?.content_type, + application: null, + // TODO: Add visibility + visibility: "public", + spoiler_text: body.subject || "", + uri: body.uri, + sensitive: body.is_sensitive, + emojis: emojis, + mentions: await parseMentionsUris(body.mentions), + reply: replyStatus + ? { + status: replyStatus, + user: (replyStatus as any).author, + } + : undefined, + quote: quotingStatus || undefined, + }); +}; - const existingStatus = await Status.findOne({ - where: { - uri: uri, - }, - }); +/** + * Return all the ancestors of this post, + */ +// eslint-disable-next-line @typescript-eslint/require-await, @typescript-eslint/no-unused-vars +export const getAncestors = async (fetcher: UserWithRelations | null) => { + // TODO: Implement + return []; +}; - if (existingStatus) return existingStatus; +/** + * Return all the descendants of this post, + */ +// eslint-disable-next-line @typescript-eslint/require-await, @typescript-eslint/no-unused-vars +export const getDescendants = async (fetcher: UserWithRelations | null) => { + // TODO: Implement + return []; +}; - const status = await fetch(uri); - - if (status.status === 404) return null; - - const body = (await status.json()) as LysandPublication; - - const content = getBestContentType(body.contents); - - const emojis = await Emoji.parseEmojis(content?.content || ""); - - const author = await User.fetchRemoteUser(body.author); - - let replyStatus: Status | null = null; - let quotingStatus: Status | null = null; - - if (body.replies_to.length > 0) { - replyStatus = await Status.fetchFromRemote(body.replies_to[0]); - } - - if (body.quotes.length > 0) { - quotingStatus = await Status.fetchFromRemote(body.quotes[0]); - } - - const newStatus = await Status.createNew({ - account: author, - content: content?.content || "", - content_type: content?.content_type, - application: null, - // TODO: Add visibility - visibility: "public", - spoiler_text: body.subject || "", - uri: body.uri, - sensitive: body.is_sensitive, - emojis: emojis, - mentions: await User.parseMentions(body.mentions), - reply: replyStatus - ? { - status: replyStatus, - user: replyStatus.account, - } - : undefined, - quote: quotingStatus || undefined, - }); - - return await newStatus.save(); - } - - /** - * Return all the ancestors of this post, - */ - async getAncestors(fetcher: User | null) { - const max = fetcher ? 4096 : 40; - const ancestors = []; - - let id = this.in_reply_to_post?.id; - - while (ancestors.length < max && id) { - const currentStatus = await Status.findOne({ - where: { - id: id, - }, - relations: statusAndUserRelations, - }); - - if (currentStatus) { - if (currentStatus.isViewableByUser(fetcher)) { - ancestors.push(currentStatus); - } - id = currentStatus.in_reply_to_post?.id; - } else { - break; - } - } - - return ancestors; - } - - /** - * Return all the descendants of this post, - */ - async getDescendants(fetcher: User | null) { - const max = fetcher ? 4096 : 60; - - const descendants = await AppDataSource.getTreeRepository( - Status - ).findDescendantsTree(this, { - depth: fetcher ? 20 : undefined, - relations: statusAndUserRelations, - }); - - // Go through .replies of each descendant recursively and add them to the list - const flatten = (descendants: Status): Status[] => { - const flattened = []; - - for (const descendant of descendants.replies) { - if (descendant.isViewableByUser(fetcher)) { - flattened.push(descendant); - } - - flattened.push(...flatten(descendant)); - } - - return flattened; - }; - - const flattened = flatten(descendants); - - return flattened.slice(0, max); - } - - /** - * Creates a new status and saves it to the database. - * @param data The data for the new status. - * @returns A promise that resolves with the new status. - */ - static async createNew(data: { - account: User; - application: Application | null; - content: string; - visibility: APIStatus["visibility"]; - sensitive: boolean; - spoiler_text: string; - emojis: Emoji[]; - content_type?: string; - uri?: string; - mentions?: User[]; - reply?: { - status: Status; - user: User; - }; - quote?: Status; - }) { - const newStatus = new Status(); - - newStatus.account = data.account; - newStatus.application = data.application ?? null; - newStatus.content = data.content; - newStatus.content_type = data.content_type ?? "text/plain"; - newStatus.visibility = data.visibility; - newStatus.sensitive = data.sensitive; - newStatus.spoiler_text = data.spoiler_text; - newStatus.emojis = data.emojis; - newStatus.isReblog = false; - newStatus.mentions = []; - newStatus.instance = data.account.instance; - newStatus.uri = - data.uri || `${config.http.base_url}/statuses/${newStatus.id}`; - newStatus.quoting_post = data.quote || null; - - if (data.reply) { - newStatus.in_reply_to_post = data.reply.status; - } - // Get people mentioned in the content - const mentionedPeople = [ - ...data.content.matchAll(/@([a-zA-Z0-9_]+)/g), - ].map(match => { +/** + * Creates a new status and saves it to the database. + * @param data The data for the new status. + * @returns A promise that resolves with the new status. + */ +const createNew = async (data: { + account: User; + application: Application | null; + content: string; + visibility: APIStatus["visibility"]; + sensitive: boolean; + spoiler_text: string; + emojis: Emoji[]; + content_type?: string; + uri?: string; + mentions?: User[]; + reply?: { + status: Status; + user: User; + }; + quote?: Status; +}) => { + // Get people mentioned in the content + const mentionedPeople = [...data.content.matchAll(/@([a-zA-Z0-9_]+)/g)].map( + match => { return `${config.http.base_url}/users/${match[1]}`; - }); - - // Get list of mentioned users - if (!data.mentions) { - await Promise.all( - mentionedPeople.map(async person => { - // Check if post is in format @username or @username@instance.com - // If is @username, the user is a local user - const instanceUrl = - person.split("@").length === 3 - ? person.split("@")[2] - : null; - - if (instanceUrl) { - const user = await User.findOne({ - where: { - username: person.split("@")[1], - // If contains instanceUrl - instance: { - base_url: instanceUrl, - }, - }, - relations: userRelations, - }); - - newStatus.mentions.push(user as User); - } else { - const user = await User.findOne({ - where: { - username: person.split("@")[1], - }, - relations: userRelations, - }); - - newStatus.mentions.push(user as User); - } - }) - ); - } else { - newStatus.mentions = data.mentions; } + ); - await newStatus.save(); - return newStatus; + let mentions = data.mentions || []; + + // Get list of mentioned users + if (mentions.length === 0) { + mentions = await client.user.findMany({ + where: { + OR: mentionedPeople.map(person => ({ + username: person.split("@")[1], + instance: { + base_url: person.split("@")[2], + }, + })), + }, + include: userRelations, + }); } - async isFavouritedBy(user: User) { - const like = await Like.findOne({ + const status = await client.status.create({ + data: { + authorId: data.account.id, + applicationId: data.application?.id, + content: data.content, + contentType: data.content_type, + visibility: data.visibility, + sensitive: data.sensitive, + spoilerText: data.spoiler_text, + emojis: { + connect: data.emojis.map(emoji => { + return { + id: emoji.id, + }; + }), + }, + inReplyToPostId: data.reply?.status.id, + quotingPostId: data.quote?.id, + instanceId: data.account.instanceId || undefined, + isReblog: false, + uri: data.uri || `${config.http.base_url}/statuses/xxx`, + mentions: { + connect: mentions.map(mention => { + return { + id: mention.id, + }; + }), + }, + }, + include: statusAndUserRelations, + }); + + if (!data.uri) status.uri = `${config.http.base_url}/statuses/${status.id}`; + + return status; +}; + +export const isFavouritedBy = async (status: Status, user: User) => { + return !!(await client.like.findFirst({ + where: { + likerId: user.id, + likedId: status.id, + }, + })); +}; + +/** + * Converts this status to an API status. + * @returns A promise that resolves with the API status. + */ +export const statusToAPI = async ( + status: StatusWithRelations, + user?: UserWithRelations +): Promise => { + return { + id: status.id, + in_reply_to_id: status.inReplyToPostId || null, + in_reply_to_account_id: status.inReplyToPost?.authorId || null, + account: await userToAPI(status.author), + created_at: new Date(status.createdAt).toISOString(), + application: status.application + ? await applicationToAPI(status.application) + : null, + card: null, + content: status.content, + emojis: await Promise.all( + status.emojis.map(emoji => emojiToAPI(emoji)) + ), + favourited: !!status.likes.find(like => like.likerId === user?.id), + favourites_count: status.likes.length, + media_attachments: [], + mentions: [], + language: null, + muted: user + ? user.relationships.find(r => r.subjectId == status.authorId) + ?.muting || false + : false, + pinned: status.author.pinnedNotes.some(note => note.id === status.id), + // TODO: Add pols + poll: null, + reblog: status.reblog + ? await statusToAPI(status.reblog as unknown as StatusWithRelations) + : null, + reblogged: !!(await client.status.findFirst({ where: { - liker: { - id: user.id, - }, - liked: { - id: this.id, - }, + authorId: user?.id, + reblogId: status.id, }, - relations: ["liker"], - }); - - return !!like; - } - - /** - * Converts this status to an API status. - * @returns A promise that resolves with the API status. - */ - async toAPI(user?: User): Promise { - const reblogCount = await Status.count({ + })), + reblogs_count: await client.status.count({ where: { - reblog: { - id: this.id, - }, + reblogId: status.id, }, - relations: ["reblog"], - }); + }), + replies_count: status.replies._count, + sensitive: status.sensitive, + spoiler_text: status.spoilerText, + tags: [], + uri: `${config.http.base_url}/statuses/${status.id}`, + visibility: "public", + url: `${config.http.base_url}/statuses/${status.id}`, + bookmarked: false, + quote: null, + quote_id: undefined, + }; +}; - const repliesCount = await Status.count({ - where: { - in_reply_to_post: { - id: this.id, - }, +export const statusToLysand = (status: StatusWithRelations): Note => { + return { + type: "Note", + created_at: new Date(status.createdAt).toISOString(), + id: status.id, + author: status.authorId, + uri: `${config.http.base_url}/users/${status.authorId}/statuses/${status.id}`, + contents: [ + { + content: status.content, + content_type: "text/html", }, - relations: ["in_reply_to_post"], - }); - - const favourited = user ? await this.isFavouritedBy(user) : false; - - const favourites_count = await Like.count({ - where: { - liked: { - id: this.id, - }, + { + // Content converted to plaintext + content: htmlToText(status.content), + content_type: "text/plain", }, - relations: ["liked"], - }); - - return { - id: this.id, - in_reply_to_id: this.in_reply_to_post?.id || null, - in_reply_to_account_id: this.in_reply_to_post?.account.id || null, - account: await this.account.toAPI(), - created_at: new Date(this.created_at).toISOString(), - application: (await this.application?.toAPI()) || null, - card: null, - content: this.content, - emojis: await Promise.all(this.emojis.map(emoji => emoji.toAPI())), - favourited, - favourites_count: favourites_count, - media_attachments: [], - mentions: await Promise.all( - this.mentions.map(async m => await m.toAPI()) - ), - language: null, - muted: false, - pinned: this.account.pinned_notes.some(note => note.id === this.id), - poll: null, - reblog: this.reblog ? await this.reblog.toAPI() : null, - reblogged: !!this.reblog, - reblogs_count: reblogCount, - replies_count: repliesCount, - sensitive: this.sensitive, - spoiler_text: this.spoiler_text, - tags: [], - uri: `${config.http.base_url}/users/${this.account.username}/statuses/${this.id}`, - visibility: "public", - url: `${config.http.base_url}/users/${this.account.username}/statuses/${this.id}`, - bookmarked: false, - quote: null, - quote_id: undefined, - }; - } - - toLysand(): Note { - return { - type: "Note", - created_at: new Date(this.created_at).toISOString(), - id: this.id, - author: this.account.uri, - uri: `${config.http.base_url}/users/${this.account.id}/statuses/${this.id}`, - contents: [ - { - content: this.content, - content_type: "text/html", - }, - { - // Content converted to plaintext - content: htmlToText(this.content), - content_type: "text/plain", - }, - ], - // TODO: Add attachments - attachments: [], - is_sensitive: this.sensitive, - mentions: this.mentions.map(mention => mention.id), - // TODO: Add quotes - quotes: [], - replies_to: this.in_reply_to_post?.id - ? [this.in_reply_to_post.id] - : [], - subject: this.spoiler_text, - extensions: { - "org.lysand:custom_emojis": { - emojis: this.emojis.map(emoji => emoji.toLysand()), - }, - // TODO: Add polls and reactions + ], + // TODO: Add attachments + attachments: [], + is_sensitive: status.sensitive, + mentions: status.mentions.map(mention => mention.uri), + quotes: status.quotingPost ? [status.quotingPost.uri] : [], + replies_to: status.inReplyToPostId ? [status.inReplyToPostId] : [], + subject: status.spoilerText, + extensions: { + "org.lysand:custom_emojis": { + emojis: status.emojis.map(emoji => emojiToLysand(emoji)), }, - }; - } -} + // TODO: Add polls and reactions + }, + }; +}; diff --git a/database/entities/Token.ts b/database/entities/Token.ts index db14c330..a46b9ab2 100644 --- a/database/entities/Token.ts +++ b/database/entities/Token.ts @@ -1,54 +1,3 @@ -import { - Entity, - BaseEntity, - PrimaryGeneratedColumn, - Column, - CreateDateColumn, - ManyToOne, -} from "typeorm"; -import { User } from "./User"; -import { Application } from "./Application"; - -/** - * Represents an access token for a user or application. - */ -@Entity({ - name: "tokens", -}) -export class Token extends BaseEntity { - /** The unique identifier for the token. */ - @PrimaryGeneratedColumn("uuid") - id!: string; - - /** The type of token. */ - @Column("varchar") - token_type: TokenType = TokenType.BEARER; - - /** The scope of the token. */ - @Column("varchar") - scope!: string; - - /** The access token string. */ - @Column("varchar") - access_token!: string; - - /** The authorization code used to obtain the token. */ - @Column("varchar") - code!: string; - - /** The date and time the token was created. */ - @CreateDateColumn() - created_at!: Date; - - /** The user associated with the token. */ - @ManyToOne(() => User, user => user.id) - user!: User; - - /** The application associated with the token. */ - @ManyToOne(() => Application, application => application.id) - application!: Application; -} - /** * The type of token. */ diff --git a/database/entities/User.ts b/database/entities/User.ts index 6e08df94..13c31bf9 100644 --- a/database/entities/User.ts +++ b/database/entities/User.ts @@ -1,655 +1,462 @@ import { ConfigType, getConfig } from "@config"; -import { - BaseEntity, - Column, - CreateDateColumn, - Entity, - JoinTable, - ManyToMany, - ManyToOne, - OneToMany, - PrimaryGeneratedColumn, - RemoveOptions, - UpdateDateColumn, -} from "typeorm"; import { APIAccount } from "~types/entities/account"; -import { Token } from "./Token"; -import { Status, statusRelations } from "./Status"; -import { APISource } from "~types/entities/source"; -import { Relationship } from "./Relationship"; -import { Instance } from "./Instance"; import { User as LysandUser } from "~types/lysand/Object"; import { htmlToText } from "html-to-text"; -import { Emoji } from "./Emoji"; +import { + Emoji, + Instance, + Like, + Relationship, + Status, + User, +} from "@prisma/client"; +import { client } from "~database/datasource"; +import { addEmojiIfNotExists, emojiToAPI, emojiToLysand } from "./Emoji"; +import { addInstanceIfNotExists } from "./Instance"; +import { APISource } from "~types/entities/source"; -export const userRelations = [ - "relationships", - "pinned_notes", - "instance", - "emojis", -]; +export interface AuthData { + user: UserWithRelations | null; + token: string; +} /** * Represents a user in the database. * Stores local and remote users */ -@Entity({ - name: "users", -}) -export class User extends BaseEntity { - /** - * The unique identifier for the user. - */ - @PrimaryGeneratedColumn("uuid") - id!: string; - /** - * The user URI on the global network - */ - @Column("varchar") - uri!: string; +export const userRelations = { + emojis: true, + instance: true, + likes: true, + relationships: true, + relationshipSubjects: true, + pinnedNotes: true, + statuses: { + select: { + _count: true, + }, + }, +}; - /** - * The username for the user. - */ - @Column("varchar", { - unique: true, - }) - username!: string; +export type UserWithRelations = User & { + emojis: Emoji[]; + instance: Instance | null; + likes: Like[]; + relationships: Relationship[]; + relationshipSubjects: Relationship[]; + pinnedNotes: Status[]; + statuses: { + length: number; + }; +}; - /** - * The display name for the user. - */ - @Column("varchar") - display_name!: string; +/** + * Get the user's avatar in raw URL format + * @param config The config to use + * @returns The raw URL for the user's avatar + */ +export const getAvatarUrl = (user: User, config: ConfigType) => { + if (!user.avatar) return config.defaults.avatar; + if (config.media.backend === "local") { + return `${config.http.base_url}/media/${user.avatar}`; + } else if (config.media.backend === "s3") { + return `${config.s3.public_url}/${user.avatar}`; + } + return ""; +}; - /** - * The password for the user. - */ - @Column("varchar", { - nullable: true, - }) - password!: string | null; +/** + * Get the user's header in raw URL format + * @param config The config to use + * @returns The raw URL for the user's header + */ +export const getHeaderUrl = (user: User, config: ConfigType) => { + if (!user.header) return config.defaults.header; + if (config.media.backend === "local") { + return `${config.http.base_url}/media/${user.header}`; + } else if (config.media.backend === "s3") { + return `${config.s3.public_url}/${user.header}`; + } + return ""; +}; - /** - * The email address for the user. - */ - @Column("varchar", { - unique: true, - nullable: true, - }) - email!: string | null; +export const getFromRequest = async (req: Request): Promise => { + // Check auth token + const token = req.headers.get("Authorization")?.split(" ")[1] || ""; - /** - * The note for the user. - */ - @Column("varchar", { - default: "", - }) - note!: string; + return { user: await retrieveUserFromToken(token), token }; +}; - /** - * Whether the user is an admin or not. - */ - @Column("boolean", { - default: false, - }) - is_admin!: boolean; +export const fetchRemoteUser = async (uri: string) => { + // Check if user not already in database + const foundUser = await client.user.findUnique({ + where: { + uri, + }, + include: userRelations, + }); - @Column("jsonb", { - nullable: true, - }) - endpoints!: { - liked: string; - disliked: string; - featured: string; - followers: string; - following: string; - inbox: string; - outbox: string; - } | null; + if (foundUser) return foundUser; - /** - * The source for the user. - */ - @Column("jsonb") - source!: APISource; + const response = await fetch(uri, { + method: "GET", + headers: { + "Content-Type": "application/json", + Accept: "application/json", + }, + }); - /** - * The avatar for the user (filename, as UUID) - */ - @Column("varchar") - avatar!: string; + const data = (await response.json()) as Partial; - /** - * The header for the user (filename, as UUID) - */ - @Column("varchar") - header!: string; - - /** - * The date the user was created. - */ - @CreateDateColumn() - created_at!: Date; - - /** - * The date the user was last updated. - */ - @UpdateDateColumn() - updated_at!: Date; - - /** - * The public key for the user. - */ - @Column("varchar") - public_key!: string; - - /** - * The private key for the user. - */ - @Column("varchar", { - nullable: true, - }) - private_key!: string | null; - - /** - * The relationships for the user. - */ - @OneToMany(() => Relationship, relationship => relationship.owner) - relationships!: Relationship[]; - - /** - * User's instance, null if local user - */ - @ManyToOne(() => Instance, { - nullable: true, - }) - instance!: Instance | null; - - /** - * The pinned notes for the user. - */ - @ManyToMany(() => Status, status => status.id) - @JoinTable() - pinned_notes!: Status[]; - - /** - * The emojis for the user. - */ - @ManyToMany(() => Emoji, emoji => emoji.id) - @JoinTable() - emojis!: Emoji[]; - - /** - * Get the user's avatar in raw URL format - * @param config The config to use - * @returns The raw URL for the user's avatar - */ - getAvatarUrl(config: ConfigType) { - if (config.media.backend === "local") { - return `${config.http.base_url}/media/${this.avatar}`; - } else if (config.media.backend === "s3") { - return `${config.s3.public_url}/${this.avatar}`; - } + if ( + !( + data.id && + data.username && + data.uri && + data.created_at && + data.disliked && + data.featured && + data.liked && + data.followers && + data.following && + data.inbox && + data.outbox && + data.public_key + ) + ) { + throw new Error("Invalid user data"); } - /** - * Get the user's header in raw URL format - * @param config The config to use - * @returns The raw URL for the user's header - */ - getHeaderUrl(config: ConfigType) { - if (config.media.backend === "local") { - return `${config.http.base_url}/media/${this.header}`; - } else if (config.media.backend === "s3") { - return `${config.s3.public_url}/${this.header}`; - } - } + // Parse emojis and add them to database + const userEmojis = + data.extensions?.["org.lysand:custom_emojis"]?.emojis ?? []; - static async getFromRequest(req: Request) { - // Check auth token - const token = req.headers.get("Authorization")?.split(" ")[1] || ""; - - return { user: await User.retrieveFromToken(token), token }; - } - - static async fetchRemoteUser(uri: string) { - // Check if user not already in database - const foundUser = await User.findOne({ - where: { - uri, + const user = await client.user.create({ + data: { + username: data.username, + uri: data.uri, + createdAt: new Date(data.created_at), + endpoints: { + disliked: data.disliked, + featured: data.featured, + liked: data.liked, + followers: data.followers, + following: data.following, + inbox: data.inbox, + outbox: data.outbox, }, - }); - - if (foundUser) return foundUser; - - const response = await fetch(uri, { - method: "GET", - headers: { - "Content-Type": "application/json", - Accept: "application/json", + avatar: (data.avatar && data.avatar[0].content) || "", + header: (data.header && data.header[0].content) || "", + displayName: data.display_name ?? "", + note: data.bio?.[0].content ?? "", + publicKey: data.public_key.public_key, + source: { + language: null, + note: "", + privacy: "public", + sensitive: false, + fields: [], }, - }); + }, + }); - const data = (await response.json()) as Partial; + const emojis = []; - const user = new User(); - - if ( - !( - data.id && - data.username && - data.uri && - data.created_at && - data.disliked && - data.featured && - data.liked && - data.followers && - data.following && - data.inbox && - data.outbox && - data.public_key - ) - ) { - throw new Error("Invalid user data"); - } - - user.id = data.id; - user.username = data.username; - user.uri = data.uri; - user.created_at = new Date(data.created_at); - user.endpoints = { - disliked: data.disliked, - featured: data.featured, - liked: data.liked, - followers: data.followers, - following: data.following, - inbox: data.inbox, - outbox: data.outbox, - }; - - user.avatar = (data.avatar && data.avatar[0].content) || ""; - user.header = (data.header && data.header[0].content) || ""; - user.display_name = data.display_name ?? ""; - // TODO: Add bio content types - user.note = data.bio?.[0].content ?? ""; - - // Parse emojis and add them to database - const emojis = - data.extensions?.["org.lysand:custom_emojis"]?.emojis ?? []; - - for (const emoji of emojis) { - user.emojis.push(await Emoji.addIfNotExists(emoji)); - } - - user.public_key = data.public_key.public_key; - - const uriData = new URL(data.uri); - - user.instance = await Instance.addIfNotExists(uriData.origin); - - await user.save(); - return user; + for (const emoji of userEmojis) { + emojis.push(await addEmojiIfNotExists(emoji)); } - /** - * Fetches the list of followers associated with the actor and updates the user's followers - */ - async fetchFollowers() { - // - } + const uriData = new URL(data.uri); - /** - * Gets a user by actor ID. - * @param id The actor ID to search for. - * @returns The user with the given actor ID. - */ - static async getByActorId(id: string) { - return await User.createQueryBuilder("user") - // Objects is a many-to-many relationship - .leftJoinAndSelect("user.actor", "actor") - .leftJoinAndSelect("user.relationships", "relationships") - .where("actor.data @> :data", { - data: JSON.stringify({ - id, - }), - }) - .getOne(); - } - - /** - * Creates a new LOCAL user. - * @param data The data for the new user. - * @returns The newly created user. - */ - static async createNewLocal(data: { - username: string; - display_name?: string; - password: string; - email: string; - bio?: string; - avatar?: string; - header?: string; - }) { - const config = getConfig(); - const user = new User(); - - user.username = data.username; - user.display_name = data.display_name ?? data.username; - user.password = await Bun.password.hash(data.password); - user.email = data.email; - user.note = data.bio ?? ""; - user.avatar = data.avatar ?? config.defaults.avatar; - user.header = data.header ?? config.defaults.avatar; - user.uri = `${config.http.base_url}/users/${user.id}`; - user.emojis = []; - - user.relationships = []; - user.instance = null; - - user.source = { - language: null, - note: "", - privacy: "public", - sensitive: false, - fields: [], - }; - - user.pinned_notes = []; - - await user.generateKeys(); - await user.save(); - - return user; - } - - static async parseMentions(mentions: string[]) { - return await Promise.all( - mentions.map(async mention => { - const user = await User.findOne({ - where: { - uri: mention, - }, - relations: userRelations, - }); - - if (user) return user; - else return await User.fetchRemoteUser(mention); - }) - ); - } - - /** - * Retrieves a user from a token. - * @param access_token The access token to retrieve the user from. - * @returns The user associated with the given access token. - */ - static async retrieveFromToken(access_token: string) { - if (!access_token) return null; - - const token = await Token.findOne({ - where: { - access_token, + return await client.user.update({ + where: { + id: user.id, + }, + data: { + emojis: { + connect: emojis.map(emoji => ({ + id: emoji.id, + })), }, - relations: userRelations.map(r => `user.${r}`), - }); + instanceId: (await addInstanceIfNotExists(uriData.origin)).id, + }, + include: userRelations, + }); +}; - if (!token) return null; +/** + * Fetches the list of followers associated with the actor and updates the user's followers + */ +export const fetchFollowers = () => { + // +}; - return token.user; - } +/** + * Creates a new LOCAL user. + * @param data The data for the new user. + * @returns The newly created user. + */ +export const createNewLocalUser = async (data: { + username: string; + display_name?: string; + password: string; + email: string; + bio?: string; + avatar?: string; + header?: string; +}) => { + const config = getConfig(); - /** - * Gets the relationship to another user. - * @param other The other user to get the relationship to. - * @returns The relationship to the other user. - */ - async getRelationshipToOtherUser(other: User) { - const relationship = await Relationship.findOne({ - where: { - owner: { - id: this.id, - }, - subject: { - id: other.id, - }, + const keys = await generateUserKeys(); + + const user = await client.user.create({ + data: { + username: data.username, + displayName: data.display_name ?? data.username, + password: await Bun.password.hash(data.password), + email: data.email, + note: data.bio ?? "", + avatar: data.avatar ?? config.defaults.avatar, + header: data.header ?? config.defaults.avatar, + uri: "", + publicKey: keys.public_key, + privateKey: keys.private_key, + source: { + language: null, + note: "", + privacy: "public", + sensitive: false, + fields: [], }, - relations: ["owner", "subject"], - }); + }, + }); - return relationship; - } + return await client.user.update({ + where: { + id: user.id, + }, + data: { + uri: `${config.http.base_url}/users/${user.id}`, + }, + include: userRelations, + }); +}; - /** - * Removes the user. - * @param options The options for removing the user. - * @returns The removed user. - */ - async remove(options?: RemoveOptions | undefined) { - // Clean up tokens - const tokens = await Token.findBy({ +/** + * Parses mentions from a list of URIs + */ +export const parseMentionsUris = async (mentions: string[]) => { + return await client.user.findMany({ + where: { + uri: { + in: mentions, + }, + }, + include: userRelations, + }); +}; + +/** + * Retrieves a user from a token. + * @param access_token The access token to retrieve the user from. + * @returns The user associated with the given access token. + */ +export const retrieveUserFromToken = async (access_token: string) => { + if (!access_token) return null; + + const token = await client.token.findFirst({ + where: { + access_token, + }, + include: { user: { - id: this.id, + include: userRelations, }, - }); + }, + }); - const statuses = await Status.find({ - where: { - account: { - id: this.id, - }, - }, - relations: statusRelations, - }); + if (!token) return null; - // Delete both - await Promise.all(tokens.map(async token => await token.remove())); + return token.user; +}; - await Promise.all(statuses.map(async status => await status.remove())); +/** + * Gets the relationship to another user. + * @param other The other user to get the relationship to. + * @returns The relationship to the other user. + */ +export const getRelationshipToOtherUser = async ( + user: UserWithRelations, + other: User +) => { + return await client.relationship.findFirst({ + where: { + ownerId: user.id, + subjectId: other.id, + }, + }); +}; - // Get relationships - const relationships = await this.getRelationships(); - // Delete them all - await Promise.all( - relationships.map(async relationship => await relationship.remove()) - ); +/** + * Generates keys for the user. + */ +export const generateUserKeys = async () => { + const keys = (await crypto.subtle.generateKey("Ed25519", true, [ + "sign", + "verify", + ])) as CryptoKeyPair; - return await super.remove(options); - } - - /** - * Gets the relationships for the user. - * @returns The relationships for the user. - */ - async getRelationships() { - const relationships = await Relationship.find({ - where: { - owner: { - id: this.id, - }, - }, - relations: ["subject"], - }); - - return relationships; - } - - /** - * Generates keys for the user. - */ - async generateKeys(): Promise { - const keys = (await crypto.subtle.generateKey("Ed25519", true, [ - "sign", - "verify", - ])) as CryptoKeyPair; - - const privateKey = btoa( - String.fromCharCode.apply(null, [ - ...new Uint8Array( - // jesus help me what do these letters mean - await crypto.subtle.exportKey("pkcs8", keys.privateKey) - ), - ]) - ); - const publicKey = btoa( - String.fromCharCode( - ...new Uint8Array( - // why is exporting a key so hard - await crypto.subtle.exportKey("spki", keys.publicKey) - ) + const privateKey = btoa( + String.fromCharCode.apply(null, [ + ...new Uint8Array( + // jesus help me what do these letters mean + await crypto.subtle.exportKey("pkcs8", keys.privateKey) + ), + ]) + ); + const publicKey = btoa( + String.fromCharCode( + ...new Uint8Array( + // why is exporting a key so hard + await crypto.subtle.exportKey("spki", keys.publicKey) ) - ); + ) + ); - // Add header, footer and newlines later on - // These keys are base64 encrypted - this.private_key = privateKey; - this.public_key = publicKey; + // Add header, footer and newlines later on + // These keys are base64 encrypted + return { + private_key: privateKey, + public_key: publicKey, + }; +}; + +// eslint-disable-next-line @typescript-eslint/require-await +export const userToAPI = async ( + user: UserWithRelations, + isOwnAccount = false +): Promise => { + const config = getConfig(); + + return { + id: user.id, + username: user.username, + display_name: user.displayName, + note: user.note, + url: user.uri, + avatar: getAvatarUrl(user, config), + header: getHeaderUrl(user, config), + // TODO: Add locked + locked: false, + created_at: new Date(user.createdAt).toISOString(), + followers_count: user.relationshipSubjects.filter(r => r.following) + .length, + following_count: user.relationships.filter(r => r.following).length, + statuses_count: user.statuses.length, + emojis: await Promise.all(user.emojis.map(emoji => emojiToAPI(emoji))), + // TODO: Add fields + fields: [], + // TODO: Add bot + bot: false, + source: + isOwnAccount && user.source + ? (user.source as any as APISource) + : undefined, + // TODO: Add static avatar and header + avatar_static: "", + header_static: "", + acct: + user.instance === null + ? `${user.username}` + : `${user.username}@${user.instance.base_url}`, + // TODO: Add these fields + limited: false, + moved: null, + noindex: false, + suspended: false, + discoverable: undefined, + mute_expires_at: undefined, + group: false, + role: undefined, + }; +}; + +/** + * Should only return local users + */ +export const userToLysand = (user: UserWithRelations): LysandUser => { + if (user.instanceId !== null) { + throw new Error("Cannot convert remote user to Lysand format"); } - // eslint-disable-next-line @typescript-eslint/require-await - async toAPI(isOwnAccount = false): Promise { - const follower_count = await Relationship.count({ - where: { - subject: { - id: this.id, - }, - following: true, + return { + id: user.id, + type: "User", + uri: user.uri, + bio: [ + { + content: user.note, + content_type: "text/html", }, - relations: ["subject"], - }); - - const following_count = await Relationship.count({ - where: { - owner: { - id: this.id, - }, - following: true, + { + content: htmlToText(user.note), + content_type: "text/plain", }, - relations: ["owner"], - }); - - const statusCount = await Status.count({ - where: { - account: { - id: this.id, - }, + ], + created_at: new Date(user.createdAt).toISOString(), + disliked: `${user.uri}/disliked`, + featured: `${user.uri}/featured`, + liked: `${user.uri}/liked`, + followers: `${user.uri}/followers`, + following: `${user.uri}/following`, + inbox: `${user.uri}/inbox`, + outbox: `${user.uri}/outbox`, + indexable: false, + username: user.username, + avatar: [ + { + content: getAvatarUrl(user, getConfig()) || "", + content_type: `image/${user.avatar.split(".")[1]}`, }, - relations: ["account"], - }); - - const config = getConfig(); - - return { - id: this.id, - username: this.username, - display_name: this.display_name, - note: this.note, - url: `${config.http.base_url}/users/${this.username}`, - avatar: this.getAvatarUrl(config) || config.defaults.avatar, - header: this.getHeaderUrl(config) || config.defaults.header, - locked: false, - created_at: new Date(this.created_at).toISOString(), - followers_count: follower_count, - following_count: following_count, - statuses_count: statusCount, - emojis: await Promise.all(this.emojis.map(emoji => emoji.toAPI())), - fields: [], - bot: false, - source: isOwnAccount ? this.source : undefined, - avatar_static: "", - header_static: "", - acct: - this.instance === null - ? `${this.username}` - : `${this.username}@${this.instance.base_url}`, - limited: false, - moved: null, - noindex: false, - suspended: false, - discoverable: undefined, - mute_expires_at: undefined, - group: false, - role: undefined, - }; - } - - /** - * Should only return local users - */ - toLysand(): LysandUser { - if (this.instance !== null) { - throw new Error("Cannot convert remote user to Lysand format"); - } - - return { - id: this.id, - type: "User", - uri: this.uri, - bio: [ + ], + header: [ + { + content: getHeaderUrl(user, getConfig()) || "", + content_type: `image/${user.header.split(".")[1]}`, + }, + ], + display_name: user.displayName, + fields: (user.source as any as APISource).fields.map(field => ({ + key: [ { - content: this.note, + content: field.name, content_type: "text/html", }, { - content: htmlToText(this.note), + content: htmlToText(field.name), content_type: "text/plain", }, ], - created_at: new Date(this.created_at).toISOString(), - disliked: `${this.uri}/disliked`, - featured: `${this.uri}/featured`, - liked: `${this.uri}/liked`, - followers: `${this.uri}/followers`, - following: `${this.uri}/following`, - inbox: `${this.uri}/inbox`, - outbox: `${this.uri}/outbox`, - indexable: false, - username: this.username, - avatar: [ + value: [ { - content: this.getAvatarUrl(getConfig()) || "", - content_type: `image/${this.avatar.split(".")[1]}`, + content: field.value, + content_type: "text/html", + }, + { + content: htmlToText(field.value), + content_type: "text/plain", }, ], - header: [ - { - content: this.getHeaderUrl(getConfig()) || "", - content_type: `image/${this.header.split(".")[1]}`, - }, - ], - display_name: this.display_name, - fields: this.source.fields.map(field => ({ - key: [ - { - content: field.name, - content_type: "text/html", - }, - { - content: htmlToText(field.name), - content_type: "text/plain", - }, - ], - value: [ - { - content: field.value, - content_type: "text/html", - }, - { - content: htmlToText(field.value), - content_type: "text/plain", - }, - ], - })), - public_key: { - actor: `${getConfig().http.base_url}/users/${this.id}`, - public_key: this.public_key, + })), + public_key: { + actor: `${getConfig().http.base_url}/users/${user.id}`, + public_key: user.publicKey, + }, + extensions: { + "org.lysand:custom_emojis": { + emojis: user.emojis.map(emoji => emojiToLysand(emoji)), }, - extensions: { - "org.lysand:custom_emojis": { - emojis: this.emojis.map(emoji => emoji.toLysand()), - }, - }, - }; - } -} + }, + }; +}; diff --git a/index.ts b/index.ts index 9e0ce494..db3b80d8 100644 --- a/index.ts +++ b/index.ts @@ -6,7 +6,7 @@ import { appendFile } from "fs/promises"; import { matches } from "ip-matching"; import "reflect-metadata"; import { AppDataSource } from "~database/datasource"; -import { User } from "~database/entities/User"; +import { AuthData, UserAction } from "~database/entities/User"; import { APIRouteMeta } from "~types/api"; const router = new Bun.FileSystemRouter({ @@ -60,7 +60,8 @@ Bun.serve({ meta: APIRouteMeta; default: ( req: Request, - matchedRoute: MatchedRoute + matchedRoute: MatchedRoute, + auth: AuthData ) => Response | Promise; } = await import(matchedRoute.filePath); @@ -78,11 +79,11 @@ Bun.serve({ // TODO: Check for ratelimits + const auth = await UserAction.getFromRequest(req); + // Check for authentication if required if (meta.auth.required) { - const { user } = await User.getFromRequest(req); - - if (!user) { + if (!auth.user) { return new Response(undefined, { status: 401, statusText: "Unauthorized", @@ -91,9 +92,7 @@ Bun.serve({ } else if ( (meta.auth.requiredOnMethods ?? []).includes(req.method as any) ) { - const { user } = await User.getFromRequest(req); - - if (!user) { + if (!auth.user) { return new Response(undefined, { status: 401, statusText: "Unauthorized", @@ -101,7 +100,7 @@ Bun.serve({ } } - return file.default(req, matchedRoute); + return file.default(req, matchedRoute, auth); } else { return new Response(undefined, { status: 404, diff --git a/package.json b/package.json index 4aca90e9..e854ea14 100644 --- a/package.json +++ b/package.json @@ -36,7 +36,8 @@ "start": "bun run index.ts" }, "trustedDependencies": [ - "sharp" + "sharp", + "@prisma/client" ], "devDependencies": { "@julr/unocss-preset-forms": "^0.0.5", @@ -61,6 +62,7 @@ }, "dependencies": { "@aws-sdk/client-s3": "^3.429.0", + "@prisma/client": "^5.5.2", "chalk": "^5.3.0", "html-to-text": "^9.0.5", "ip-matching": "^2.1.2", @@ -68,6 +70,7 @@ "jsonld": "^8.3.1", "marked": "^9.1.2", "pg": "^8.11.3", + "prisma": "^5.5.2", "reflect-metadata": "^0.1.13", "typeorm": "^0.3.17" } diff --git a/prisma/schema.prisma b/prisma/schema.prisma new file mode 100644 index 00000000..a3b2bc0d --- /dev/null +++ b/prisma/schema.prisma @@ -0,0 +1,166 @@ +generator client { + provider = "prisma-client-js" + previewFeatures = ["postgresqlExtensions"] +} + +datasource db { + provider = "postgresql" + url = env("DATABASE_URL") + extensions = [pg_uuidv7] +} + +model Application { + id String @id @default(dbgenerated("uuid_generate_v7()")) @db.Uuid + name String + website String? + vapid_key String? + client_id String + secret String + scopes String + redirect_uris String + statuses Status[] // One to many relation with Status + tokens Token[] // One to many relation with Token +} + +model Emoji { + id String @id @default(dbgenerated("uuid_generate_v7()")) @db.Uuid + shortcode String + url String + visible_in_picker Boolean + instance Instance? @relation(fields: [instanceId], references: [id], onDelete: Cascade) + instanceId String? @db.Uuid + alt String? + content_type String + users User[] // Many to many relation with User + statuses Status[] // Many to many relation with Status +} + +model Instance { + id String @id @default(dbgenerated("uuid_generate_v7()")) @db.Uuid + base_url String + name String + version String + logo Json + emojis Emoji[] // One to many relation with Emoji + statuses Status[] // One to many relation with Status + users User[] // One to many relation with User +} + +model Like { + id String @id @default(dbgenerated("uuid_generate_v7()")) @db.Uuid + liker User @relation("UserLiked", fields: [likerId], references: [id], onDelete: Cascade) + likerId String @db.Uuid + liked Status @relation("LikedToStatus", fields: [likedId], references: [id], onDelete: Cascade) + likedId String @db.Uuid + createdAt DateTime @default(now()) +} + +model LysandObject { + id String @id @default(dbgenerated("uuid_generate_v7()")) @db.Uuid + remote_id String @unique + type String + uri String @unique + created_at DateTime @default(now()) + author LysandObject? @relation("LysandObjectToAuthor", fields: [authorId], references: [id], onDelete: Cascade) + authorId String? @db.Uuid + extra_data Json + extensions Json + children LysandObject[] @relation("LysandObjectToAuthor") +} + +model Relationship { + id String @id @default(dbgenerated("uuid_generate_v7()")) @db.Uuid + owner User @relation("OwnerToRelationship", fields: [ownerId], references: [id], onDelete: Cascade) + ownerId String @db.Uuid + subject User @relation("SubjectToRelationship", fields: [subjectId], references: [id], onDelete: Cascade) + subjectId String @db.Uuid + following Boolean + showingReblogs Boolean + notifying Boolean + followedBy Boolean + blocking Boolean + blockedBy Boolean + muting Boolean + mutingNotifications Boolean + requested Boolean + domainBlocking Boolean + endorsed Boolean + languages String[] + note String + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt +} + +model Status { + id String @id @default(dbgenerated("uuid_generate_v7()")) @db.Uuid + uri String @unique + author User @relation("UserStatuses", fields: [authorId], references: [id], onDelete: Cascade) + authorId String @db.Uuid + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + reblog Status? @relation("StatusToStatus", fields: [reblogId], references: [id], onDelete: Cascade) + reblogId String? @db.Uuid + isReblog Boolean + content String @default("") + contentType String @default("text/plain") + visibility String + inReplyToPost Status? @relation("StatusToStatusReply", fields: [inReplyToPostId], references: [id], onDelete: SetNull) + inReplyToPostId String? @db.Uuid + quotingPost Status? @relation("StatusToStatusQuote", fields: [quotingPostId], references: [id], onDelete: SetNull) + quotingPostId String? @db.Uuid + instance Instance? @relation(fields: [instanceId], references: [id], onDelete: Cascade) + instanceId String? @db.Uuid + sensitive Boolean + spoilerText String @default("") + application Application? @relation(fields: [applicationId], references: [id], onDelete: SetNull) + applicationId String? @db.Uuid + emojis Emoji[] @relation + mentions User[] + likes Like[] @relation("LikedToStatus") + reblogs Status[] @relation("StatusToStatus") + replies Status[] @relation("StatusToStatusReply") + quotes Status[] @relation("StatusToStatusQuote") + pinnedBy User[] @relation("UserPinnedNotes") +} + +model Token { + id String @id @default(dbgenerated("uuid_generate_v7()")) @db.Uuid + token_type String + scope String + access_token String + code String + created_at DateTime @default(now()) + user User? @relation(fields: [userId], references: [id], onDelete: Cascade) + userId String? @db.Uuid + application Application? @relation(fields: [applicationId], references: [id], onDelete: Cascade) + applicationId String? @db.Uuid +} + +model User { + id String @id @default(dbgenerated("uuid_generate_v7()")) @db.Uuid + uri String @unique + username String @unique + displayName String + password String? // Nullable + email String? @unique // Nullable + note String @default("") + isAdmin Boolean @default(false) + endpoints Json? // Nullable + source Json + avatar String + header String + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + publicKey String + privateKey String? // Nullable + relationships Relationship[] @relation("OwnerToRelationship") // One to many relation with Relationship + relationshipSubjects Relationship[] @relation("SubjectToRelationship") // One to many relation with Relationship + instance Instance? @relation(fields: [instanceId], references: [id], onDelete: Cascade) // Many to one relation with Instance + instanceId String? @db.Uuid + pinnedNotes Status[] @relation("UserPinnedNotes") // Many to many relation with Status + emojis Emoji[] // Many to many relation with Emoji + statuses Status[] @relation("UserStatuses") // One to many relation with Status + tokens Token[] // One to many relation with Token + likes Like[] @relation("UserLiked") // One to many relation with Like + statusesMentioned Status[] // Many to many relation with Status +} diff --git a/server/api/.well-known/webfinger/index.ts b/server/api/.well-known/webfinger/index.ts index d9ecb5c6..15e28d1b 100644 --- a/server/api/.well-known/webfinger/index.ts +++ b/server/api/.well-known/webfinger/index.ts @@ -1,8 +1,8 @@ import { errorResponse, jsonResponse } from "@response"; import { MatchedRoute } from "bun"; -import { User, userRelations } from "~database/entities/User"; import { getConfig, getHost } from "@config"; import { applyConfig } from "@api"; +import { client } from "~database/datasource"; export const meta = applyConfig({ allowedMethods: ["GET"], @@ -34,9 +34,8 @@ export default async ( return errorResponse("User is a remote user", 404); } - const user = await User.findOne({ + const user = await client.user.findUnique({ where: { username: requestedUser.split("@")[0] }, - relations: userRelations }); if (!user) { diff --git a/server/api/api/v1/accounts/[id]/block.ts b/server/api/api/v1/accounts/[id]/block.ts index fe4cba5f..583ce511 100644 --- a/server/api/api/v1/accounts/[id]/block.ts +++ b/server/api/api/v1/accounts/[id]/block.ts @@ -1,7 +1,7 @@ import { errorResponse, jsonResponse } from "@response"; import { MatchedRoute } from "bun"; import { Relationship } from "~database/entities/Relationship"; -import { User, userRelations } from "~database/entities/User"; +import { UserAction, userRelations } from "~database/entities/User"; import { applyConfig } from "@api"; export const meta = applyConfig({ @@ -25,11 +25,11 @@ export default async ( ): Promise => { const id = matchedRoute.params.id; - const { user: self } = await User.getFromRequest(req); + const { user: self } = await UserAction.getFromRequest(req); if (!self) return errorResponse("Unauthorized", 401); - const user = await User.findOne({ + const user = await UserAction.findOne({ where: { id, }, diff --git a/server/api/api/v1/accounts/[id]/follow.ts b/server/api/api/v1/accounts/[id]/follow.ts index 3ac51e43..bc5f1e70 100644 --- a/server/api/api/v1/accounts/[id]/follow.ts +++ b/server/api/api/v1/accounts/[id]/follow.ts @@ -2,7 +2,7 @@ import { parseRequest } from "@request"; import { errorResponse, jsonResponse } from "@response"; import { MatchedRoute } from "bun"; import { Relationship } from "~database/entities/Relationship"; -import { User, userRelations } from "~database/entities/User"; +import { UserAction, userRelations } from "~database/entities/User"; import { applyConfig } from "@api"; export const meta = applyConfig({ @@ -26,7 +26,7 @@ export default async ( ): Promise => { const id = matchedRoute.params.id; - const { user: self } = await User.getFromRequest(req); + const { user: self } = await UserAction.getFromRequest(req); if (!self) return errorResponse("Unauthorized", 401); @@ -36,7 +36,7 @@ export default async ( languages?: string[]; }>(req); - const user = await User.findOne({ + const user = await UserAction.findOne({ where: { id, }, diff --git a/server/api/api/v1/accounts/[id]/index.ts b/server/api/api/v1/accounts/[id]/index.ts index 6dc4e479..f9d52e20 100644 --- a/server/api/api/v1/accounts/[id]/index.ts +++ b/server/api/api/v1/accounts/[id]/index.ts @@ -1,6 +1,6 @@ import { errorResponse, jsonResponse } from "@response"; import { MatchedRoute } from "bun"; -import { User, userRelations } from "~database/entities/User"; +import { UserAction, userRelations } from "~database/entities/User"; import { applyConfig } from "@api"; export const meta = applyConfig({ @@ -24,11 +24,11 @@ export default async ( ): Promise => { const id = matchedRoute.params.id; - const { user } = await User.getFromRequest(req); + const { user } = await UserAction.getFromRequest(req); - let foundUser: User | null; + let foundUser: UserAction | null; try { - foundUser = await User.findOne({ + foundUser = await UserAction.findOne({ where: { id, }, diff --git a/server/api/api/v1/accounts/[id]/mute.ts b/server/api/api/v1/accounts/[id]/mute.ts index 39a99dbe..eabacd52 100644 --- a/server/api/api/v1/accounts/[id]/mute.ts +++ b/server/api/api/v1/accounts/[id]/mute.ts @@ -2,7 +2,7 @@ import { parseRequest } from "@request"; import { errorResponse, jsonResponse } from "@response"; import { MatchedRoute } from "bun"; import { Relationship } from "~database/entities/Relationship"; -import { User, userRelations } from "~database/entities/User"; +import { UserAction, userRelations } from "~database/entities/User"; import { applyConfig } from "@api"; export const meta = applyConfig({ @@ -26,7 +26,7 @@ export default async ( ): Promise => { const id = matchedRoute.params.id; - const { user: self } = await User.getFromRequest(req); + const { user: self } = await UserAction.getFromRequest(req); if (!self) return errorResponse("Unauthorized", 401); @@ -36,7 +36,7 @@ export default async ( duration: number; }>(req); - const user = await User.findOne({ + const user = await UserAction.findOne({ where: { id, }, diff --git a/server/api/api/v1/accounts/[id]/note.ts b/server/api/api/v1/accounts/[id]/note.ts index 76b1fdea..54f8378d 100644 --- a/server/api/api/v1/accounts/[id]/note.ts +++ b/server/api/api/v1/accounts/[id]/note.ts @@ -2,7 +2,7 @@ import { parseRequest } from "@request"; import { errorResponse, jsonResponse } from "@response"; import { MatchedRoute } from "bun"; import { Relationship } from "~database/entities/Relationship"; -import { User, userRelations } from "~database/entities/User"; +import { UserAction, userRelations } from "~database/entities/User"; import { applyConfig } from "@api"; export const meta = applyConfig({ @@ -26,7 +26,7 @@ export default async ( ): Promise => { const id = matchedRoute.params.id; - const { user: self } = await User.getFromRequest(req); + const { user: self } = await UserAction.getFromRequest(req); if (!self) return errorResponse("Unauthorized", 401); @@ -34,7 +34,7 @@ export default async ( comment: string; }>(req); - const user = await User.findOne({ + const user = await UserAction.findOne({ where: { id, }, diff --git a/server/api/api/v1/accounts/[id]/pin.ts b/server/api/api/v1/accounts/[id]/pin.ts index d894129d..82ee42cb 100644 --- a/server/api/api/v1/accounts/[id]/pin.ts +++ b/server/api/api/v1/accounts/[id]/pin.ts @@ -1,7 +1,7 @@ import { errorResponse, jsonResponse } from "@response"; import { MatchedRoute } from "bun"; import { Relationship } from "~database/entities/Relationship"; -import { User, userRelations } from "~database/entities/User"; +import { UserAction, userRelations } from "~database/entities/User"; import { applyConfig } from "@api"; export const meta = applyConfig({ @@ -25,11 +25,11 @@ export default async ( ): Promise => { const id = matchedRoute.params.id; - const { user: self } = await User.getFromRequest(req); + const { user: self } = await UserAction.getFromRequest(req); if (!self) return errorResponse("Unauthorized", 401); - const user = await User.findOne({ + const user = await UserAction.findOne({ where: { id, }, diff --git a/server/api/api/v1/accounts/[id]/remove_from_followers.ts b/server/api/api/v1/accounts/[id]/remove_from_followers.ts index d2318dca..9bf94a9c 100644 --- a/server/api/api/v1/accounts/[id]/remove_from_followers.ts +++ b/server/api/api/v1/accounts/[id]/remove_from_followers.ts @@ -1,7 +1,7 @@ import { errorResponse, jsonResponse } from "@response"; import { MatchedRoute } from "bun"; import { Relationship } from "~database/entities/Relationship"; -import { User, userRelations } from "~database/entities/User"; +import { UserAction, userRelations } from "~database/entities/User"; import { applyConfig } from "@api"; export const meta = applyConfig({ @@ -25,11 +25,11 @@ export default async ( ): Promise => { const id = matchedRoute.params.id; - const { user: self } = await User.getFromRequest(req); + const { user: self } = await UserAction.getFromRequest(req); if (!self) return errorResponse("Unauthorized", 401); - const user = await User.findOne({ + const user = await UserAction.findOne({ where: { id, }, diff --git a/server/api/api/v1/accounts/[id]/statuses.ts b/server/api/api/v1/accounts/[id]/statuses.ts index 120cfc43..8b663b5a 100644 --- a/server/api/api/v1/accounts/[id]/statuses.ts +++ b/server/api/api/v1/accounts/[id]/statuses.ts @@ -2,7 +2,7 @@ import { errorResponse, jsonResponse } from "@response"; import { MatchedRoute } from "bun"; import { Status, statusAndUserRelations } from "~database/entities/Status"; -import { User, userRelations } from "~database/entities/User"; +import { UserAction, userRelations } from "~database/entities/User"; import { applyConfig } from "@api"; import { FindManyOptions } from "typeorm"; @@ -47,7 +47,7 @@ export default async ( tagged?: string; } = matchedRoute.query; - const user = await User.findOne({ + const user = await UserAction.findOne({ where: { id, }, diff --git a/server/api/api/v1/accounts/[id]/unblock.ts b/server/api/api/v1/accounts/[id]/unblock.ts index b03d2121..d28f16a4 100644 --- a/server/api/api/v1/accounts/[id]/unblock.ts +++ b/server/api/api/v1/accounts/[id]/unblock.ts @@ -1,7 +1,7 @@ import { errorResponse, jsonResponse } from "@response"; import { MatchedRoute } from "bun"; import { Relationship } from "~database/entities/Relationship"; -import { User, userRelations } from "~database/entities/User"; +import { UserAction, userRelations } from "~database/entities/User"; import { applyConfig } from "@api"; export const meta = applyConfig({ @@ -25,11 +25,11 @@ export default async ( ): Promise => { const id = matchedRoute.params.id; - const { user: self } = await User.getFromRequest(req); + const { user: self } = await UserAction.getFromRequest(req); if (!self) return errorResponse("Unauthorized", 401); - const user = await User.findOne({ + const user = await UserAction.findOne({ where: { id, }, diff --git a/server/api/api/v1/accounts/[id]/unfollow.ts b/server/api/api/v1/accounts/[id]/unfollow.ts index 60ac83cc..0d189824 100644 --- a/server/api/api/v1/accounts/[id]/unfollow.ts +++ b/server/api/api/v1/accounts/[id]/unfollow.ts @@ -1,7 +1,7 @@ import { errorResponse, jsonResponse } from "@response"; import { MatchedRoute } from "bun"; import { Relationship } from "~database/entities/Relationship"; -import { User, userRelations } from "~database/entities/User"; +import { UserAction, userRelations } from "~database/entities/User"; import { applyConfig } from "@api"; export const meta = applyConfig({ @@ -25,11 +25,11 @@ export default async ( ): Promise => { const id = matchedRoute.params.id; - const { user: self } = await User.getFromRequest(req); + const { user: self } = await UserAction.getFromRequest(req); if (!self) return errorResponse("Unauthorized", 401); - const user = await User.findOne({ + const user = await UserAction.findOne({ where: { id, }, diff --git a/server/api/api/v1/accounts/[id]/unmute.ts b/server/api/api/v1/accounts/[id]/unmute.ts index 2a84bc7a..7a79bae0 100644 --- a/server/api/api/v1/accounts/[id]/unmute.ts +++ b/server/api/api/v1/accounts/[id]/unmute.ts @@ -1,7 +1,7 @@ import { errorResponse, jsonResponse } from "@response"; import { MatchedRoute } from "bun"; import { Relationship } from "~database/entities/Relationship"; -import { User, userRelations } from "~database/entities/User"; +import { UserAction, userRelations } from "~database/entities/User"; import { applyConfig } from "@api"; export const meta = applyConfig({ @@ -25,11 +25,11 @@ export default async ( ): Promise => { const id = matchedRoute.params.id; - const { user: self } = await User.getFromRequest(req); + const { user: self } = await UserAction.getFromRequest(req); if (!self) return errorResponse("Unauthorized", 401); - const user = await User.findOne({ + const user = await UserAction.findOne({ where: { id, }, diff --git a/server/api/api/v1/accounts/[id]/unpin.ts b/server/api/api/v1/accounts/[id]/unpin.ts index f51d0e0f..1462f145 100644 --- a/server/api/api/v1/accounts/[id]/unpin.ts +++ b/server/api/api/v1/accounts/[id]/unpin.ts @@ -1,7 +1,7 @@ import { errorResponse, jsonResponse } from "@response"; import { MatchedRoute } from "bun"; import { Relationship } from "~database/entities/Relationship"; -import { User, userRelations } from "~database/entities/User"; +import { UserAction, userRelations } from "~database/entities/User"; import { applyConfig } from "@api"; export const meta = applyConfig({ @@ -25,11 +25,11 @@ export default async ( ): Promise => { const id = matchedRoute.params.id; - const { user: self } = await User.getFromRequest(req); + const { user: self } = await UserAction.getFromRequest(req); if (!self) return errorResponse("Unauthorized", 401); - const user = await User.findOne({ + const user = await UserAction.findOne({ where: { id, }, diff --git a/server/api/api/v1/accounts/familiar_followers/index.ts b/server/api/api/v1/accounts/familiar_followers/index.ts index 2961fc47..c012d85f 100644 --- a/server/api/api/v1/accounts/familiar_followers/index.ts +++ b/server/api/api/v1/accounts/familiar_followers/index.ts @@ -1,6 +1,6 @@ import { parseRequest } from "@request"; import { errorResponse, jsonResponse } from "@response"; -import { User } from "~database/entities/User"; +import { UserAction } from "~database/entities/User"; import { APIAccount } from "~types/entities/account"; import { applyConfig } from "@api"; @@ -20,7 +20,7 @@ export const meta = applyConfig({ * Find familiar followers (followers of a user that you also follow) */ export default async (req: Request): Promise => { - const { user: self } = await User.getFromRequest(req); + const { user: self } = await UserAction.getFromRequest(req); if (!self) return errorResponse("Unauthorized", 401); @@ -39,7 +39,7 @@ export default async (req: Request): Promise => { // Find followers of user that you also follow // Get user - const user = await User.findOne({ + const user = await UserAction.findOne({ where: { id }, relations: { relationships: { diff --git a/server/api/api/v1/accounts/index.ts b/server/api/api/v1/accounts/index.ts index ce81d0ab..69d997cf 100644 --- a/server/api/api/v1/accounts/index.ts +++ b/server/api/api/v1/accounts/index.ts @@ -2,7 +2,7 @@ import { getConfig } from "@config"; import { parseRequest } from "@request"; import { jsonResponse } from "@response"; import { tempmailDomains } from "@tempmail"; -import { User } from "~database/entities/User"; +import { UserAction } from "~database/entities/User"; import { applyConfig } from "@api"; export const meta = applyConfig({ @@ -115,7 +115,7 @@ export default async (req: Request): Promise => { }); // Check if username is taken - if (await User.findOne({ where: { username: body.username } })) + if (await UserAction.findOne({ where: { username: body.username } })) errors.details.username.push({ error: "ERR_TAKEN", description: `is already taken`, @@ -170,7 +170,7 @@ export default async (req: Request): Promise => { // TODO: Check if locale is valid - await User.createNewLocal({ + await UserAction.createNewLocal({ username: body.username ?? "", password: body.password ?? "", email: body.email ?? "", diff --git a/server/api/api/v1/accounts/relationships/index.ts b/server/api/api/v1/accounts/relationships/index.ts index 6e55e815..b9dde030 100644 --- a/server/api/api/v1/accounts/relationships/index.ts +++ b/server/api/api/v1/accounts/relationships/index.ts @@ -1,7 +1,7 @@ import { parseRequest } from "@request"; import { errorResponse, jsonResponse } from "@response"; import { Relationship } from "~database/entities/Relationship"; -import { User } from "~database/entities/User"; +import { UserAction } from "~database/entities/User"; import { applyConfig } from "@api"; export const meta = applyConfig({ @@ -20,7 +20,7 @@ export const meta = applyConfig({ * Find relationships */ export default async (req: Request): Promise => { - const { user: self } = await User.getFromRequest(req); + const { user: self } = await UserAction.getFromRequest(req); if (!self) return errorResponse("Unauthorized", 401); @@ -38,7 +38,7 @@ export default async (req: Request): Promise => { const relationships = ( await Promise.all( ids.map(async id => { - const user = await User.findOneBy({ id }); + const user = await UserAction.findOneBy({ id }); if (!user) return null; let relationship = await self.getRelationshipToOtherUser(user); diff --git a/server/api/api/v1/accounts/update_credentials/index.ts b/server/api/api/v1/accounts/update_credentials/index.ts index 105c6b07..8ac804c6 100644 --- a/server/api/api/v1/accounts/update_credentials/index.ts +++ b/server/api/api/v1/accounts/update_credentials/index.ts @@ -1,12 +1,12 @@ import { getConfig } from "@config"; import { parseRequest } from "@request"; import { errorResponse, jsonResponse } from "@response"; -import { User } from "~database/entities/User"; +import { UserAction } from "~database/entities/User"; import { applyConfig } from "@api"; import { sanitize } from "isomorphic-dompurify"; import { sanitizeHtml } from "@sanitization"; import { uploadFile } from "~classes/media"; -import { Emoji } from "~database/entities/Emoji"; +import { EmojiAction } from "~database/entities/Emoji"; export const meta = applyConfig({ allowedMethods: ["PATCH"], @@ -24,7 +24,7 @@ export const meta = applyConfig({ * Patches a user */ export default async (req: Request): Promise => { - const { user } = await User.getFromRequest(req); + const { user } = await UserAction.getFromRequest(req); if (!user) return errorResponse("Unauthorized", 401); @@ -202,8 +202,8 @@ export default async (req: Request): Promise => { // Parse emojis - const displaynameEmojis = await Emoji.parseEmojis(sanitizedDisplayName); - const noteEmojis = await Emoji.parseEmojis(sanitizedNote); + const displaynameEmojis = await EmojiAction.parseEmojis(sanitizedDisplayName); + const noteEmojis = await EmojiAction.parseEmojis(sanitizedNote); user.emojis = [...displaynameEmojis, ...noteEmojis]; diff --git a/server/api/api/v1/accounts/verify_credentials/index.ts b/server/api/api/v1/accounts/verify_credentials/index.ts index 1c82b064..102d49fd 100644 --- a/server/api/api/v1/accounts/verify_credentials/index.ts +++ b/server/api/api/v1/accounts/verify_credentials/index.ts @@ -1,5 +1,5 @@ import { errorResponse, jsonResponse } from "@response"; -import { User } from "~database/entities/User"; +import { UserAction } from "~database/entities/User"; import { applyConfig } from "@api"; export const meta = applyConfig({ @@ -17,7 +17,7 @@ export const meta = applyConfig({ export default async (req: Request): Promise => { // TODO: Add checks for disabled or not email verified accounts - const { user } = await User.getFromRequest(req); + const { user } = await UserAction.getFromRequest(req); if (!user) return errorResponse("Unauthorized", 401); diff --git a/server/api/api/v1/apps/index.ts b/server/api/api/v1/apps/index.ts index 62ee7206..c1b06dec 100644 --- a/server/api/api/v1/apps/index.ts +++ b/server/api/api/v1/apps/index.ts @@ -2,7 +2,7 @@ import { applyConfig } from "@api"; import { parseRequest } from "@request"; import { errorResponse, jsonResponse } from "@response"; import { randomBytes } from "crypto"; -import { Application } from "~database/entities/Application"; +import { ApplicationAction } from "~database/entities/Application"; export const meta = applyConfig({ allowedMethods: ["POST"], @@ -27,7 +27,7 @@ export default async (req: Request): Promise => { website: string; }>(req); - const application = new Application(); + const application = new ApplicationAction(); application.name = client_name || ""; diff --git a/server/api/api/v1/apps/verify_credentials/index.ts b/server/api/api/v1/apps/verify_credentials/index.ts index 14cf5437..bd606cfb 100644 --- a/server/api/api/v1/apps/verify_credentials/index.ts +++ b/server/api/api/v1/apps/verify_credentials/index.ts @@ -1,7 +1,7 @@ import { applyConfig } from "@api"; import { errorResponse, jsonResponse } from "@response"; -import { Application } from "~database/entities/Application"; -import { User } from "~database/entities/User"; +import { ApplicationAction } from "~database/entities/Application"; +import { UserAction } from "~database/entities/User"; export const meta = applyConfig({ allowedMethods: ["GET"], @@ -19,8 +19,8 @@ export const meta = applyConfig({ * Returns OAuth2 credentials */ export default async (req: Request): Promise => { - const { user, token } = await User.getFromRequest(req); - const application = await Application.getFromToken(token); + const { user, token } = await UserAction.getFromRequest(req); + const application = await ApplicationAction.getFromToken(token); if (!user) return errorResponse("Unauthorized", 401); if (!application) return errorResponse("Unauthorized", 401); diff --git a/server/api/api/v1/custom_emojis/index.ts b/server/api/api/v1/custom_emojis/index.ts index 2de2a801..b19e24dc 100644 --- a/server/api/api/v1/custom_emojis/index.ts +++ b/server/api/api/v1/custom_emojis/index.ts @@ -1,7 +1,7 @@ import { applyConfig } from "@api"; import { jsonResponse } from "@response"; import { IsNull } from "typeorm"; -import { Emoji } from "~database/entities/Emoji"; +import { EmojiAction } from "~database/entities/Emoji"; export const meta = applyConfig({ allowedMethods: ["GET"], @@ -20,7 +20,7 @@ export const meta = applyConfig({ */ // eslint-disable-next-line @typescript-eslint/require-await export default async (): Promise => { - const emojis = await Emoji.findBy({ + const emojis = await EmojiAction.findBy({ instance: IsNull(), }); diff --git a/server/api/api/v1/instance/index.ts b/server/api/api/v1/instance/index.ts index a1c20de8..75237801 100644 --- a/server/api/api/v1/instance/index.ts +++ b/server/api/api/v1/instance/index.ts @@ -2,7 +2,7 @@ import { applyConfig } from "@api"; import { getConfig } from "@config"; import { jsonResponse } from "@response"; import { Status } from "~database/entities/Status"; -import { User } from "~database/entities/User"; +import { UserAction } from "~database/entities/User"; export const meta = applyConfig({ allowedMethods: ["GET"], @@ -24,7 +24,7 @@ export default async (): Promise => { const config = getConfig(); const statusCount = await Status.count(); - const userCount = await User.count(); + const userCount = await UserAction.count(); // TODO: fill in more values return jsonResponse({ diff --git a/server/api/api/v1/statuses/[id]/context.ts b/server/api/api/v1/statuses/[id]/context.ts index 8cfd8266..f62d30b0 100644 --- a/server/api/api/v1/statuses/[id]/context.ts +++ b/server/api/api/v1/statuses/[id]/context.ts @@ -2,7 +2,7 @@ import { applyConfig } from "@api"; import { errorResponse, jsonResponse } from "@response"; import { MatchedRoute } from "bun"; import { Status, statusAndUserRelations } from "~database/entities/Status"; -import { User } from "~database/entities/User"; +import { UserAction } from "~database/entities/User"; import { APIRouteMeta } from "~types/api"; export const meta: APIRouteMeta = applyConfig({ @@ -28,7 +28,7 @@ export default async ( // User token + read:statuses for up to 4,096 ancestors, 4,096 descendants, unlimited depth, and private statuses. const id = matchedRoute.params.id; - const { user } = await User.getFromRequest(req); + const { user } = await UserAction.getFromRequest(req); let foundStatus: Status | null; try { diff --git a/server/api/api/v1/statuses/[id]/favourite.ts b/server/api/api/v1/statuses/[id]/favourite.ts index c4d16847..348a57c1 100644 --- a/server/api/api/v1/statuses/[id]/favourite.ts +++ b/server/api/api/v1/statuses/[id]/favourite.ts @@ -4,7 +4,7 @@ import { errorResponse, jsonResponse } from "@response"; import { MatchedRoute } from "bun"; import { Like } from "~database/entities/Like"; import { Status, statusAndUserRelations } from "~database/entities/Status"; -import { User, userRelations } from "~database/entities/User"; +import { UserAction, userRelations } from "~database/entities/User"; import { APIRouteMeta } from "~types/api"; export const meta: APIRouteMeta = applyConfig({ @@ -28,7 +28,7 @@ export default async ( ): Promise => { const id = matchedRoute.params.id; - const { user } = await User.getFromRequest(req); + const { user } = await UserAction.getFromRequest(req); if (!user) return errorResponse("Unauthorized", 401); diff --git a/server/api/api/v1/statuses/[id]/favourited_by.ts b/server/api/api/v1/statuses/[id]/favourited_by.ts index b6dd8187..c64b5de9 100644 --- a/server/api/api/v1/statuses/[id]/favourited_by.ts +++ b/server/api/api/v1/statuses/[id]/favourited_by.ts @@ -6,7 +6,7 @@ import { MatchedRoute } from "bun"; import { FindManyOptions } from "typeorm"; import { Like } from "~database/entities/Like"; import { Status, statusAndUserRelations } from "~database/entities/Status"; -import { User, userRelations } from "~database/entities/User"; +import { UserAction, userRelations } from "~database/entities/User"; import { APIRouteMeta } from "~types/api"; export const meta: APIRouteMeta = applyConfig({ @@ -30,7 +30,7 @@ export default async ( ): Promise => { const id = matchedRoute.params.id; - const { user } = await User.getFromRequest(req); + const { user } = await UserAction.getFromRequest(req); let foundStatus: Status | null; try { diff --git a/server/api/api/v1/statuses/[id]/index.ts b/server/api/api/v1/statuses/[id]/index.ts index c862da7c..ae0058e4 100644 --- a/server/api/api/v1/statuses/[id]/index.ts +++ b/server/api/api/v1/statuses/[id]/index.ts @@ -2,7 +2,7 @@ import { applyConfig } from "@api"; import { errorResponse, jsonResponse } from "@response"; import { MatchedRoute } from "bun"; import { Status, statusAndUserRelations } from "~database/entities/Status"; -import { User } from "~database/entities/User"; +import { UserAction } from "~database/entities/User"; import { APIRouteMeta } from "~types/api"; export const meta: APIRouteMeta = applyConfig({ @@ -27,7 +27,7 @@ export default async ( ): Promise => { const id = matchedRoute.params.id; - const { user } = await User.getFromRequest(req); + const { user } = await UserAction.getFromRequest(req); let foundStatus: Status | null; try { diff --git a/server/api/api/v1/statuses/[id]/reblogged_by.ts b/server/api/api/v1/statuses/[id]/reblogged_by.ts index d3593ba6..cf30e4ff 100644 --- a/server/api/api/v1/statuses/[id]/reblogged_by.ts +++ b/server/api/api/v1/statuses/[id]/reblogged_by.ts @@ -5,7 +5,7 @@ import { errorResponse, jsonResponse } from "@response"; import { MatchedRoute } from "bun"; import { FindManyOptions } from "typeorm"; import { Status, statusAndUserRelations } from "~database/entities/Status"; -import { User } from "~database/entities/User"; +import { UserAction } from "~database/entities/User"; import { APIRouteMeta } from "~types/api"; export const meta: APIRouteMeta = applyConfig({ @@ -29,7 +29,7 @@ export default async ( ): Promise => { const id = matchedRoute.params.id; - const { user } = await User.getFromRequest(req); + const { user } = await UserAction.getFromRequest(req); let foundStatus: Status | null; try { diff --git a/server/api/api/v1/statuses/[id]/unfavourite.ts b/server/api/api/v1/statuses/[id]/unfavourite.ts index 52fdd85d..7d349d94 100644 --- a/server/api/api/v1/statuses/[id]/unfavourite.ts +++ b/server/api/api/v1/statuses/[id]/unfavourite.ts @@ -4,7 +4,7 @@ import { errorResponse, jsonResponse } from "@response"; import { MatchedRoute } from "bun"; import { Like } from "~database/entities/Like"; import { Status, statusAndUserRelations } from "~database/entities/Status"; -import { User } from "~database/entities/User"; +import { UserAction } from "~database/entities/User"; import { APIRouteMeta } from "~types/api"; export const meta: APIRouteMeta = applyConfig({ @@ -28,7 +28,7 @@ export default async ( ): Promise => { const id = matchedRoute.params.id; - const { user } = await User.getFromRequest(req); + const { user } = await UserAction.getFromRequest(req); if (!user) return errorResponse("Unauthorized", 401); diff --git a/server/api/api/v1/statuses/index.ts b/server/api/api/v1/statuses/index.ts index d84623b5..0c9865d6 100644 --- a/server/api/api/v1/statuses/index.ts +++ b/server/api/api/v1/statuses/index.ts @@ -6,10 +6,11 @@ import { getConfig } from "@config"; import { parseRequest } from "@request"; import { errorResponse, jsonResponse } from "@response"; import { sanitizeHtml } from "@sanitization"; +import { MatchedRoute } from "bun"; import { parse } from "marked"; -import { Application } from "~database/entities/Application"; +import { ApplicationAction } from "~database/entities/Application"; import { Status, statusRelations } from "~database/entities/Status"; -import { User } from "~database/entities/User"; +import { AuthData, UserAction } from "~database/entities/User"; import { APIRouteMeta } from "~types/api"; export const meta: APIRouteMeta = applyConfig({ @@ -27,9 +28,13 @@ export const meta: APIRouteMeta = applyConfig({ /** * Post new status */ -export default async (req: Request): Promise => { - const { user, token } = await User.getFromRequest(req); - const application = await Application.getFromToken(token); +export default async ( + req: Request, + matchedRoute: MatchedRoute, + authData: AuthData +): Promise => { + const { user, token } = authData; + const application = await ApplicationAction.getFromToken(token); if (!user) return errorResponse("Unauthorized", 401); @@ -122,7 +127,7 @@ export default async (req: Request): Promise => { // Get reply account and status if exists let replyStatus: Status | null = null; - let replyUser: User | null = null; + let replyUser: UserAction | null = null; if (in_reply_to_id) { replyStatus = await Status.findOne({ diff --git a/server/api/api/v1/timelines/home.ts b/server/api/api/v1/timelines/home.ts index dc389e95..6a571189 100644 --- a/server/api/api/v1/timelines/home.ts +++ b/server/api/api/v1/timelines/home.ts @@ -2,9 +2,10 @@ import { applyConfig } from "@api"; import { parseRequest } from "@request"; import { errorResponse, jsonResponse } from "@response"; +import { MatchedRoute } from "bun"; import { FindManyOptions } from "typeorm"; import { Status, statusAndUserRelations } from "~database/entities/Status"; -import { User } from "~database/entities/User"; +import { AuthData } from "~database/entities/User"; import { APIRouteMeta } from "~types/api"; export const meta: APIRouteMeta = applyConfig({ @@ -22,7 +23,11 @@ export const meta: APIRouteMeta = applyConfig({ /** * Fetch home timeline statuses */ -export default async (req: Request): Promise => { +export default async ( + req: Request, + matchedRoute: MatchedRoute, + authData: AuthData +): Promise => { const { limit = 20, max_id, @@ -35,7 +40,7 @@ export default async (req: Request): Promise => { limit?: number; }>(req); - const { user } = await User.getFromRequest(req); + const { user } = authData; if (limit < 1 || limit > 40) { return errorResponse("Limit must be between 1 and 40", 400); diff --git a/server/api/api/v1/timelines/public.ts b/server/api/api/v1/timelines/public.ts index fb5a4e11..ad69380a 100644 --- a/server/api/api/v1/timelines/public.ts +++ b/server/api/api/v1/timelines/public.ts @@ -1,4 +1,3 @@ -/* eslint-disable @typescript-eslint/no-unsafe-member-access */ import { applyConfig } from "@api"; import { parseRequest } from "@request"; import { errorResponse, jsonResponse } from "@response"; @@ -18,9 +17,29 @@ export const meta: APIRouteMeta = applyConfig({ }, }); -/** - * Fetch public timeline statuses - */ +const updateQuery = async ( + id: string | undefined, + operator: string, + query: FindManyOptions +) => { + if (!id) return query; + const post = await Status.findOneBy({ id }); + if (post) { + query = { + ...query, + where: { + ...query.where, + created_at: { + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + ...(query.where as any)?.created_at, + [operator]: post.created_at, + }, + }, + }; + } + return query; +}; + export default async (req: Request): Promise => { const { local, @@ -59,53 +78,9 @@ export default async (req: Request): Promise => { relations: statusAndUserRelations, }; - if (max_id) { - const maxPost = await Status.findOneBy({ id: max_id }); - if (maxPost) { - query = { - ...query, - where: { - ...query.where, - created_at: { - ...(query.where as any)?.created_at, - $lt: maxPost.created_at, - }, - }, - }; - } - } - - if (min_id) { - const minPost = await Status.findOneBy({ id: min_id }); - if (minPost) { - query = { - ...query, - where: { - ...query.where, - created_at: { - ...(query.where as any)?.created_at, - $gt: minPost.created_at, - }, - }, - }; - } - } - - if (since_id) { - const sincePost = await Status.findOneBy({ id: since_id }); - if (sincePost) { - query = { - ...query, - where: { - ...query.where, - created_at: { - ...(query.where as any)?.created_at, - $gte: sincePost.created_at, - }, - }, - }; - } - } + query = await updateQuery(max_id, "$lt", query); + query = await updateQuery(min_id, "$gt", query); + query = await updateQuery(since_id, "$gte", query); if (only_media) { // TODO: add diff --git a/server/api/auth/login/index.ts b/server/api/auth/login/index.ts index 2f631e66..3fe279e7 100644 --- a/server/api/auth/login/index.ts +++ b/server/api/auth/login/index.ts @@ -2,9 +2,9 @@ import { applyConfig } from "@api"; import { errorResponse } from "@response"; import { MatchedRoute } from "bun"; import { randomBytes } from "crypto"; -import { Application } from "~database/entities/Application"; +import { ApplicationAction } from "~database/entities/Application"; import { Token } from "~database/entities/Token"; -import { User, userRelations } from "~database/entities/User"; +import { UserAction, userRelations } from "~database/entities/User"; import { APIRouteMeta } from "~types/api"; export const meta: APIRouteMeta = applyConfig({ @@ -45,7 +45,7 @@ export default async ( return errorResponse("Missing username or password", 400); // Get user - const user = await User.findOne({ + const user = await UserAction.findOne({ where: { email, }, @@ -56,7 +56,7 @@ export default async ( return errorResponse("Invalid username or password", 401); // Get application - const application = await Application.findOneBy({ + const application = await ApplicationAction.findOneBy({ client_id, }); diff --git a/server/api/users/[uuid]/inbox/index.ts b/server/api/users/[uuid]/inbox/index.ts index 9701b399..1a143b7c 100644 --- a/server/api/users/[uuid]/inbox/index.ts +++ b/server/api/users/[uuid]/inbox/index.ts @@ -5,10 +5,10 @@ import { getConfig } from "@config"; import { getBestContentType } from "@content_types"; import { errorResponse, jsonResponse } from "@response"; import { MatchedRoute } from "bun"; -import { Emoji } from "~database/entities/Emoji"; +import { EmojiAction } from "~database/entities/Emoji"; import { LysandObject } from "~database/entities/Object"; import { Status } from "~database/entities/Status"; -import { User, userRelations } from "~database/entities/User"; +import { UserAction, userRelations } from "~database/entities/User"; import { ContentFormat, LysandAction, @@ -61,7 +61,7 @@ export default async ( // Process request body const body = (await req.json()) as LysandPublication | LysandAction; - const author = await User.findOne({ + const author = await UserAction.findOne({ where: { uri: body.author, }, @@ -145,7 +145,7 @@ export default async ( const content = getBestContentType(body.contents); - const emojis = await Emoji.parseEmojis(content?.content || ""); + const emojis = await EmojiAction.parseEmojis(content?.content || ""); const newStatus = await Status.createNew({ account: author, @@ -158,7 +158,7 @@ export default async ( sensitive: body.is_sensitive, uri: body.uri, emojis: emojis, - mentions: await User.parseMentions(body.mentions), + mentions: await UserAction.parseMentions(body.mentions), }); // If there is a reply, fetch all the reply parents and add them to the database @@ -187,7 +187,7 @@ export default async ( const content = getBestContentType(patch.contents); - const emojis = await Emoji.parseEmojis(content?.content || ""); + const emojis = await EmojiAction.parseEmojis(content?.content || ""); const status = await Status.findOneBy({ id: patch.patched_id, diff --git a/server/api/users/[uuid]/index.ts b/server/api/users/[uuid]/index.ts index 8c6bc1c9..f80eca54 100644 --- a/server/api/users/[uuid]/index.ts +++ b/server/api/users/[uuid]/index.ts @@ -4,7 +4,7 @@ import { applyConfig } from "@api"; import { getConfig } from "@config"; import { errorResponse, jsonResponse } from "@response"; import { MatchedRoute } from "bun"; -import { User, userRelations } from "~database/entities/User"; +import { UserAction, userRelations } from "~database/entities/User"; export const meta = applyConfig({ allowedMethods: ["POST"], @@ -29,7 +29,7 @@ export default async ( const config = getConfig(); - const user = await User.findOne({ + const user = await UserAction.findOne({ where: { id: uuid, }, diff --git a/tests/api.test.ts b/tests/api.test.ts index 738bf971..a7f939f8 100644 --- a/tests/api.test.ts +++ b/tests/api.test.ts @@ -3,25 +3,25 @@ import { getConfig } from "@config"; import { afterAll, beforeAll, describe, expect, test } from "bun:test"; import { AppDataSource } from "~database/datasource"; -import { Application } from "~database/entities/Application"; -import { Emoji } from "~database/entities/Emoji"; +import { ApplicationAction } from "~database/entities/Application"; +import { EmojiAction } from "~database/entities/Emoji"; import { Token, TokenType } from "~database/entities/Token"; -import { User } from "~database/entities/User"; +import { UserAction } from "~database/entities/User"; import { APIEmoji } from "~types/entities/emoji"; import { APIInstance } from "~types/entities/instance"; const config = getConfig(); let token: Token; -let user: User; -let user2: User; +let user: UserAction; +let user2: UserAction; describe("API Tests", () => { beforeAll(async () => { if (!AppDataSource.isInitialized) await AppDataSource.initialize(); // Initialize test user - user = await User.createNewLocal({ + user = await UserAction.createNewLocal({ email: "test@test.com", username: "test", password: "test", @@ -29,14 +29,14 @@ describe("API Tests", () => { }); // Initialize second test user - user2 = await User.createNewLocal({ + user2 = await UserAction.createNewLocal({ email: "test2@test.com", username: "test2", password: "test2", display_name: "", }); - const app = new Application(); + const app = new ApplicationAction(); app.name = "Test Application"; app.website = "https://example.com"; @@ -106,7 +106,7 @@ describe("API Tests", () => { describe("GET /api/v1/custom_emojis", () => { beforeAll(async () => { - const emoji = new Emoji(); + const emoji = new EmojiAction(); emoji.instance = null; emoji.url = "https://example.com/test.png"; @@ -139,7 +139,7 @@ describe("API Tests", () => { expect(emojis[0].url).toBe("https://example.com/test.png"); }); afterAll(async () => { - await Emoji.delete({ shortcode: "test" }); + await EmojiAction.delete({ shortcode: "test" }); }); }); }); diff --git a/tests/api/accounts.test.ts b/tests/api/accounts.test.ts index 83d86029..44af9feb 100644 --- a/tests/api/accounts.test.ts +++ b/tests/api/accounts.test.ts @@ -3,9 +3,9 @@ import { getConfig } from "@config"; import { afterAll, beforeAll, describe, expect, test } from "bun:test"; import { AppDataSource } from "~database/datasource"; -import { Application } from "~database/entities/Application"; +import { ApplicationAction } from "~database/entities/Application"; import { Token, TokenType } from "~database/entities/Token"; -import { User } from "~database/entities/User"; +import { UserAction } from "~database/entities/User"; import { APIAccount } from "~types/entities/account"; import { APIRelationship } from "~types/entities/relationship"; import { APIStatus } from "~types/entities/status"; @@ -13,15 +13,15 @@ import { APIStatus } from "~types/entities/status"; const config = getConfig(); let token: Token; -let user: User; -let user2: User; +let user: UserAction; +let user2: UserAction; describe("API Tests", () => { beforeAll(async () => { if (!AppDataSource.isInitialized) await AppDataSource.initialize(); // Initialize test user - user = await User.createNewLocal({ + user = await UserAction.createNewLocal({ email: "test@test.com", username: "test", password: "test", @@ -29,14 +29,14 @@ describe("API Tests", () => { }); // Initialize second test user - user2 = await User.createNewLocal({ + user2 = await UserAction.createNewLocal({ email: "test2@test.com", username: "test2", password: "test2", display_name: "", }); - const app = new Application(); + const app = new ApplicationAction(); app.name = "Test Application"; app.website = "https://example.com"; diff --git a/tests/api/statuses.test.ts b/tests/api/statuses.test.ts index 484a3979..168a9b27 100644 --- a/tests/api/statuses.test.ts +++ b/tests/api/statuses.test.ts @@ -3,17 +3,17 @@ import { getConfig } from "@config"; import { afterAll, beforeAll, describe, expect, test } from "bun:test"; import { AppDataSource } from "~database/datasource"; -import { Application } from "~database/entities/Application"; +import { ApplicationAction } from "~database/entities/Application"; import { Token, TokenType } from "~database/entities/Token"; -import { User } from "~database/entities/User"; +import { UserAction } from "~database/entities/User"; import { APIContext } from "~types/entities/context"; import { APIStatus } from "~types/entities/status"; const config = getConfig(); let token: Token; -let user: User; -let user2: User; +let user: UserAction; +let user2: UserAction; let status: APIStatus | null = null; let status2: APIStatus | null = null; @@ -22,7 +22,7 @@ describe("API Tests", () => { if (!AppDataSource.isInitialized) await AppDataSource.initialize(); // Initialize test user - user = await User.createNewLocal({ + user = await UserAction.createNewLocal({ email: "test@test.com", username: "test", password: "test", @@ -30,14 +30,14 @@ describe("API Tests", () => { }); // Initialize second test user - user2 = await User.createNewLocal({ + user2 = await UserAction.createNewLocal({ email: "test2@test.com", username: "test2", password: "test2", display_name: "", }); - const app = new Application(); + const app = new ApplicationAction(); app.name = "Test Application"; app.website = "https://example.com"; @@ -157,7 +157,7 @@ describe("API Tests", () => { expect(status2.card).toBeNull(); expect(status2.poll).toBeNull(); expect(status2.emojis).toEqual([]); - expect(status2.in_reply_to_id).toEqual(status?.id); + expect(status2.in_reply_to_id).toEqual(status?.id || null); expect(status2.in_reply_to_account_id).toEqual(user.id); }); }); @@ -181,7 +181,7 @@ describe("API Tests", () => { const statusJson = (await response.json()) as APIStatus; - expect(statusJson.id).toBe(status?.id); + expect(statusJson.id).toBe(status?.id || ""); expect(statusJson.content).toBeDefined(); expect(statusJson.created_at).toBeDefined(); expect(statusJson.account).toBeDefined(); @@ -231,7 +231,7 @@ describe("API Tests", () => { expect(context.descendants.length).toBe(1); // First descendant should be status2 - expect(context.descendants[0].id).toBe(status2?.id); + expect(context.descendants[0].id).toBe(status2?.id || ""); }); }); @@ -322,7 +322,7 @@ describe("API Tests", () => { "application/json" ); - const users = (await response.json()) as User[]; + const users = (await response.json()) as UserAction[]; expect(users.length).toBe(1); expect(users[0].id).toBe(user.id); diff --git a/tests/oauth.test.ts b/tests/oauth.test.ts index e7bf0e56..a0d132d9 100644 --- a/tests/oauth.test.ts +++ b/tests/oauth.test.ts @@ -1,9 +1,9 @@ import { getConfig } from "@config"; import { afterAll, beforeAll, describe, expect, test } from "bun:test"; import { AppDataSource } from "~database/datasource"; -import { Application } from "~database/entities/Application"; +import { ApplicationAction } from "~database/entities/Application"; import { Token } from "~database/entities/Token"; -import { User, userRelations } from "~database/entities/User"; +import { UserAction, userRelations } from "~database/entities/User"; const config = getConfig(); @@ -16,7 +16,7 @@ beforeAll(async () => { if (!AppDataSource.isInitialized) await AppDataSource.initialize(); // Initialize test user - await User.createNewLocal({ + await UserAction.createNewLocal({ email: "test@test.com", username: "test", password: "test", @@ -139,7 +139,7 @@ describe("GET /api/v1/apps/verify_credentials", () => { expect(response.status).toBe(200); expect(response.headers.get("content-type")).toBe("application/json"); - const credentials = (await response.json()) as Partial; + const credentials = (await response.json()) as Partial; expect(credentials.name).toBe("Test Application"); expect(credentials.website).toBe("https://example.com"); @@ -150,7 +150,7 @@ describe("GET /api/v1/apps/verify_credentials", () => { afterAll(async () => { // Clean up user - const user = await User.findOne({ + const user = await UserAction.findOne({ where: { username: "test", }, @@ -164,7 +164,7 @@ afterAll(async () => { }, }); - const applications = await Application.findBy({ + const applications = await ApplicationAction.findBy({ client_id, secret: client_secret, });