From 88b3ec7b437adf7e41bcfb529174d72c2fc4a1a9 Mon Sep 17 00:00:00 2001 From: Jesse Wierzbinski Date: Tue, 16 Apr 2024 20:36:01 -1000 Subject: [PATCH] refactor(database): :art: Update database and schema names to be clearer --- cli.ts | 50 +- database/entities/Application.ts | 6 +- database/entities/Attachment.ts | 8 +- database/entities/Emoji.ts | 22 +- database/entities/Instance.ts | 4 +- database/entities/Like.ts | 24 +- database/entities/Notification.ts | 12 +- database/entities/Object.ts | 6 +- database/entities/Relationship.ts | 10 +- database/entities/Status.ts | 136 +- database/entities/Token.ts | 4 +- database/entities/User.ts | 88 +- drizzle/0009_easy_slyde.sql | 331 ++++ drizzle/0010_daffy_frightful_four.sql | 8 + drizzle/0011_special_the_fury.sql | 15 + drizzle/meta/0009_snapshot.json | 1582 +++++++++++++++ drizzle/meta/0010_snapshot.json | 1582 +++++++++++++++ drizzle/meta/0011_snapshot.json | 1760 +++++++++++++++++ drizzle/meta/_journal.json | 157 +- drizzle/schema.ts | 411 ++-- index.ts | 4 +- packages/database-interface/note.ts | 154 +- packages/database-interface/timeline.ts | 6 +- packages/server-handler/index.ts | 2 +- server/api/api/auth/login/index.ts | 6 +- server/api/api/auth/mastodon-login/index.ts | 4 +- server/api/api/auth/mastodon-logout/index.ts | 6 - server/api/api/auth/redirect/index.ts | 13 +- server/api/api/v1/accounts/[id]/block.test.ts | 98 + server/api/api/v1/accounts/[id]/block.ts | 6 +- .../api/api/v1/accounts/[id]/follow.test.ts | 98 + server/api/api/v1/accounts/[id]/followers.ts | 2 +- server/api/api/v1/accounts/[id]/following.ts | 2 +- server/api/api/v1/accounts/[id]/mute.test.ts | 98 + 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 | 12 +- .../api/api/v1/accounts/[id]/statuses.test.ts | 200 ++ server/api/api/v1/accounts/[id]/statuses.ts | 45 +- server/api/api/v1/accounts/[id]/unblock.ts | 6 +- server/api/api/v1/accounts/[id]/unfollow.ts | 6 +- .../api/api/v1/accounts/[id]/unmute.test.ts | 115 ++ server/api/api/v1/accounts/[id]/unmute.ts | 6 +- server/api/api/v1/accounts/[id]/unpin.ts | 6 +- .../v1/accounts/familiar_followers/index.ts | 4 +- .../api/v1/accounts/relationships/index.ts | 2 +- server/api/api/v1/accounts/search/index.ts | 4 +- .../v1/accounts/update_credentials/index.ts | 14 +- server/api/api/v1/apps/index.ts | 4 +- server/api/api/v1/blocks/index.ts | 2 +- server/api/api/v1/custom_emojis/index.ts | 2 +- server/api/api/v1/favourites/index.ts | 10 +- .../follow_requests/[account_id]/authorize.ts | 14 +- .../v1/follow_requests/[account_id]/reject.ts | 14 +- server/api/api/v1/follow_requests/index.ts | 2 +- server/api/api/v1/instance/index.ts | 22 +- server/api/api/v1/media/[id]/index.ts | 8 +- server/api/api/v1/media/index.ts | 4 +- server/api/api/v1/mutes/index.test.ts | 102 + server/api/api/v1/mutes/index.ts | 2 +- .../api/api/v1/notifications/[id]/dismiss.ts | 6 +- .../api/api/v1/notifications/clear/index.ts | 6 +- .../notifications/destroy_multiple/index.ts | 6 +- server/api/api/v1/profile/avatar.ts | 4 +- server/api/api/v1/profile/header.ts | 4 +- server/api/api/v1/statuses/[id]/context.ts | 4 +- server/api/api/v1/statuses/[id]/favourite.ts | 2 +- .../api/api/v1/statuses/[id]/favourited_by.ts | 4 +- server/api/api/v1/statuses/[id]/index.ts | 2 +- server/api/api/v1/statuses/[id]/pin.ts | 11 +- server/api/api/v1/statuses/[id]/reblog.ts | 19 +- .../api/api/v1/statuses/[id]/reblogged_by.ts | 4 +- server/api/api/v1/statuses/[id]/unreblog.ts | 6 +- server/api/api/v1/statuses/index.ts | 32 +- server/api/api/v1/timelines/home.ts | 16 +- server/api/api/v1/timelines/public.ts | 14 +- server/api/api/v2/instance/index.ts | 12 +- server/api/api/v2/media/index.ts | 4 +- server/api/api/v2/search/index.ts | 18 +- server/api/oauth/authorize-external/index.ts | 6 +- server/api/oauth/callback/[issuer]/index.ts | 4 +- server/api/oauth/token/index.ts | 4 +- server/api/objects/[uuid]/index.ts | 8 +- server/api/users/[uuid]/inbox/index.ts | 16 +- server/api/users/[uuid]/outbox/index.ts | 12 +- tests/api.test.ts | 6 +- tests/api/accounts.test.ts | 179 -- tests/api/statuses.test.ts | 29 - tests/utils.ts | 14 +- utils/meilisearch.ts | 10 +- utils/timelines.ts | 2 +- 92 files changed, 6785 insertions(+), 1018 deletions(-) create mode 100644 drizzle/0009_easy_slyde.sql create mode 100644 drizzle/0010_daffy_frightful_four.sql create mode 100644 drizzle/0011_special_the_fury.sql create mode 100644 drizzle/meta/0009_snapshot.json create mode 100644 drizzle/meta/0010_snapshot.json create mode 100644 drizzle/meta/0011_snapshot.json create mode 100644 server/api/api/v1/accounts/[id]/block.test.ts create mode 100644 server/api/api/v1/accounts/[id]/follow.test.ts create mode 100644 server/api/api/v1/accounts/[id]/mute.test.ts create mode 100644 server/api/api/v1/accounts/[id]/statuses.test.ts create mode 100644 server/api/api/v1/accounts/[id]/unmute.test.ts create mode 100644 server/api/api/v1/mutes/index.test.ts diff --git a/cli.ts b/cli.ts index ca28c45c..00dc3aaf 100644 --- a/cli.ts +++ b/cli.ts @@ -20,7 +20,7 @@ import { findManyUsers, } from "~database/entities/User"; import { client, db } from "~drizzle/db"; -import { emoji, openIdAccount, status, user } from "~drizzle/schema"; +import { Emojis, Notes, OpenIdAccounts, Users } from "~drizzle/schema"; import { Note } from "~packages/database-interface/note"; await client.connect(); @@ -225,7 +225,7 @@ const cliBuilder = new CliBuilder([ } } - await db.delete(user).where(eq(user.id, foundUser.id)); + await db.delete(Users).where(eq(Users.id, foundUser.id)); console.log( `${chalk.green("✓")} Deleted user ${chalk.blue( @@ -640,13 +640,15 @@ const cliBuilder = new CliBuilder([ return 1; } - const linkedOpenIdAccounts = await db.query.openIdAccount.findMany({ - where: (account, { eq, and }) => - and( - eq(account.userId, user.id), - eq(account.issuerId, issuerId), - ), - }); + const linkedOpenIdAccounts = await db.query.OpenIdAccounts.findMany( + { + where: (account, { eq, and }) => + and( + eq(account.userId, user.id), + eq(account.issuerId, issuerId), + ), + }, + ); if (linkedOpenIdAccounts.find((a) => a.issuerId === issuerId)) { console.log( @@ -658,7 +660,7 @@ const cliBuilder = new CliBuilder([ } // Connect the OpenID account - await db.insert(openIdAccount).values({ + await db.insert(OpenIdAccounts).values({ issuerId: issuerId, serverId: serverId, userId: user.id, @@ -712,7 +714,7 @@ const cliBuilder = new CliBuilder([ return 1; } - const account = await db.query.openIdAccount.findFirst({ + const account = await db.query.OpenIdAccounts.findFirst({ where: (account, { eq }) => eq(account.serverId, id), }); @@ -735,8 +737,8 @@ const cliBuilder = new CliBuilder([ }); await db - .delete(openIdAccount) - .where(eq(openIdAccount.id, account.id)); + .delete(OpenIdAccounts) + .where(eq(OpenIdAccounts.id, account.id)); console.log( `${chalk.green( @@ -950,14 +952,14 @@ const cliBuilder = new CliBuilder([ } let instanceQuery: SQL | undefined = - sql`EXISTS (SELECT 1 FROM "User" WHERE "User"."id" = ${status.authorId} AND "User"."instanceId" IS NULL)`; + sql`EXISTS (SELECT 1 FROM "User" WHERE "User"."id" = ${Notes.authorId} AND "User"."instanceId" IS NULL)`; if (local && remote) { instanceQuery = undefined; } else if (local) { - instanceQuery = sql`EXISTS (SELECT 1 FROM "User" WHERE "User"."id" = ${status.authorId} AND "User"."instanceId" IS NULL)`; + instanceQuery = sql`EXISTS (SELECT 1 FROM "User" WHERE "User"."id" = ${Notes.authorId} AND "User"."instanceId" IS NULL)`; } else if (remote) { - instanceQuery = sql`EXISTS (SELECT 1 FROM "User" WHERE "User"."id" = ${status.authorId} AND "User"."instanceId" IS NOT NULL)`; + instanceQuery = sql`EXISTS (SELECT 1 FROM "User" WHERE "User"."id" = ${Notes.authorId} AND "User"."instanceId" IS NOT NULL)`; } const notes = ( @@ -966,7 +968,7 @@ const cliBuilder = new CliBuilder([ or( ...fields.map((field) => // @ts-expect-error - like(status[field], `%${query}%`), + like(Notes[field], `%${query}%`), ), ), instanceQuery, @@ -1178,7 +1180,7 @@ const cliBuilder = new CliBuilder([ } // Check if emoji already exists - const existingEmoji = await db.query.emoji.findFirst({ + const existingEmoji = await db.query.Emojis.findFirst({ where: (emoji, { and, eq, isNull }) => and( eq(emoji.shortcode, shortcode), @@ -1246,7 +1248,7 @@ const cliBuilder = new CliBuilder([ const newEmoji = ( await db - .insert(emoji) + .insert(Emojis) .values({ shortcode: shortcode, url: newUrl, @@ -1323,7 +1325,7 @@ const cliBuilder = new CliBuilder([ return 1; } - const emojis = await db.query.emoji.findMany({ + const emojis = await db.query.Emojis.findMany({ where: (emoji, { and, isNull, like }) => and( like(emoji.shortcode, shortcode.replace(/\*/g, "%")), @@ -1367,9 +1369,9 @@ const cliBuilder = new CliBuilder([ } } - await db.delete(emoji).where( + await db.delete(Emojis).where( inArray( - emoji.id, + Emojis.id, emojis.map((e) => e.id), ), ); @@ -1426,7 +1428,7 @@ const cliBuilder = new CliBuilder([ return 0; } - const emojis = await db.query.emoji.findMany({ + const emojis = await db.query.Emojis.findMany({ where: (emoji, { isNull }) => isNull(emoji.instanceId), limit: Number(limit), }); @@ -1704,7 +1706,7 @@ const cliBuilder = new CliBuilder([ ).toString(); // Check if emoji already exists - const existingEmoji = await db.query.emoji.findFirst({ + const existingEmoji = await db.query.Emojis.findFirst({ where: (emoji, { and, eq, isNull }) => and( eq(emoji.shortcode, shortcode), diff --git a/database/entities/Application.ts b/database/entities/Application.ts index bd9c2aa0..67607120 100644 --- a/database/entities/Application.ts +++ b/database/entities/Application.ts @@ -1,9 +1,9 @@ import type { InferSelectModel } from "drizzle-orm"; import { db } from "~drizzle/db"; -import type { application } from "~drizzle/schema"; +import type { Applications } from "~drizzle/schema"; import type { Application as APIApplication } from "~types/mastodon/application"; -export type Application = InferSelectModel; +export type Application = InferSelectModel; /** * Retrieves the application associated with the given access token. @@ -13,7 +13,7 @@ export type Application = InferSelectModel; export const getFromToken = async ( token: string, ): Promise => { - const result = await db.query.token.findFirst({ + const result = await db.query.Tokens.findFirst({ where: (tokens, { eq }) => eq(tokens.accessToken, token), with: { application: true, diff --git a/database/entities/Attachment.ts b/database/entities/Attachment.ts index aabcc4a2..591162a8 100644 --- a/database/entities/Attachment.ts +++ b/database/entities/Attachment.ts @@ -3,11 +3,11 @@ import type { InferSelectModel } from "drizzle-orm"; import type * as Lysand from "lysand-types"; import { MediaBackendType } from "media-manager"; import { db } from "~drizzle/db"; -import { attachment } from "~drizzle/schema"; +import { Attachments } from "~drizzle/schema"; import type { AsyncAttachment as APIAsyncAttachment } from "~types/mastodon/async_attachment"; import type { Attachment as APIAttachment } from "~types/mastodon/attachment"; -export type Attachment = InferSelectModel; +export type Attachment = InferSelectModel; export const attachmentToAPI = ( attachment: Attachment, @@ -86,12 +86,12 @@ export const attachmentToLysand = ( export const attachmentFromLysand = async ( attachmentToConvert: Lysand.ContentFormat, -): Promise> => { +): Promise> => { const key = Object.keys(attachmentToConvert)[0]; const value = attachmentToConvert[key]; const result = await db - .insert(attachment) + .insert(Attachments) .values({ mimeType: key, url: value.content, diff --git a/database/entities/Emoji.ts b/database/entities/Emoji.ts index d5cc4554..42c8d86a 100644 --- a/database/entities/Emoji.ts +++ b/database/entities/Emoji.ts @@ -1,12 +1,12 @@ import { type InferSelectModel, and, eq } from "drizzle-orm"; import type * as Lysand from "lysand-types"; import { db } from "~drizzle/db"; -import { emoji, instance } from "~drizzle/schema"; +import { Emojis, Instances } from "~drizzle/schema"; import type { Emoji as APIEmoji } from "~types/mastodon/emoji"; import { addInstanceIfNotExists } from "./Instance"; -export type EmojiWithInstance = InferSelectModel & { - instance: InferSelectModel | null; +export type EmojiWithInstance = InferSelectModel & { + instance: InferSelectModel | null; }; /** @@ -18,7 +18,7 @@ export const parseEmojis = async (text: string) => { const regex = /:[a-zA-Z0-9_]+:/g; const matches = text.match(regex); if (!matches) return []; - const emojis = await db.query.emoji.findMany({ + const emojis = await db.query.Emojis.findMany({ where: (emoji, { eq, or }) => or( ...matches @@ -45,27 +45,27 @@ export const fetchEmoji = async ( ): Promise => { const existingEmoji = await db .select() - .from(emoji) - .innerJoin(instance, eq(emoji.instanceId, instance.id)) + .from(Emojis) + .innerJoin(Instances, eq(Emojis.instanceId, Instances.id)) .where( and( - eq(emoji.shortcode, emojiToFetch.name), - host ? eq(instance.baseUrl, host) : undefined, + eq(Emojis.shortcode, emojiToFetch.name), + host ? eq(Instances.baseUrl, host) : undefined, ), ) .limit(1); if (existingEmoji[0]) return { - ...existingEmoji[0].Emoji, - instance: existingEmoji[0].Instance, + ...existingEmoji[0].Emojis, + instance: existingEmoji[0].Instances, }; const foundInstance = host ? await addInstanceIfNotExists(host) : null; const result = ( await db - .insert(emoji) + .insert(Emojis) .values({ shortcode: emojiToFetch.name, url: Object.entries(emojiToFetch.url)[0][1].content, diff --git a/database/entities/Instance.ts b/database/entities/Instance.ts index 646ad085..6f217898 100644 --- a/database/entities/Instance.ts +++ b/database/entities/Instance.ts @@ -1,6 +1,6 @@ import type * as Lysand from "lysand-types"; import { db } from "~drizzle/db"; -import { instance } from "~drizzle/schema"; +import { Instances } from "~drizzle/schema"; /** * Represents an instance in the database. @@ -38,7 +38,7 @@ export const addInstanceIfNotExists = async (url: string) => { return ( await db - .insert(instance) + .insert(Instances) .values({ baseUrl: host, name: metadata.name, diff --git a/database/entities/Like.ts b/database/entities/Like.ts index 069d3bd9..584b7e34 100644 --- a/database/entities/Like.ts +++ b/database/entities/Like.ts @@ -2,11 +2,11 @@ import { config } from "config-manager"; import { type InferSelectModel, and, eq } from "drizzle-orm"; import type * as Lysand from "lysand-types"; import { db } from "~drizzle/db"; -import { like, notification } from "~drizzle/schema"; +import { Likes, Notifications } from "~drizzle/schema"; import type { StatusWithRelations } from "./Status"; import type { UserWithRelations } from "./User"; -export type Like = InferSelectModel; +export type Like = InferSelectModel; /** * Represents a Like entity in the database. @@ -33,18 +33,18 @@ export const createLike = async ( user: UserWithRelations, status: StatusWithRelations, ) => { - await db.insert(like).values({ + await db.insert(Likes).values({ likedId: status.id, likerId: user.id, }); if (status.author.instanceId === user.instanceId) { // Notify the user that their post has been favourited - await db.insert(notification).values({ + await db.insert(Notifications).values({ accountId: user.id, type: "favourite", notifiedId: status.authorId, - statusId: status.id, + noteId: status.id, }); } else { // TODO: Add database jobs for federating this @@ -61,18 +61,18 @@ export const deleteLike = async ( status: StatusWithRelations, ) => { await db - .delete(like) - .where(and(eq(like.likedId, status.id), eq(like.likerId, user.id))); + .delete(Likes) + .where(and(eq(Likes.likedId, status.id), eq(Likes.likerId, user.id))); // Notify the user that their post has been favourited await db - .delete(notification) + .delete(Notifications) .where( and( - eq(notification.accountId, user.id), - eq(notification.type, "favourite"), - eq(notification.notifiedId, status.authorId), - eq(notification.statusId, status.id), + eq(Notifications.accountId, user.id), + eq(Notifications.type, "favourite"), + eq(Notifications.notifiedId, status.authorId), + eq(Notifications.noteId, status.id), ), ); diff --git a/database/entities/Notification.ts b/database/entities/Notification.ts index a976049a..1cdcedf3 100644 --- a/database/entities/Notification.ts +++ b/database/entities/Notification.ts @@ -1,6 +1,6 @@ import type { InferSelectModel } from "drizzle-orm"; import { db } from "~drizzle/db"; -import type { notification } from "~drizzle/schema"; +import type { Notifications } from "~drizzle/schema"; import { Note } from "~packages/database-interface/note"; import type { Notification as APINotification } from "~types/mastodon/notification"; import type { StatusWithRelations } from "./Status"; @@ -12,7 +12,7 @@ import { userToAPI, } from "./User"; -export type Notification = InferSelectModel; +export type Notification = InferSelectModel; export type NotificationWithRelations = Notification & { status: StatusWithRelations | null; @@ -20,9 +20,9 @@ export type NotificationWithRelations = Notification & { }; export const findManyNotifications = async ( - query: Parameters[0], + query: Parameters[0], ): Promise => { - const output = await db.query.notification.findMany({ + const output = await db.query.Notifications.findMany({ ...query, with: { ...query?.with, @@ -30,7 +30,7 @@ export const findManyNotifications = async ( with: { ...userRelations, }, - extras: userExtrasTemplate("notification_account"), + extras: userExtrasTemplate("Notifications_account"), }, }, extras: { @@ -42,7 +42,7 @@ export const findManyNotifications = async ( output.map(async (notif) => ({ ...notif, account: transformOutputToUserWithRelations(notif.account), - status: (await Note.fromId(notif.statusId))?.getStatus() ?? null, + status: (await Note.fromId(notif.noteId))?.getStatus() ?? null, })), ); }; diff --git a/database/entities/Object.ts b/database/entities/Object.ts index a9a069b2..f4c0dd4e 100644 --- a/database/entities/Object.ts +++ b/database/entities/Object.ts @@ -1,10 +1,10 @@ import type { InferSelectModel } from "drizzle-orm"; import type * as Lysand from "lysand-types"; import { db } from "~drizzle/db"; -import { lysandObject } from "~drizzle/schema"; +import { LysandObjects } from "~drizzle/schema"; import { findFirstUser } from "./User"; -export type LysandObject = InferSelectModel; +export type LysandObject = InferSelectModel; /** * Represents a Lysand object in the database. @@ -29,7 +29,7 @@ export const createFromObject = async ( where: (user, { eq }) => eq(user.uri, authorUri), }); - return await db.insert(lysandObject).values({ + return await db.insert(LysandObjects).values({ authorId: author?.id, createdAt: new Date(object.created_at).toISOString(), extensions: object.extensions, diff --git a/database/entities/Relationship.ts b/database/entities/Relationship.ts index 99d9e92e..b3266f4d 100644 --- a/database/entities/Relationship.ts +++ b/database/entities/Relationship.ts @@ -1,10 +1,10 @@ import type { InferSelectModel } from "drizzle-orm"; import { db } from "~drizzle/db"; -import { relationship } from "~drizzle/schema"; +import { Relationships } from "~drizzle/schema"; import type { Relationship as APIRelationship } from "~types/mastodon/relationship"; import type { User } from "./User"; -export type Relationship = InferSelectModel; +export type Relationship = InferSelectModel; /** * Creates a new relationship between two users. @@ -18,7 +18,7 @@ export const createNewRelationship = async ( ): Promise => { return ( await db - .insert(relationship) + .insert(Relationships) .values({ ownerId: owner.id, subjectId: other.id, @@ -46,12 +46,12 @@ export const checkForBidirectionalRelationships = async ( user2: User, createIfNotExists = true, ): Promise => { - const relationship1 = await db.query.relationship.findFirst({ + const relationship1 = await db.query.Relationships.findFirst({ where: (rel, { and, eq }) => and(eq(rel.ownerId, user1.id), eq(rel.subjectId, user2.id)), }); - const relationship2 = await db.query.relationship.findFirst({ + const relationship2 = await db.query.Relationships.findFirst({ where: (rel, { and, eq }) => and(eq(rel.ownerId, user2.id), eq(rel.subjectId, user1.id)), }); diff --git a/database/entities/Status.ts b/database/entities/Status.ts index 106b1ffb..59243cad 100644 --- a/database/entities/Status.ts +++ b/database/entities/Status.ts @@ -27,13 +27,13 @@ import { import { parse } from "marked"; import { db } from "~drizzle/db"; import { - attachment, - emojiToStatus, - instance, - notification, - status, - statusToMentions, - user, + Attachments, + EmojiToNote, + Instances, + NoteToMentions, + Notes, + Notifications, + Users, } from "~drizzle/schema"; import { Note } from "~packages/database-interface/note"; import { LogLevel } from "~packages/log-manager"; @@ -61,17 +61,17 @@ import { userRelations, } from "./User"; -export type Status = InferSelectModel; +export type Status = InferSelectModel; export type StatusWithRelations = Status & { author: UserWithRelations; mentions: UserWithInstance[]; - attachments: InferSelectModel[]; + attachments: InferSelectModel[]; reblog: StatusWithoutRecursiveRelations | null; emojis: EmojiWithInstance[]; likes: Like[]; - inReplyTo: Status | null; - quoting: Status | null; + reply: Status | null; + quote: Status | null; application: Application | null; reblogCount: number; likeCount: number; @@ -80,20 +80,20 @@ export type StatusWithRelations = Status & { export type StatusWithoutRecursiveRelations = Omit< StatusWithRelations, - "inReplyTo" | "quoting" | "reblog" + "reply" | "quote" | "reblog" >; export const noteExtras = { reblogCount: - sql`(SELECT COUNT(*) FROM "Status" "status" WHERE "status"."reblogId" = "status".id)`.as( + sql`(SELECT COUNT(*) FROM "Notes" WHERE "Notes"."reblogId" = "Notes".id)`.as( "reblog_count", ), likeCount: - sql`(SELECT COUNT(*) FROM "Like" "like" WHERE "like"."likedId" = "status".id)`.as( + sql`(SELECT COUNT(*) FROM "Likes" WHERE "Likes"."likedId" = "Notes".id)`.as( "like_count", ), replyCount: - sql`(SELECT COUNT(*) FROM "Status" "status" WHERE "status"."inReplyToPostId" = "status".id)`.as( + sql`(SELECT COUNT(*) FROM "Notes" WHERE "Notes"."replyId" = "Notes".id)`.as( "reply_count", ), }; @@ -104,15 +104,15 @@ export const noteExtras = { * @returns */ export const findManyNotes = async ( - query: Parameters[0], + query: Parameters[0], ): Promise => { - const output = await db.query.status.findMany({ + const output = await db.query.Notes.findMany({ ...query, with: { ...query?.with, attachments: { where: (attachment, { eq }) => - eq(attachment.statusId, sql`"status"."id"`), + eq(attachment.noteId, sql`"Notes"."id"`), }, emojis: { with: { @@ -127,7 +127,7 @@ export const findManyNotes = async ( with: { ...userRelations, }, - extras: userExtrasTemplate("status_author"), + extras: userExtrasTemplate("Notes_author"), }, mentions: { with: { @@ -157,7 +157,7 @@ export const findManyNotes = async ( user: { with: userRelations, extras: userExtrasTemplate( - "status_reblog_mentions_user", + "Notes_reblog_mentions_user", ), }, }, @@ -166,15 +166,15 @@ export const findManyNotes = async ( with: { ...userRelations, }, - extras: userExtrasTemplate("status_reblog_author"), + extras: userExtrasTemplate("Notes_reblog_author"), }, }, extras: { ...noteExtras, }, }, - inReplyTo: true, - quoting: true, + reply: true, + quote: true, }, extras: { ...noteExtras, @@ -187,25 +187,17 @@ export const findManyNotes = async ( author: transformOutputToUserWithRelations(post.author), mentions: post.mentions.map((mention) => ({ ...mention.user, - endpoints: mention.user.endpoints as User["endpoints"], + endpoints: mention.user.endpoints, })), - emojis: (post.emojis ?? []).map( - (emoji) => - (emoji as unknown as Record) - .emoji as EmojiWithInstance, - ), + emojis: (post.emojis ?? []).map((emoji) => emoji.emoji), reblog: post.reblog && { ...post.reblog, author: transformOutputToUserWithRelations(post.reblog.author), mentions: post.reblog.mentions.map((mention) => ({ ...mention.user, - endpoints: mention.user.endpoints as User["endpoints"], + endpoints: mention.user.endpoints, })), - emojis: (post.reblog.emojis ?? []).map( - (emoji) => - (emoji as unknown as Record) - .emoji as EmojiWithInstance, - ), + emojis: (post.reblog.emojis ?? []).map((emoji) => emoji.emoji), reblogCount: Number(post.reblog.reblogCount), likeCount: Number(post.reblog.likeCount), replyCount: Number(post.reblog.replyCount), @@ -217,15 +209,15 @@ export const findManyNotes = async ( }; export const findFirstNote = async ( - query: Parameters[0], + query: Parameters[0], ): Promise => { - const output = await db.query.status.findFirst({ + const output = await db.query.Notes.findFirst({ ...query, with: { ...query?.with, attachments: { where: (attachment, { eq }) => - eq(attachment.statusId, sql`"status"."id"`), + eq(attachment.noteId, sql`"Notes"."id"`), }, emojis: { with: { @@ -240,7 +232,7 @@ export const findFirstNote = async ( with: { ...userRelations, }, - extras: userExtrasTemplate("status_author"), + extras: userExtrasTemplate("Notes_author"), }, mentions: { with: { @@ -270,7 +262,7 @@ export const findFirstNote = async ( user: { with: userRelations, extras: userExtrasTemplate( - "status_reblog_mentions_user", + "Notes_reblog_mentions_user", ), }, }, @@ -279,15 +271,15 @@ export const findFirstNote = async ( with: { ...userRelations, }, - extras: userExtrasTemplate("status_reblog_author"), + extras: userExtrasTemplate("Notes_reblog_author"), }, }, extras: { ...noteExtras, }, }, - inReplyTo: true, - quoting: true, + reply: true, + quote: true, }, extras: { ...noteExtras, @@ -302,25 +294,17 @@ export const findFirstNote = async ( author: transformOutputToUserWithRelations(output.author), mentions: output.mentions.map((mention) => ({ ...mention.user, - endpoints: mention.user.endpoints as User["endpoints"], + endpoints: mention.user.endpoints, })), - emojis: (output.emojis ?? []).map( - (emoji) => - (emoji as unknown as Record) - .emoji as EmojiWithInstance, - ), + emojis: (output.emojis ?? []).map((emoji) => emoji.emoji), reblog: output.reblog && { ...output.reblog, author: transformOutputToUserWithRelations(output.reblog.author), mentions: output.reblog.mentions.map((mention) => ({ ...mention.user, - endpoints: mention.user.endpoints as User["endpoints"], + endpoints: mention.user.endpoints, })), - emojis: (output.reblog.emojis ?? []).map( - (emoji) => - (emoji as unknown as Record) - .emoji as EmojiWithInstance, - ), + emojis: (output.reblog.emojis ?? []).map((emoji) => emoji.emoji), reblogCount: Number(output.reblog.reblogCount), likeCount: Number(output.reblog.likeCount), replyCount: Number(output.reblog.replyCount), @@ -340,7 +324,7 @@ export const resolveNote = async ( } const foundStatus = await Note.fromSql( - eq(status.uri, uri ?? providedNote?.uri ?? ""), + eq(Notes.uri, uri ?? providedNote?.uri ?? ""), ); if (foundStatus) return foundStatus; @@ -478,20 +462,20 @@ export const parseTextMentions = async ( const foundUsers = await db .select({ - id: user.id, - username: user.username, - baseUrl: instance.baseUrl, + id: Users.id, + username: Users.username, + baseUrl: Instances.baseUrl, }) - .from(user) - .leftJoin(instance, eq(user.instanceId, instance.id)) + .from(Users) + .leftJoin(Instances, eq(Users.instanceId, Instances.id)) .where( or( ...mentionedPeople.map((person) => and( - eq(user.username, person?.[1] ?? ""), + eq(Users.username, person?.[1] ?? ""), isLocal(person?.[2]) - ? isNull(user.instanceId) - : eq(instance.baseUrl, person?.[2] ?? ""), + ? isNull(Users.instanceId) + : eq(Instances.baseUrl, person?.[2] ?? ""), ), ), ), @@ -701,10 +685,10 @@ export const editStatus = async ( // Connect emojis for (const emoji of data.emojis) { await db - .insert(emojiToStatus) + .insert(EmojiToNote) .values({ emojiId: emoji.id, - statusId: updated.id, + noteId: updated.id, }) .execute(); } @@ -712,9 +696,9 @@ export const editStatus = async ( // Connect mentions for (const mention of mentions) { await db - .insert(statusToMentions) + .insert(NoteToMentions) .values({ - statusId: updated.id, + noteId: updated.id, userId: mention.id, }) .execute(); @@ -723,28 +707,28 @@ export const editStatus = async ( // Send notifications for mentioned local users for (const mention of mentions ?? []) { if (mention.instanceId === null) { - await db.insert(notification).values({ + await db.insert(Notifications).values({ accountId: statusToEdit.authorId, notifiedId: mention.id, type: "mention", - statusId: updated.id, + noteId: updated.id, }); } } // Set attachment parents await db - .update(attachment) + .update(Attachments) .set({ - statusId: updated.id, + noteId: updated.id, }) - .where(inArray(attachment.id, data.media_attachments ?? [])); + .where(inArray(Attachments.id, data.media_attachments ?? [])); return await Note.fromId(updated.id); }; export const isFavouritedBy = async (status: Status, user: User) => { - return !!(await db.query.like.findFirst({ + return !!(await db.query.Likes.findFirst({ where: (like, { and, eq }) => and(eq(like.likerId, user.id), eq(like.likedId, status.id)), })); @@ -779,8 +763,8 @@ export const statusToLysand = (status: StatusWithRelations): Lysand.Note => { ), is_sensitive: status.sensitive, mentions: status.mentions.map((mention) => mention.uri || ""), - quotes: getStatusUri(status.quoting) ?? undefined, - replies_to: getStatusUri(status.inReplyTo) ?? undefined, + quotes: getStatusUri(status.quote) ?? undefined, + replies_to: getStatusUri(status.reply) ?? undefined, subject: status.spoilerText, visibility: status.visibility as Lysand.Visibility, extensions: { diff --git a/database/entities/Token.ts b/database/entities/Token.ts index bb553bd6..be7fab6f 100644 --- a/database/entities/Token.ts +++ b/database/entities/Token.ts @@ -1,5 +1,5 @@ import type { InferSelectModel } from "drizzle-orm"; -import type { token } from "~drizzle/schema"; +import type { Tokens } from "~drizzle/schema"; /** * The type of token. @@ -8,4 +8,4 @@ export enum TokenType { BEARER = "Bearer", } -export type Token = InferSelectModel; +export type Token = InferSelectModel; diff --git a/database/entities/User.ts b/database/entities/User.ts index d05bc9a5..dd7258f0 100644 --- a/database/entities/User.ts +++ b/database/entities/User.ts @@ -7,13 +7,13 @@ import { htmlToText } from "html-to-text"; import type * as Lysand from "lysand-types"; import { db } from "~drizzle/db"; import { - application, - emojiToUser, - instance, - notification, - relationship, - token, - user, + Applications, + EmojiToUser, + Instances, + Notifications, + Relationships, + Tokens, + Users, } from "~drizzle/schema"; import { LogLevel } from "~packages/log-manager"; import type { Account as APIAccount } from "~types/mastodon/account"; @@ -31,14 +31,14 @@ import { addInstanceIfNotExists } from "./Instance"; import { createNewRelationship } from "./Relationship"; import type { Token } from "./Token"; -export type User = InferSelectModel; +export type User = InferSelectModel; export type UserWithInstance = User & { - instance: InferSelectModel | null; + instance: InferSelectModel | null; }; export type UserWithRelations = User & { - instance: InferSelectModel | null; + instance: InferSelectModel | null; emojis: EmojiWithInstance[]; followerCount: number; followingCount: number; @@ -46,8 +46,8 @@ export type UserWithRelations = User & { }; export type UserWithRelationsAndRelationships = UserWithRelations & { - relationships: InferSelectModel[]; - relationshipSubjects: InferSelectModel[]; + relationships: InferSelectModel[]; + relationshipSubjects: InferSelectModel[]; }; export const userRelations: { @@ -76,15 +76,15 @@ export const userRelations: { export const userExtras = { followerCount: - sql`(SELECT COUNT(*) FROM "Relationship" "relationships" WHERE ("relationships"."ownerId" = "user".id AND "relationships"."following" = true))`.as( + sql`(SELECT COUNT(*) FROM "Relationships" "relationships" WHERE ("relationships"."ownerId" = "Users".id AND "relationships"."following" = true))`.as( "follower_count", ), followingCount: - sql`(SELECT COUNT(*) FROM "Relationship" "relationshipSubjects" WHERE ("relationshipSubjects"."subjectId" = "user".id AND "relationshipSubjects"."following" = true))`.as( + sql`(SELECT COUNT(*) FROM "Relationships" "relationshipSubjects" WHERE ("relationshipSubjects"."subjectId" = "Users".id AND "relationshipSubjects"."following" = true))`.as( "following_count", ), statusCount: - sql`(SELECT COUNT(*) FROM "Status" "statuses" WHERE "statuses"."authorId" = "user".id)`.as( + sql`(SELECT COUNT(*) FROM "Notes" WHERE "Notes"."authorId" = "Users".id)`.as( "status_count", ), }; @@ -92,15 +92,15 @@ export const userExtras = { export const userExtrasTemplate = (name: string) => ({ // @ts-ignore followerCount: sql([ - `(SELECT COUNT(*) FROM "Relationship" "relationships" WHERE ("relationships"."ownerId" = "${name}".id AND "relationships"."following" = true))`, + `(SELECT COUNT(*) FROM "Relationships" "relationships" WHERE ("relationships"."ownerId" = "${name}".id AND "relationships"."following" = true))`, ]).as("follower_count"), // @ts-ignore followingCount: sql([ - `(SELECT COUNT(*) FROM "Relationship" "relationshipSubjects" WHERE ("relationshipSubjects"."subjectId" = "${name}".id AND "relationshipSubjects"."following" = true))`, + `(SELECT COUNT(*) FROM "Relationships" "relationshipSubjects" WHERE ("relationshipSubjects"."subjectId" = "${name}".id AND "relationshipSubjects"."following" = true))`, ]).as("following_count"), // @ts-ignore statusCount: sql([ - `(SELECT COUNT(*) FROM "Status" "statuses" WHERE "statuses"."authorId" = "${name}".id)`, + `(SELECT COUNT(*) FROM "Notes" WHERE "Notes"."authorId" = "${name}".id)`, ]).as("status_count"), }); @@ -151,12 +151,12 @@ export const followRequestUser = async ( reblogs = false, notify = false, languages: string[] = [], -): Promise> => { +): Promise> => { const isRemote = followee.instanceId !== null; const updatedRelationship = ( await db - .update(relationship) + .update(Relationships) .set({ following: isRemote ? false : !followee.isLocked, requested: isRemote ? true : followee.isLocked, @@ -164,7 +164,7 @@ export const followRequestUser = async ( notifying: notify, languages: languages, }) - .where(eq(relationship.id, relationshipId)) + .where(eq(Relationships.id, relationshipId)) .returning() )[0]; @@ -195,17 +195,17 @@ export const followRequestUser = async ( return ( await db - .update(relationship) + .update(Relationships) .set({ following: false, requested: false, }) - .where(eq(relationship.id, relationshipId)) + .where(eq(Relationships.id, relationshipId)) .returning() )[0]; } } else { - await db.insert(notification).values({ + await db.insert(Notifications).values({ accountId: follower.id, type: followee.isLocked ? "follow_request" : "follow", notifiedId: followee.id, @@ -277,7 +277,7 @@ export const transformOutputToUserWithRelations = ( emojiId: string; emoji?: EmojiWithInstance; }[]; - instance: InferSelectModel | null; + instance: InferSelectModel | null; endpoints: unknown; }, ): UserWithRelations => { @@ -306,9 +306,9 @@ export const transformOutputToUserWithRelations = ( }; export const findManyUsers = async ( - query: Parameters[0], + query: Parameters[0], ): Promise => { - const output = await db.query.user.findMany({ + const output = await db.query.Users.findMany({ ...query, with: { ...userRelations, @@ -324,9 +324,9 @@ export const findManyUsers = async ( }; export const findFirstUser = async ( - query: Parameters[0], + query: Parameters[0], ): Promise => { - const output = await db.query.user.findFirst({ + const output = await db.query.Users.findFirst({ ...query, with: { ...userRelations, @@ -418,7 +418,7 @@ export const resolveUser = async ( const newUser = ( await db - .insert(user) + .insert(Users) .values({ username: data.username, uri: data.uri, @@ -456,7 +456,7 @@ export const resolveUser = async ( // Add emojis to user if (emojis.length > 0) { - await db.insert(emojiToUser).values( + await db.insert(EmojiToUser).values( emojis.map((emoji) => ({ emojiId: emoji.id, userId: newUser.id, @@ -494,15 +494,15 @@ export const resolveWebFinger = async ( // Check if user not already in database const foundUser = await db .select() - .from(user) - .innerJoin(instance, eq(user.instanceId, instance.id)) - .where(and(eq(user.username, identifier), eq(instance.baseUrl, host))) + .from(Users) + .innerJoin(Instances, eq(Users.instanceId, Instances.id)) + .where(and(eq(Users.username, identifier), eq(Instances.baseUrl, host))) .limit(1); if (foundUser[0]) return ( (await findFirstUser({ - where: (user, { eq }) => eq(user.id, foundUser[0].User.id), + where: (user, { eq }) => eq(user.id, foundUser[0].Users.id), })) || null ); @@ -580,7 +580,7 @@ export const createNewLocalUser = async (data: { const newUser = ( await db - .insert(user) + .insert(Users) .values({ username: data.username, displayName: data.display_name ?? data.username, @@ -662,12 +662,12 @@ export const retrieveUserAndApplicationFromToken = async ( const output = ( await db .select({ - token: token, - application: application, + token: Tokens, + application: Applications, }) - .from(token) - .leftJoin(application, eq(token.applicationId, application.id)) - .where(eq(token.accessToken, access_token)) + .from(Tokens) + .leftJoin(Applications, eq(Tokens.applicationId, Applications.id)) + .where(eq(Tokens.accessToken, access_token)) .limit(1) )[0]; @@ -686,7 +686,7 @@ export const retrieveToken = async ( if (!access_token) return null; return ( - (await db.query.token.findFirst({ + (await db.query.Tokens.findFirst({ where: (tokens, { eq }) => eq(tokens.accessToken, access_token), })) ?? null ); @@ -700,8 +700,8 @@ export const retrieveToken = async ( export const getRelationshipToOtherUser = async ( user: UserWithRelations, other: User, -): Promise> => { - const foundRelationship = await db.query.relationship.findFirst({ +): Promise> => { + const foundRelationship = await db.query.Relationships.findFirst({ where: (relationship, { and, eq }) => and( eq(relationship.ownerId, user.id), diff --git a/drizzle/0009_easy_slyde.sql b/drizzle/0009_easy_slyde.sql new file mode 100644 index 00000000..dce5c0bf --- /dev/null +++ b/drizzle/0009_easy_slyde.sql @@ -0,0 +1,331 @@ +ALTER TABLE "Application" RENAME TO "Applications";--> statement-breakpoint +ALTER TABLE "Attachment" RENAME TO "Attachments";--> statement-breakpoint +ALTER TABLE "Emoji" RENAME TO "Emojis";--> statement-breakpoint +ALTER TABLE "EmojiToStatus" RENAME TO "EmojiToNote";--> statement-breakpoint +ALTER TABLE "Flag" RENAME TO "Flags";--> statement-breakpoint +ALTER TABLE "Instance" RENAME TO "Instances";--> statement-breakpoint +ALTER TABLE "Like" RENAME TO "Likes";--> statement-breakpoint +ALTER TABLE "ModNote" RENAME TO "ModNotes";--> statement-breakpoint +ALTER TABLE "ModTag" RENAME TO "ModTags";--> statement-breakpoint +ALTER TABLE "Notification" RENAME TO "Notifications";--> statement-breakpoint +ALTER TABLE "OpenIdAccount" RENAME TO "OpenIdAccounts";--> statement-breakpoint +ALTER TABLE "OpenIdLoginFlow" RENAME TO "OpenIdLoginFlows";--> statement-breakpoint +ALTER TABLE "Relationship" RENAME TO "Relationships";--> statement-breakpoint +ALTER TABLE "Status" RENAME TO "Notes";--> statement-breakpoint +ALTER TABLE "StatusToMentions" RENAME TO "NoteToMentions";--> statement-breakpoint +ALTER TABLE "Token" RENAME TO "Tokens";--> statement-breakpoint +ALTER TABLE "User" RENAME TO "Users";--> statement-breakpoint +ALTER TABLE "UserToPinnedNotes" RENAME COLUMN "statusId" TO "noteId";--> statement-breakpoint +ALTER TABLE "Attachments" RENAME COLUMN "statusId" TO "noteId";--> statement-breakpoint +ALTER TABLE "EmojiToNote" RENAME COLUMN "statusId" TO "noteId";--> statement-breakpoint +ALTER TABLE "Flags" RENAME COLUMN "flaggeStatusId" TO "noteId";--> statement-breakpoint +ALTER TABLE "Flags" RENAME COLUMN "flaggedUserId" TO "userId";--> statement-breakpoint +ALTER TABLE "ModNotes" RENAME COLUMN "notedStatusId" TO "noteId";--> statement-breakpoint +ALTER TABLE "ModNotes" RENAME COLUMN "notedUserId" TO "userId";--> statement-breakpoint +ALTER TABLE "ModTags" RENAME COLUMN "taggedStatusId" TO "statusId";--> statement-breakpoint +ALTER TABLE "ModTags" RENAME COLUMN "taggedUserId" TO "userId";--> statement-breakpoint +ALTER TABLE "Notifications" RENAME COLUMN "statusId" TO "noteId";--> statement-breakpoint +ALTER TABLE "Notes" RENAME COLUMN "inReplyToPostId" TO "replyId";--> statement-breakpoint +ALTER TABLE "Notes" RENAME COLUMN "quotingPostId" TO "quoteId";--> statement-breakpoint +ALTER TABLE "NoteToMentions" RENAME COLUMN "statusId" TO "noteId";--> statement-breakpoint +ALTER TABLE "EmojiToUser" DROP CONSTRAINT "EmojiToUser_emojiId_Emoji_id_fk"; +--> statement-breakpoint +ALTER TABLE "EmojiToUser" DROP CONSTRAINT "EmojiToUser_userId_User_id_fk"; +--> statement-breakpoint +ALTER TABLE "UserToPinnedNotes" DROP CONSTRAINT "UserToPinnedNotes_userId_Status_id_fk"; +--> statement-breakpoint +ALTER TABLE "UserToPinnedNotes" DROP CONSTRAINT "UserToPinnedNotes_statusId_User_id_fk"; +--> statement-breakpoint +ALTER TABLE "Attachments" DROP CONSTRAINT "Attachment_statusId_Status_id_fk"; +--> statement-breakpoint +ALTER TABLE "Emojis" DROP CONSTRAINT "Emoji_instanceId_Instance_id_fk"; +--> statement-breakpoint +ALTER TABLE "EmojiToNote" DROP CONSTRAINT "EmojiToStatus_emojiId_Emoji_id_fk"; +--> statement-breakpoint +ALTER TABLE "EmojiToNote" DROP CONSTRAINT "EmojiToStatus_statusId_Status_id_fk"; +--> statement-breakpoint +ALTER TABLE "Flags" DROP CONSTRAINT "Flag_flaggeStatusId_Status_id_fk"; +--> statement-breakpoint +ALTER TABLE "Flags" DROP CONSTRAINT "Flag_flaggedUserId_User_id_fk"; +--> statement-breakpoint +ALTER TABLE "Likes" DROP CONSTRAINT "Like_likerId_User_id_fk"; +--> statement-breakpoint +ALTER TABLE "Likes" DROP CONSTRAINT "Like_likedId_Status_id_fk"; +--> statement-breakpoint +ALTER TABLE "ModNotes" DROP CONSTRAINT "ModNote_notedStatusId_Status_id_fk"; +--> statement-breakpoint +ALTER TABLE "ModNotes" DROP CONSTRAINT "ModNote_notedUserId_User_id_fk"; +--> statement-breakpoint +ALTER TABLE "ModNotes" DROP CONSTRAINT "ModNote_modId_User_id_fk"; +--> statement-breakpoint +ALTER TABLE "ModTags" DROP CONSTRAINT "ModTag_taggedStatusId_Status_id_fk"; +--> statement-breakpoint +ALTER TABLE "ModTags" DROP CONSTRAINT "ModTag_taggedUserId_User_id_fk"; +--> statement-breakpoint +ALTER TABLE "ModTags" DROP CONSTRAINT "ModTag_modId_User_id_fk"; +--> statement-breakpoint +ALTER TABLE "Notifications" DROP CONSTRAINT "Notification_notifiedId_User_id_fk"; +--> statement-breakpoint +ALTER TABLE "Notifications" DROP CONSTRAINT "Notification_accountId_User_id_fk"; +--> statement-breakpoint +ALTER TABLE "Notifications" DROP CONSTRAINT "Notification_statusId_Status_id_fk"; +--> statement-breakpoint +ALTER TABLE "OpenIdAccounts" DROP CONSTRAINT "OpenIdAccount_userId_User_id_fk"; +--> statement-breakpoint +ALTER TABLE "OpenIdLoginFlows" DROP CONSTRAINT "OpenIdLoginFlow_applicationId_Application_id_fk"; +--> statement-breakpoint +ALTER TABLE "Relationships" DROP CONSTRAINT "Relationship_ownerId_User_id_fk"; +--> statement-breakpoint +ALTER TABLE "Relationships" DROP CONSTRAINT "Relationship_subjectId_User_id_fk"; +--> statement-breakpoint +ALTER TABLE "Notes" DROP CONSTRAINT "Status_authorId_User_id_fk"; +--> statement-breakpoint +ALTER TABLE "Notes" DROP CONSTRAINT "Status_applicationId_Application_id_fk"; +--> statement-breakpoint +ALTER TABLE "Notes" DROP CONSTRAINT "Status_reblogId_Status_id_fk"; +--> statement-breakpoint +ALTER TABLE "Notes" DROP CONSTRAINT "Status_inReplyToPostId_Status_id_fk"; +--> statement-breakpoint +ALTER TABLE "Notes" DROP CONSTRAINT "Status_quotingPostId_Status_id_fk"; +--> statement-breakpoint +ALTER TABLE "NoteToMentions" DROP CONSTRAINT "StatusToMentions_statusId_Status_id_fk"; +--> statement-breakpoint +ALTER TABLE "NoteToMentions" DROP CONSTRAINT "StatusToMentions_userId_User_id_fk"; +--> statement-breakpoint +ALTER TABLE "Tokens" DROP CONSTRAINT "Token_userId_User_id_fk"; +--> statement-breakpoint +ALTER TABLE "Tokens" DROP CONSTRAINT "Token_applicationId_Application_id_fk"; +--> statement-breakpoint +ALTER TABLE "Users" DROP CONSTRAINT "User_instanceId_Instance_id_fk"; +--> statement-breakpoint +DROP INDEX IF EXISTS "UserToPinnedNotes_userId_statusId_index";--> statement-breakpoint +DROP INDEX IF EXISTS "UserToPinnedNotes_statusId_index";--> statement-breakpoint +DROP INDEX IF EXISTS "Application_client_id_index";--> statement-breakpoint +DROP INDEX IF EXISTS "EmojiToStatus_emojiId_statusId_index";--> statement-breakpoint +DROP INDEX IF EXISTS "EmojiToStatus_statusId_index";--> statement-breakpoint +DROP INDEX IF EXISTS "Status_uri_index";--> statement-breakpoint +DROP INDEX IF EXISTS "StatusToMentions_statusId_userId_index";--> statement-breakpoint +DROP INDEX IF EXISTS "StatusToMentions_userId_index";--> statement-breakpoint +DROP INDEX IF EXISTS "User_uri_index";--> statement-breakpoint +DROP INDEX IF EXISTS "User_username_index";--> statement-breakpoint +DROP INDEX IF EXISTS "User_email_index";--> statement-breakpoint +CREATE UNIQUE INDEX IF NOT EXISTS "UserToPinnedNotes_userId_noteId_index" ON "UserToPinnedNotes" ("userId","noteId");--> statement-breakpoint +CREATE INDEX IF NOT EXISTS "UserToPinnedNotes_noteId_index" ON "UserToPinnedNotes" ("noteId");--> statement-breakpoint +CREATE UNIQUE INDEX IF NOT EXISTS "Applications_client_id_index" ON "Applications" ("client_id");--> statement-breakpoint +CREATE UNIQUE INDEX IF NOT EXISTS "EmojiToNote_emojiId_noteId_index" ON "EmojiToNote" ("emojiId","noteId");--> statement-breakpoint +CREATE INDEX IF NOT EXISTS "EmojiToNote_noteId_index" ON "EmojiToNote" ("noteId");--> statement-breakpoint +CREATE UNIQUE INDEX IF NOT EXISTS "Notes_uri_index" ON "Notes" ("uri");--> statement-breakpoint +CREATE UNIQUE INDEX IF NOT EXISTS "NoteToMentions_noteId_userId_index" ON "NoteToMentions" ("noteId","userId");--> statement-breakpoint +CREATE INDEX IF NOT EXISTS "NoteToMentions_userId_index" ON "NoteToMentions" ("userId");--> statement-breakpoint +CREATE UNIQUE INDEX IF NOT EXISTS "Users_uri_index" ON "Users" ("uri");--> statement-breakpoint +CREATE UNIQUE INDEX IF NOT EXISTS "Users_username_index" ON "Users" ("username");--> statement-breakpoint +CREATE UNIQUE INDEX IF NOT EXISTS "Users_email_index" ON "Users" ("email");--> statement-breakpoint +DO $$ BEGIN + ALTER TABLE "EmojiToUser" ADD CONSTRAINT "EmojiToUser_emojiId_Emojis_id_fk" FOREIGN KEY ("emojiId") REFERENCES "Emojis"("id") ON DELETE cascade ON UPDATE cascade; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; +--> statement-breakpoint +DO $$ BEGIN + ALTER TABLE "EmojiToUser" ADD CONSTRAINT "EmojiToUser_userId_Users_id_fk" FOREIGN KEY ("userId") REFERENCES "Users"("id") ON DELETE cascade ON UPDATE cascade; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; +--> statement-breakpoint +DO $$ BEGIN + ALTER TABLE "UserToPinnedNotes" ADD CONSTRAINT "UserToPinnedNotes_userId_Notes_id_fk" FOREIGN KEY ("userId") REFERENCES "Notes"("id") ON DELETE cascade ON UPDATE cascade; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; +--> statement-breakpoint +DO $$ BEGIN + ALTER TABLE "UserToPinnedNotes" ADD CONSTRAINT "UserToPinnedNotes_noteId_Users_id_fk" FOREIGN KEY ("noteId") REFERENCES "Users"("id") ON DELETE cascade ON UPDATE cascade; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; +--> statement-breakpoint +DO $$ BEGIN + ALTER TABLE "Attachments" ADD CONSTRAINT "Attachments_noteId_Notes_id_fk" FOREIGN KEY ("noteId") REFERENCES "Notes"("id") ON DELETE cascade ON UPDATE cascade; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; +--> statement-breakpoint +DO $$ BEGIN + ALTER TABLE "Emojis" ADD CONSTRAINT "Emojis_instanceId_Instances_id_fk" FOREIGN KEY ("instanceId") REFERENCES "Instances"("id") ON DELETE cascade ON UPDATE cascade; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; +--> statement-breakpoint +DO $$ BEGIN + ALTER TABLE "EmojiToNote" ADD CONSTRAINT "EmojiToNote_emojiId_Emojis_id_fk" FOREIGN KEY ("emojiId") REFERENCES "Emojis"("id") ON DELETE cascade ON UPDATE cascade; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; +--> statement-breakpoint +DO $$ BEGIN + ALTER TABLE "EmojiToNote" ADD CONSTRAINT "EmojiToNote_noteId_Notes_id_fk" FOREIGN KEY ("noteId") REFERENCES "Notes"("id") ON DELETE cascade ON UPDATE cascade; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; +--> statement-breakpoint +DO $$ BEGIN + ALTER TABLE "Flags" ADD CONSTRAINT "Flags_noteId_Notes_id_fk" FOREIGN KEY ("noteId") REFERENCES "Notes"("id") ON DELETE cascade ON UPDATE cascade; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; +--> statement-breakpoint +DO $$ BEGIN + ALTER TABLE "Flags" ADD CONSTRAINT "Flags_userId_Users_id_fk" FOREIGN KEY ("userId") REFERENCES "Users"("id") ON DELETE cascade ON UPDATE cascade; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; +--> statement-breakpoint +DO $$ BEGIN + ALTER TABLE "Likes" ADD CONSTRAINT "Likes_likerId_Users_id_fk" FOREIGN KEY ("likerId") REFERENCES "Users"("id") ON DELETE cascade ON UPDATE cascade; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; +--> statement-breakpoint +DO $$ BEGIN + ALTER TABLE "Likes" ADD CONSTRAINT "Likes_likedId_Notes_id_fk" FOREIGN KEY ("likedId") REFERENCES "Notes"("id") ON DELETE cascade ON UPDATE cascade; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; +--> statement-breakpoint +DO $$ BEGIN + ALTER TABLE "ModNotes" ADD CONSTRAINT "ModNotes_noteId_Notes_id_fk" FOREIGN KEY ("noteId") REFERENCES "Notes"("id") ON DELETE cascade ON UPDATE cascade; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; +--> statement-breakpoint +DO $$ BEGIN + ALTER TABLE "ModNotes" ADD CONSTRAINT "ModNotes_userId_Users_id_fk" FOREIGN KEY ("userId") REFERENCES "Users"("id") ON DELETE cascade ON UPDATE cascade; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; +--> statement-breakpoint +DO $$ BEGIN + ALTER TABLE "ModNotes" ADD CONSTRAINT "ModNotes_modId_Users_id_fk" FOREIGN KEY ("modId") REFERENCES "Users"("id") ON DELETE cascade ON UPDATE cascade; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; +--> statement-breakpoint +DO $$ BEGIN + ALTER TABLE "ModTags" ADD CONSTRAINT "ModTags_statusId_Notes_id_fk" FOREIGN KEY ("statusId") REFERENCES "Notes"("id") ON DELETE cascade ON UPDATE cascade; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; +--> statement-breakpoint +DO $$ BEGIN + ALTER TABLE "ModTags" ADD CONSTRAINT "ModTags_userId_Users_id_fk" FOREIGN KEY ("userId") REFERENCES "Users"("id") ON DELETE cascade ON UPDATE cascade; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; +--> statement-breakpoint +DO $$ BEGIN + ALTER TABLE "ModTags" ADD CONSTRAINT "ModTags_modId_Users_id_fk" FOREIGN KEY ("modId") REFERENCES "Users"("id") ON DELETE cascade ON UPDATE cascade; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; +--> statement-breakpoint +DO $$ BEGIN + ALTER TABLE "Notifications" ADD CONSTRAINT "Notifications_notifiedId_Users_id_fk" FOREIGN KEY ("notifiedId") REFERENCES "Users"("id") ON DELETE cascade ON UPDATE cascade; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; +--> statement-breakpoint +DO $$ BEGIN + ALTER TABLE "Notifications" ADD CONSTRAINT "Notifications_accountId_Users_id_fk" FOREIGN KEY ("accountId") REFERENCES "Users"("id") ON DELETE cascade ON UPDATE cascade; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; +--> statement-breakpoint +DO $$ BEGIN + ALTER TABLE "Notifications" ADD CONSTRAINT "Notifications_noteId_Notes_id_fk" FOREIGN KEY ("noteId") REFERENCES "Notes"("id") ON DELETE cascade ON UPDATE cascade; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; +--> statement-breakpoint +DO $$ BEGIN + ALTER TABLE "OpenIdAccounts" ADD CONSTRAINT "OpenIdAccounts_userId_Users_id_fk" FOREIGN KEY ("userId") REFERENCES "Users"("id") ON DELETE set null ON UPDATE cascade; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; +--> statement-breakpoint +DO $$ BEGIN + ALTER TABLE "OpenIdLoginFlows" ADD CONSTRAINT "OpenIdLoginFlows_applicationId_Applications_id_fk" FOREIGN KEY ("applicationId") REFERENCES "Applications"("id") ON DELETE cascade ON UPDATE cascade; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; +--> statement-breakpoint +DO $$ BEGIN + ALTER TABLE "Relationships" ADD CONSTRAINT "Relationships_ownerId_Users_id_fk" FOREIGN KEY ("ownerId") REFERENCES "Users"("id") ON DELETE cascade ON UPDATE cascade; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; +--> statement-breakpoint +DO $$ BEGIN + ALTER TABLE "Relationships" ADD CONSTRAINT "Relationships_subjectId_Users_id_fk" FOREIGN KEY ("subjectId") REFERENCES "Users"("id") ON DELETE cascade ON UPDATE cascade; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; +--> statement-breakpoint +DO $$ BEGIN + ALTER TABLE "Notes" ADD CONSTRAINT "Notes_authorId_Users_id_fk" FOREIGN KEY ("authorId") REFERENCES "Users"("id") ON DELETE cascade ON UPDATE cascade; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; +--> statement-breakpoint +DO $$ BEGIN + ALTER TABLE "Notes" ADD CONSTRAINT "Notes_applicationId_Applications_id_fk" FOREIGN KEY ("applicationId") REFERENCES "Applications"("id") ON DELETE set null ON UPDATE cascade; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; +--> statement-breakpoint +DO $$ BEGIN + ALTER TABLE "Notes" ADD CONSTRAINT "Notes_reblogId_Notes_id_fk" FOREIGN KEY ("reblogId") REFERENCES "Notes"("id") ON DELETE cascade ON UPDATE cascade; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; +--> statement-breakpoint +DO $$ BEGIN + ALTER TABLE "Notes" ADD CONSTRAINT "Notes_replyId_Notes_id_fk" FOREIGN KEY ("replyId") REFERENCES "Notes"("id") ON DELETE set null ON UPDATE cascade; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; +--> statement-breakpoint +DO $$ BEGIN + ALTER TABLE "Notes" ADD CONSTRAINT "Notes_quoteId_Notes_id_fk" FOREIGN KEY ("quoteId") REFERENCES "Notes"("id") ON DELETE set null ON UPDATE cascade; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; +--> statement-breakpoint +DO $$ BEGIN + ALTER TABLE "NoteToMentions" ADD CONSTRAINT "NoteToMentions_noteId_Notes_id_fk" FOREIGN KEY ("noteId") REFERENCES "Notes"("id") ON DELETE cascade ON UPDATE cascade; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; +--> statement-breakpoint +DO $$ BEGIN + ALTER TABLE "NoteToMentions" ADD CONSTRAINT "NoteToMentions_userId_Users_id_fk" FOREIGN KEY ("userId") REFERENCES "Users"("id") ON DELETE cascade ON UPDATE cascade; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; +--> statement-breakpoint +DO $$ BEGIN + ALTER TABLE "Tokens" ADD CONSTRAINT "Tokens_userId_Users_id_fk" FOREIGN KEY ("userId") REFERENCES "Users"("id") ON DELETE cascade ON UPDATE cascade; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; +--> statement-breakpoint +DO $$ BEGIN + ALTER TABLE "Tokens" ADD CONSTRAINT "Tokens_applicationId_Applications_id_fk" FOREIGN KEY ("applicationId") REFERENCES "Applications"("id") ON DELETE cascade ON UPDATE cascade; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; +--> statement-breakpoint +DO $$ BEGIN + ALTER TABLE "Users" ADD CONSTRAINT "Users_instanceId_Instances_id_fk" FOREIGN KEY ("instanceId") REFERENCES "Instances"("id") ON DELETE cascade ON UPDATE cascade; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; diff --git a/drizzle/0010_daffy_frightful_four.sql b/drizzle/0010_daffy_frightful_four.sql new file mode 100644 index 00000000..00e193c1 --- /dev/null +++ b/drizzle/0010_daffy_frightful_four.sql @@ -0,0 +1,8 @@ +ALTER TABLE "ModTags" RENAME COLUMN "statusId" TO "noteId";--> statement-breakpoint +ALTER TABLE "ModTags" DROP CONSTRAINT "ModTags_statusId_Notes_id_fk"; +--> statement-breakpoint +DO $$ BEGIN + ALTER TABLE "ModTags" ADD CONSTRAINT "ModTags_noteId_Notes_id_fk" FOREIGN KEY ("noteId") REFERENCES "Notes"("id") ON DELETE cascade ON UPDATE cascade; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; diff --git a/drizzle/0011_special_the_fury.sql b/drizzle/0011_special_the_fury.sql new file mode 100644 index 00000000..438e072e --- /dev/null +++ b/drizzle/0011_special_the_fury.sql @@ -0,0 +1,15 @@ +ALTER TABLE "UserToPinnedNotes" DROP CONSTRAINT "UserToPinnedNotes_userId_Notes_id_fk"; +--> statement-breakpoint +ALTER TABLE "UserToPinnedNotes" DROP CONSTRAINT "UserToPinnedNotes_noteId_Users_id_fk"; +--> statement-breakpoint +DO $$ BEGIN + ALTER TABLE "UserToPinnedNotes" ADD CONSTRAINT "UserToPinnedNotes_userId_Users_id_fk" FOREIGN KEY ("userId") REFERENCES "Users"("id") ON DELETE cascade ON UPDATE cascade; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; +--> statement-breakpoint +DO $$ BEGIN + ALTER TABLE "UserToPinnedNotes" ADD CONSTRAINT "UserToPinnedNotes_noteId_Notes_id_fk" FOREIGN KEY ("noteId") REFERENCES "Notes"("id") ON DELETE cascade ON UPDATE cascade; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; diff --git a/drizzle/meta/0009_snapshot.json b/drizzle/meta/0009_snapshot.json new file mode 100644 index 00000000..1b7daefa --- /dev/null +++ b/drizzle/meta/0009_snapshot.json @@ -0,0 +1,1582 @@ +{ + "id": "8d1c9139-3ed4-46ad-b0ca-4d972d1e5d3e", + "prevId": "146b3c00-8c0f-4dec-8ce7-36f91a2fc445", + "version": "5", + "dialect": "pg", + "tables": { + "Applications": { + "name": "Applications", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "uuid_generate_v7()" + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "website": { + "name": "website", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "vapid_key": { + "name": "vapid_key", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "client_id": { + "name": "client_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "secret": { + "name": "secret", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "scopes": { + "name": "scopes", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "redirect_uris": { + "name": "redirect_uris", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "Applications_client_id_index": { + "name": "Applications_client_id_index", + "columns": ["client_id"], + "isUnique": true + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "Attachments": { + "name": "Attachments", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "uuid_generate_v7()" + }, + "url": { + "name": "url", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "remote_url": { + "name": "remote_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "thumbnail_url": { + "name": "thumbnail_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mime_type": { + "name": "mime_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "blurhash": { + "name": "blurhash", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "sha256": { + "name": "sha256", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "fps": { + "name": "fps", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "duration": { + "name": "duration", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "width": { + "name": "width", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "height": { + "name": "height", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "size": { + "name": "size", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "noteId": { + "name": "noteId", + "type": "uuid", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "Attachments_noteId_Notes_id_fk": { + "name": "Attachments_noteId_Notes_id_fk", + "tableFrom": "Attachments", + "tableTo": "Notes", + "columnsFrom": ["noteId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "Emojis": { + "name": "Emojis", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "uuid_generate_v7()" + }, + "shortcode": { + "name": "shortcode", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "url": { + "name": "url", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "visible_in_picker": { + "name": "visible_in_picker", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "alt": { + "name": "alt", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "content_type": { + "name": "content_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "instanceId": { + "name": "instanceId", + "type": "uuid", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "Emojis_instanceId_Instances_id_fk": { + "name": "Emojis_instanceId_Instances_id_fk", + "tableFrom": "Emojis", + "tableTo": "Instances", + "columnsFrom": ["instanceId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "EmojiToNote": { + "name": "EmojiToNote", + "schema": "", + "columns": { + "emojiId": { + "name": "emojiId", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "noteId": { + "name": "noteId", + "type": "uuid", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "EmojiToNote_emojiId_noteId_index": { + "name": "EmojiToNote_emojiId_noteId_index", + "columns": ["emojiId", "noteId"], + "isUnique": true + }, + "EmojiToNote_noteId_index": { + "name": "EmojiToNote_noteId_index", + "columns": ["noteId"], + "isUnique": false + } + }, + "foreignKeys": { + "EmojiToNote_emojiId_Emojis_id_fk": { + "name": "EmojiToNote_emojiId_Emojis_id_fk", + "tableFrom": "EmojiToNote", + "tableTo": "Emojis", + "columnsFrom": ["emojiId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "EmojiToNote_noteId_Notes_id_fk": { + "name": "EmojiToNote_noteId_Notes_id_fk", + "tableFrom": "EmojiToNote", + "tableTo": "Notes", + "columnsFrom": ["noteId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "EmojiToUser": { + "name": "EmojiToUser", + "schema": "", + "columns": { + "emojiId": { + "name": "emojiId", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "userId": { + "name": "userId", + "type": "uuid", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "EmojiToUser_emojiId_userId_index": { + "name": "EmojiToUser_emojiId_userId_index", + "columns": ["emojiId", "userId"], + "isUnique": true + }, + "EmojiToUser_userId_index": { + "name": "EmojiToUser_userId_index", + "columns": ["userId"], + "isUnique": false + } + }, + "foreignKeys": { + "EmojiToUser_emojiId_Emojis_id_fk": { + "name": "EmojiToUser_emojiId_Emojis_id_fk", + "tableFrom": "EmojiToUser", + "tableTo": "Emojis", + "columnsFrom": ["emojiId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "EmojiToUser_userId_Users_id_fk": { + "name": "EmojiToUser_userId_Users_id_fk", + "tableFrom": "EmojiToUser", + "tableTo": "Users", + "columnsFrom": ["userId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "Flags": { + "name": "Flags", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "uuid_generate_v7()" + }, + "flag_type": { + "name": "flag_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'other'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "noteId": { + "name": "noteId", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "userId": { + "name": "userId", + "type": "uuid", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "Flags_noteId_Notes_id_fk": { + "name": "Flags_noteId_Notes_id_fk", + "tableFrom": "Flags", + "tableTo": "Notes", + "columnsFrom": ["noteId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "Flags_userId_Users_id_fk": { + "name": "Flags_userId_Users_id_fk", + "tableFrom": "Flags", + "tableTo": "Users", + "columnsFrom": ["userId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "Instances": { + "name": "Instances", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "uuid_generate_v7()" + }, + "base_url": { + "name": "base_url", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "version": { + "name": "version", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "logo": { + "name": "logo", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "disable_automoderation": { + "name": "disable_automoderation", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "Likes": { + "name": "Likes", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "uuid_generate_v7()" + }, + "likerId": { + "name": "likerId", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "likedId": { + "name": "likedId", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "Likes_likerId_Users_id_fk": { + "name": "Likes_likerId_Users_id_fk", + "tableFrom": "Likes", + "tableTo": "Users", + "columnsFrom": ["likerId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "Likes_likedId_Notes_id_fk": { + "name": "Likes_likedId_Notes_id_fk", + "tableFrom": "Likes", + "tableTo": "Notes", + "columnsFrom": ["likedId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "LysandObject": { + "name": "LysandObject", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "uuid_generate_v7()" + }, + "remote_id": { + "name": "remote_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "uri": { + "name": "uri", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "authorId": { + "name": "authorId", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "extra_data": { + "name": "extra_data", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "extensions": { + "name": "extensions", + "type": "jsonb", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "LysandObject_remote_id_index": { + "name": "LysandObject_remote_id_index", + "columns": ["remote_id"], + "isUnique": true + }, + "LysandObject_uri_index": { + "name": "LysandObject_uri_index", + "columns": ["uri"], + "isUnique": true + } + }, + "foreignKeys": { + "LysandObject_authorId_LysandObject_id_fk": { + "name": "LysandObject_authorId_LysandObject_id_fk", + "tableFrom": "LysandObject", + "tableTo": "LysandObject", + "columnsFrom": ["authorId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "ModNotes": { + "name": "ModNotes", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "uuid_generate_v7()" + }, + "noteId": { + "name": "noteId", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "userId": { + "name": "userId", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "modId": { + "name": "modId", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "note": { + "name": "note", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "ModNotes_noteId_Notes_id_fk": { + "name": "ModNotes_noteId_Notes_id_fk", + "tableFrom": "ModNotes", + "tableTo": "Notes", + "columnsFrom": ["noteId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "ModNotes_userId_Users_id_fk": { + "name": "ModNotes_userId_Users_id_fk", + "tableFrom": "ModNotes", + "tableTo": "Users", + "columnsFrom": ["userId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "ModNotes_modId_Users_id_fk": { + "name": "ModNotes_modId_Users_id_fk", + "tableFrom": "ModNotes", + "tableTo": "Users", + "columnsFrom": ["modId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "ModTags": { + "name": "ModTags", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "uuid_generate_v7()" + }, + "statusId": { + "name": "statusId", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "userId": { + "name": "userId", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "modId": { + "name": "modId", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "tag": { + "name": "tag", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "ModTags_statusId_Notes_id_fk": { + "name": "ModTags_statusId_Notes_id_fk", + "tableFrom": "ModTags", + "tableTo": "Notes", + "columnsFrom": ["statusId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "ModTags_userId_Users_id_fk": { + "name": "ModTags_userId_Users_id_fk", + "tableFrom": "ModTags", + "tableTo": "Users", + "columnsFrom": ["userId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "ModTags_modId_Users_id_fk": { + "name": "ModTags_modId_Users_id_fk", + "tableFrom": "ModTags", + "tableTo": "Users", + "columnsFrom": ["modId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "Notifications": { + "name": "Notifications", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "uuid_generate_v7()" + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "notifiedId": { + "name": "notifiedId", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "accountId": { + "name": "accountId", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "noteId": { + "name": "noteId", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "dismissed": { + "name": "dismissed", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + } + }, + "indexes": {}, + "foreignKeys": { + "Notifications_notifiedId_Users_id_fk": { + "name": "Notifications_notifiedId_Users_id_fk", + "tableFrom": "Notifications", + "tableTo": "Users", + "columnsFrom": ["notifiedId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "Notifications_accountId_Users_id_fk": { + "name": "Notifications_accountId_Users_id_fk", + "tableFrom": "Notifications", + "tableTo": "Users", + "columnsFrom": ["accountId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "Notifications_noteId_Notes_id_fk": { + "name": "Notifications_noteId_Notes_id_fk", + "tableFrom": "Notifications", + "tableTo": "Notes", + "columnsFrom": ["noteId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "OpenIdAccounts": { + "name": "OpenIdAccounts", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "uuid_generate_v7()" + }, + "userId": { + "name": "userId", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "server_id": { + "name": "server_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "issuer_id": { + "name": "issuer_id", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "OpenIdAccounts_userId_Users_id_fk": { + "name": "OpenIdAccounts_userId_Users_id_fk", + "tableFrom": "OpenIdAccounts", + "tableTo": "Users", + "columnsFrom": ["userId"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "OpenIdLoginFlows": { + "name": "OpenIdLoginFlows", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "uuid_generate_v7()" + }, + "code_verifier": { + "name": "code_verifier", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "applicationId": { + "name": "applicationId", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "issuer_id": { + "name": "issuer_id", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "OpenIdLoginFlows_applicationId_Applications_id_fk": { + "name": "OpenIdLoginFlows_applicationId_Applications_id_fk", + "tableFrom": "OpenIdLoginFlows", + "tableTo": "Applications", + "columnsFrom": ["applicationId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "Relationships": { + "name": "Relationships", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "uuid_generate_v7()" + }, + "ownerId": { + "name": "ownerId", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "subjectId": { + "name": "subjectId", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "following": { + "name": "following", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "showing_reblogs": { + "name": "showing_reblogs", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "notifying": { + "name": "notifying", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "followed_by": { + "name": "followed_by", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "blocking": { + "name": "blocking", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "blocked_by": { + "name": "blocked_by", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "muting": { + "name": "muting", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "muting_notifications": { + "name": "muting_notifications", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "requested": { + "name": "requested", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "domain_blocking": { + "name": "domain_blocking", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "endorsed": { + "name": "endorsed", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "languages": { + "name": "languages", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "note": { + "name": "note", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "Relationships_ownerId_Users_id_fk": { + "name": "Relationships_ownerId_Users_id_fk", + "tableFrom": "Relationships", + "tableTo": "Users", + "columnsFrom": ["ownerId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "Relationships_subjectId_Users_id_fk": { + "name": "Relationships_subjectId_Users_id_fk", + "tableFrom": "Relationships", + "tableTo": "Users", + "columnsFrom": ["subjectId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "Notes": { + "name": "Notes", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "uuid_generate_v7()" + }, + "uri": { + "name": "uri", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "authorId": { + "name": "authorId", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updatedAt": { + "name": "updatedAt", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "reblogId": { + "name": "reblogId", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "content": { + "name": "content", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "content_type": { + "name": "content_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'text/plain'" + }, + "visibility": { + "name": "visibility", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "replyId": { + "name": "replyId", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "quoteId": { + "name": "quoteId", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "sensitive": { + "name": "sensitive", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "spoiler_text": { + "name": "spoiler_text", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "applicationId": { + "name": "applicationId", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "content_source": { + "name": "content_source", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + } + }, + "indexes": { + "Notes_uri_index": { + "name": "Notes_uri_index", + "columns": ["uri"], + "isUnique": true + } + }, + "foreignKeys": { + "Notes_authorId_Users_id_fk": { + "name": "Notes_authorId_Users_id_fk", + "tableFrom": "Notes", + "tableTo": "Users", + "columnsFrom": ["authorId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "Notes_applicationId_Applications_id_fk": { + "name": "Notes_applicationId_Applications_id_fk", + "tableFrom": "Notes", + "tableTo": "Applications", + "columnsFrom": ["applicationId"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "cascade" + }, + "Notes_reblogId_Notes_id_fk": { + "name": "Notes_reblogId_Notes_id_fk", + "tableFrom": "Notes", + "tableTo": "Notes", + "columnsFrom": ["reblogId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "Notes_replyId_Notes_id_fk": { + "name": "Notes_replyId_Notes_id_fk", + "tableFrom": "Notes", + "tableTo": "Notes", + "columnsFrom": ["replyId"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "cascade" + }, + "Notes_quoteId_Notes_id_fk": { + "name": "Notes_quoteId_Notes_id_fk", + "tableFrom": "Notes", + "tableTo": "Notes", + "columnsFrom": ["quoteId"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "NoteToMentions": { + "name": "NoteToMentions", + "schema": "", + "columns": { + "noteId": { + "name": "noteId", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "userId": { + "name": "userId", + "type": "uuid", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "NoteToMentions_noteId_userId_index": { + "name": "NoteToMentions_noteId_userId_index", + "columns": ["noteId", "userId"], + "isUnique": true + }, + "NoteToMentions_userId_index": { + "name": "NoteToMentions_userId_index", + "columns": ["userId"], + "isUnique": false + } + }, + "foreignKeys": { + "NoteToMentions_noteId_Notes_id_fk": { + "name": "NoteToMentions_noteId_Notes_id_fk", + "tableFrom": "NoteToMentions", + "tableTo": "Notes", + "columnsFrom": ["noteId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "NoteToMentions_userId_Users_id_fk": { + "name": "NoteToMentions_userId_Users_id_fk", + "tableFrom": "NoteToMentions", + "tableTo": "Users", + "columnsFrom": ["userId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "Tokens": { + "name": "Tokens", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "uuid_generate_v7()" + }, + "token_type": { + "name": "token_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "scope": { + "name": "scope", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "access_token": { + "name": "access_token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "code": { + "name": "code", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "userId": { + "name": "userId", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "applicationId": { + "name": "applicationId", + "type": "uuid", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "Tokens_userId_Users_id_fk": { + "name": "Tokens_userId_Users_id_fk", + "tableFrom": "Tokens", + "tableTo": "Users", + "columnsFrom": ["userId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "Tokens_applicationId_Applications_id_fk": { + "name": "Tokens_applicationId_Applications_id_fk", + "tableFrom": "Tokens", + "tableTo": "Applications", + "columnsFrom": ["applicationId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "Users": { + "name": "Users", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "uuid_generate_v7()" + }, + "uri": { + "name": "uri", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "display_name": { + "name": "display_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "note": { + "name": "note", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "is_admin": { + "name": "is_admin", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "endpoints": { + "name": "endpoints", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "source": { + "name": "source", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "avatar": { + "name": "avatar", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "header": { + "name": "header", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "is_bot": { + "name": "is_bot", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "is_locked": { + "name": "is_locked", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "is_discoverable": { + "name": "is_discoverable", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "sanctions": { + "name": "sanctions", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "public_key": { + "name": "public_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "private_key": { + "name": "private_key", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "instanceId": { + "name": "instanceId", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "disable_automoderation": { + "name": "disable_automoderation", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + } + }, + "indexes": { + "Users_uri_index": { + "name": "Users_uri_index", + "columns": ["uri"], + "isUnique": true + }, + "Users_username_index": { + "name": "Users_username_index", + "columns": ["username"], + "isUnique": true + }, + "Users_email_index": { + "name": "Users_email_index", + "columns": ["email"], + "isUnique": true + } + }, + "foreignKeys": { + "Users_instanceId_Instances_id_fk": { + "name": "Users_instanceId_Instances_id_fk", + "tableFrom": "Users", + "tableTo": "Instances", + "columnsFrom": ["instanceId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "UserToPinnedNotes": { + "name": "UserToPinnedNotes", + "schema": "", + "columns": { + "userId": { + "name": "userId", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "noteId": { + "name": "noteId", + "type": "uuid", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "UserToPinnedNotes_userId_noteId_index": { + "name": "UserToPinnedNotes_userId_noteId_index", + "columns": ["userId", "noteId"], + "isUnique": true + }, + "UserToPinnedNotes_noteId_index": { + "name": "UserToPinnedNotes_noteId_index", + "columns": ["noteId"], + "isUnique": false + } + }, + "foreignKeys": { + "UserToPinnedNotes_userId_Notes_id_fk": { + "name": "UserToPinnedNotes_userId_Notes_id_fk", + "tableFrom": "UserToPinnedNotes", + "tableTo": "Notes", + "columnsFrom": ["userId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "UserToPinnedNotes_noteId_Users_id_fk": { + "name": "UserToPinnedNotes_noteId_Users_id_fk", + "tableFrom": "UserToPinnedNotes", + "tableTo": "Users", + "columnsFrom": ["noteId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + } + }, + "enums": {}, + "schemas": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} diff --git a/drizzle/meta/0010_snapshot.json b/drizzle/meta/0010_snapshot.json new file mode 100644 index 00000000..96bf2837 --- /dev/null +++ b/drizzle/meta/0010_snapshot.json @@ -0,0 +1,1582 @@ +{ + "id": "bed9861c-61a1-4421-a0f3-469977e2c11d", + "prevId": "8d1c9139-3ed4-46ad-b0ca-4d972d1e5d3e", + "version": "5", + "dialect": "pg", + "tables": { + "Applications": { + "name": "Applications", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "uuid_generate_v7()" + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "website": { + "name": "website", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "vapid_key": { + "name": "vapid_key", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "client_id": { + "name": "client_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "secret": { + "name": "secret", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "scopes": { + "name": "scopes", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "redirect_uris": { + "name": "redirect_uris", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "Applications_client_id_index": { + "name": "Applications_client_id_index", + "columns": ["client_id"], + "isUnique": true + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "Attachments": { + "name": "Attachments", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "uuid_generate_v7()" + }, + "url": { + "name": "url", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "remote_url": { + "name": "remote_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "thumbnail_url": { + "name": "thumbnail_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mime_type": { + "name": "mime_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "blurhash": { + "name": "blurhash", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "sha256": { + "name": "sha256", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "fps": { + "name": "fps", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "duration": { + "name": "duration", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "width": { + "name": "width", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "height": { + "name": "height", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "size": { + "name": "size", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "noteId": { + "name": "noteId", + "type": "uuid", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "Attachments_noteId_Notes_id_fk": { + "name": "Attachments_noteId_Notes_id_fk", + "tableFrom": "Attachments", + "tableTo": "Notes", + "columnsFrom": ["noteId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "Emojis": { + "name": "Emojis", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "uuid_generate_v7()" + }, + "shortcode": { + "name": "shortcode", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "url": { + "name": "url", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "visible_in_picker": { + "name": "visible_in_picker", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "alt": { + "name": "alt", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "content_type": { + "name": "content_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "instanceId": { + "name": "instanceId", + "type": "uuid", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "Emojis_instanceId_Instances_id_fk": { + "name": "Emojis_instanceId_Instances_id_fk", + "tableFrom": "Emojis", + "tableTo": "Instances", + "columnsFrom": ["instanceId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "EmojiToNote": { + "name": "EmojiToNote", + "schema": "", + "columns": { + "emojiId": { + "name": "emojiId", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "noteId": { + "name": "noteId", + "type": "uuid", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "EmojiToNote_emojiId_noteId_index": { + "name": "EmojiToNote_emojiId_noteId_index", + "columns": ["emojiId", "noteId"], + "isUnique": true + }, + "EmojiToNote_noteId_index": { + "name": "EmojiToNote_noteId_index", + "columns": ["noteId"], + "isUnique": false + } + }, + "foreignKeys": { + "EmojiToNote_emojiId_Emojis_id_fk": { + "name": "EmojiToNote_emojiId_Emojis_id_fk", + "tableFrom": "EmojiToNote", + "tableTo": "Emojis", + "columnsFrom": ["emojiId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "EmojiToNote_noteId_Notes_id_fk": { + "name": "EmojiToNote_noteId_Notes_id_fk", + "tableFrom": "EmojiToNote", + "tableTo": "Notes", + "columnsFrom": ["noteId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "EmojiToUser": { + "name": "EmojiToUser", + "schema": "", + "columns": { + "emojiId": { + "name": "emojiId", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "userId": { + "name": "userId", + "type": "uuid", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "EmojiToUser_emojiId_userId_index": { + "name": "EmojiToUser_emojiId_userId_index", + "columns": ["emojiId", "userId"], + "isUnique": true + }, + "EmojiToUser_userId_index": { + "name": "EmojiToUser_userId_index", + "columns": ["userId"], + "isUnique": false + } + }, + "foreignKeys": { + "EmojiToUser_emojiId_Emojis_id_fk": { + "name": "EmojiToUser_emojiId_Emojis_id_fk", + "tableFrom": "EmojiToUser", + "tableTo": "Emojis", + "columnsFrom": ["emojiId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "EmojiToUser_userId_Users_id_fk": { + "name": "EmojiToUser_userId_Users_id_fk", + "tableFrom": "EmojiToUser", + "tableTo": "Users", + "columnsFrom": ["userId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "Flags": { + "name": "Flags", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "uuid_generate_v7()" + }, + "flag_type": { + "name": "flag_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'other'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "noteId": { + "name": "noteId", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "userId": { + "name": "userId", + "type": "uuid", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "Flags_noteId_Notes_id_fk": { + "name": "Flags_noteId_Notes_id_fk", + "tableFrom": "Flags", + "tableTo": "Notes", + "columnsFrom": ["noteId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "Flags_userId_Users_id_fk": { + "name": "Flags_userId_Users_id_fk", + "tableFrom": "Flags", + "tableTo": "Users", + "columnsFrom": ["userId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "Instances": { + "name": "Instances", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "uuid_generate_v7()" + }, + "base_url": { + "name": "base_url", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "version": { + "name": "version", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "logo": { + "name": "logo", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "disable_automoderation": { + "name": "disable_automoderation", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "Likes": { + "name": "Likes", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "uuid_generate_v7()" + }, + "likerId": { + "name": "likerId", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "likedId": { + "name": "likedId", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "Likes_likerId_Users_id_fk": { + "name": "Likes_likerId_Users_id_fk", + "tableFrom": "Likes", + "tableTo": "Users", + "columnsFrom": ["likerId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "Likes_likedId_Notes_id_fk": { + "name": "Likes_likedId_Notes_id_fk", + "tableFrom": "Likes", + "tableTo": "Notes", + "columnsFrom": ["likedId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "LysandObject": { + "name": "LysandObject", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "uuid_generate_v7()" + }, + "remote_id": { + "name": "remote_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "uri": { + "name": "uri", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "authorId": { + "name": "authorId", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "extra_data": { + "name": "extra_data", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "extensions": { + "name": "extensions", + "type": "jsonb", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "LysandObject_remote_id_index": { + "name": "LysandObject_remote_id_index", + "columns": ["remote_id"], + "isUnique": true + }, + "LysandObject_uri_index": { + "name": "LysandObject_uri_index", + "columns": ["uri"], + "isUnique": true + } + }, + "foreignKeys": { + "LysandObject_authorId_LysandObject_id_fk": { + "name": "LysandObject_authorId_LysandObject_id_fk", + "tableFrom": "LysandObject", + "tableTo": "LysandObject", + "columnsFrom": ["authorId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "ModNotes": { + "name": "ModNotes", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "uuid_generate_v7()" + }, + "noteId": { + "name": "noteId", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "userId": { + "name": "userId", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "modId": { + "name": "modId", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "note": { + "name": "note", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "ModNotes_noteId_Notes_id_fk": { + "name": "ModNotes_noteId_Notes_id_fk", + "tableFrom": "ModNotes", + "tableTo": "Notes", + "columnsFrom": ["noteId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "ModNotes_userId_Users_id_fk": { + "name": "ModNotes_userId_Users_id_fk", + "tableFrom": "ModNotes", + "tableTo": "Users", + "columnsFrom": ["userId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "ModNotes_modId_Users_id_fk": { + "name": "ModNotes_modId_Users_id_fk", + "tableFrom": "ModNotes", + "tableTo": "Users", + "columnsFrom": ["modId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "ModTags": { + "name": "ModTags", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "uuid_generate_v7()" + }, + "noteId": { + "name": "noteId", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "userId": { + "name": "userId", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "modId": { + "name": "modId", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "tag": { + "name": "tag", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "ModTags_noteId_Notes_id_fk": { + "name": "ModTags_noteId_Notes_id_fk", + "tableFrom": "ModTags", + "tableTo": "Notes", + "columnsFrom": ["noteId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "ModTags_userId_Users_id_fk": { + "name": "ModTags_userId_Users_id_fk", + "tableFrom": "ModTags", + "tableTo": "Users", + "columnsFrom": ["userId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "ModTags_modId_Users_id_fk": { + "name": "ModTags_modId_Users_id_fk", + "tableFrom": "ModTags", + "tableTo": "Users", + "columnsFrom": ["modId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "Notifications": { + "name": "Notifications", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "uuid_generate_v7()" + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "notifiedId": { + "name": "notifiedId", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "accountId": { + "name": "accountId", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "noteId": { + "name": "noteId", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "dismissed": { + "name": "dismissed", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + } + }, + "indexes": {}, + "foreignKeys": { + "Notifications_notifiedId_Users_id_fk": { + "name": "Notifications_notifiedId_Users_id_fk", + "tableFrom": "Notifications", + "tableTo": "Users", + "columnsFrom": ["notifiedId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "Notifications_accountId_Users_id_fk": { + "name": "Notifications_accountId_Users_id_fk", + "tableFrom": "Notifications", + "tableTo": "Users", + "columnsFrom": ["accountId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "Notifications_noteId_Notes_id_fk": { + "name": "Notifications_noteId_Notes_id_fk", + "tableFrom": "Notifications", + "tableTo": "Notes", + "columnsFrom": ["noteId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "OpenIdAccounts": { + "name": "OpenIdAccounts", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "uuid_generate_v7()" + }, + "userId": { + "name": "userId", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "server_id": { + "name": "server_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "issuer_id": { + "name": "issuer_id", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "OpenIdAccounts_userId_Users_id_fk": { + "name": "OpenIdAccounts_userId_Users_id_fk", + "tableFrom": "OpenIdAccounts", + "tableTo": "Users", + "columnsFrom": ["userId"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "OpenIdLoginFlows": { + "name": "OpenIdLoginFlows", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "uuid_generate_v7()" + }, + "code_verifier": { + "name": "code_verifier", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "applicationId": { + "name": "applicationId", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "issuer_id": { + "name": "issuer_id", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "OpenIdLoginFlows_applicationId_Applications_id_fk": { + "name": "OpenIdLoginFlows_applicationId_Applications_id_fk", + "tableFrom": "OpenIdLoginFlows", + "tableTo": "Applications", + "columnsFrom": ["applicationId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "Relationships": { + "name": "Relationships", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "uuid_generate_v7()" + }, + "ownerId": { + "name": "ownerId", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "subjectId": { + "name": "subjectId", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "following": { + "name": "following", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "showing_reblogs": { + "name": "showing_reblogs", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "notifying": { + "name": "notifying", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "followed_by": { + "name": "followed_by", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "blocking": { + "name": "blocking", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "blocked_by": { + "name": "blocked_by", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "muting": { + "name": "muting", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "muting_notifications": { + "name": "muting_notifications", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "requested": { + "name": "requested", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "domain_blocking": { + "name": "domain_blocking", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "endorsed": { + "name": "endorsed", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "languages": { + "name": "languages", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "note": { + "name": "note", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "Relationships_ownerId_Users_id_fk": { + "name": "Relationships_ownerId_Users_id_fk", + "tableFrom": "Relationships", + "tableTo": "Users", + "columnsFrom": ["ownerId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "Relationships_subjectId_Users_id_fk": { + "name": "Relationships_subjectId_Users_id_fk", + "tableFrom": "Relationships", + "tableTo": "Users", + "columnsFrom": ["subjectId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "Notes": { + "name": "Notes", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "uuid_generate_v7()" + }, + "uri": { + "name": "uri", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "authorId": { + "name": "authorId", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updatedAt": { + "name": "updatedAt", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "reblogId": { + "name": "reblogId", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "content": { + "name": "content", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "content_type": { + "name": "content_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'text/plain'" + }, + "visibility": { + "name": "visibility", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "replyId": { + "name": "replyId", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "quoteId": { + "name": "quoteId", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "sensitive": { + "name": "sensitive", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "spoiler_text": { + "name": "spoiler_text", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "applicationId": { + "name": "applicationId", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "content_source": { + "name": "content_source", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + } + }, + "indexes": { + "Notes_uri_index": { + "name": "Notes_uri_index", + "columns": ["uri"], + "isUnique": true + } + }, + "foreignKeys": { + "Notes_authorId_Users_id_fk": { + "name": "Notes_authorId_Users_id_fk", + "tableFrom": "Notes", + "tableTo": "Users", + "columnsFrom": ["authorId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "Notes_applicationId_Applications_id_fk": { + "name": "Notes_applicationId_Applications_id_fk", + "tableFrom": "Notes", + "tableTo": "Applications", + "columnsFrom": ["applicationId"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "cascade" + }, + "Notes_reblogId_Notes_id_fk": { + "name": "Notes_reblogId_Notes_id_fk", + "tableFrom": "Notes", + "tableTo": "Notes", + "columnsFrom": ["reblogId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "Notes_replyId_Notes_id_fk": { + "name": "Notes_replyId_Notes_id_fk", + "tableFrom": "Notes", + "tableTo": "Notes", + "columnsFrom": ["replyId"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "cascade" + }, + "Notes_quoteId_Notes_id_fk": { + "name": "Notes_quoteId_Notes_id_fk", + "tableFrom": "Notes", + "tableTo": "Notes", + "columnsFrom": ["quoteId"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "NoteToMentions": { + "name": "NoteToMentions", + "schema": "", + "columns": { + "noteId": { + "name": "noteId", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "userId": { + "name": "userId", + "type": "uuid", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "NoteToMentions_noteId_userId_index": { + "name": "NoteToMentions_noteId_userId_index", + "columns": ["noteId", "userId"], + "isUnique": true + }, + "NoteToMentions_userId_index": { + "name": "NoteToMentions_userId_index", + "columns": ["userId"], + "isUnique": false + } + }, + "foreignKeys": { + "NoteToMentions_noteId_Notes_id_fk": { + "name": "NoteToMentions_noteId_Notes_id_fk", + "tableFrom": "NoteToMentions", + "tableTo": "Notes", + "columnsFrom": ["noteId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "NoteToMentions_userId_Users_id_fk": { + "name": "NoteToMentions_userId_Users_id_fk", + "tableFrom": "NoteToMentions", + "tableTo": "Users", + "columnsFrom": ["userId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "Tokens": { + "name": "Tokens", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "uuid_generate_v7()" + }, + "token_type": { + "name": "token_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "scope": { + "name": "scope", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "access_token": { + "name": "access_token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "code": { + "name": "code", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "userId": { + "name": "userId", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "applicationId": { + "name": "applicationId", + "type": "uuid", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "Tokens_userId_Users_id_fk": { + "name": "Tokens_userId_Users_id_fk", + "tableFrom": "Tokens", + "tableTo": "Users", + "columnsFrom": ["userId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "Tokens_applicationId_Applications_id_fk": { + "name": "Tokens_applicationId_Applications_id_fk", + "tableFrom": "Tokens", + "tableTo": "Applications", + "columnsFrom": ["applicationId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "Users": { + "name": "Users", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "uuid_generate_v7()" + }, + "uri": { + "name": "uri", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "display_name": { + "name": "display_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "note": { + "name": "note", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "is_admin": { + "name": "is_admin", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "endpoints": { + "name": "endpoints", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "source": { + "name": "source", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "avatar": { + "name": "avatar", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "header": { + "name": "header", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "is_bot": { + "name": "is_bot", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "is_locked": { + "name": "is_locked", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "is_discoverable": { + "name": "is_discoverable", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "sanctions": { + "name": "sanctions", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "public_key": { + "name": "public_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "private_key": { + "name": "private_key", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "instanceId": { + "name": "instanceId", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "disable_automoderation": { + "name": "disable_automoderation", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + } + }, + "indexes": { + "Users_uri_index": { + "name": "Users_uri_index", + "columns": ["uri"], + "isUnique": true + }, + "Users_username_index": { + "name": "Users_username_index", + "columns": ["username"], + "isUnique": true + }, + "Users_email_index": { + "name": "Users_email_index", + "columns": ["email"], + "isUnique": true + } + }, + "foreignKeys": { + "Users_instanceId_Instances_id_fk": { + "name": "Users_instanceId_Instances_id_fk", + "tableFrom": "Users", + "tableTo": "Instances", + "columnsFrom": ["instanceId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "UserToPinnedNotes": { + "name": "UserToPinnedNotes", + "schema": "", + "columns": { + "userId": { + "name": "userId", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "noteId": { + "name": "noteId", + "type": "uuid", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "UserToPinnedNotes_userId_noteId_index": { + "name": "UserToPinnedNotes_userId_noteId_index", + "columns": ["userId", "noteId"], + "isUnique": true + }, + "UserToPinnedNotes_noteId_index": { + "name": "UserToPinnedNotes_noteId_index", + "columns": ["noteId"], + "isUnique": false + } + }, + "foreignKeys": { + "UserToPinnedNotes_userId_Notes_id_fk": { + "name": "UserToPinnedNotes_userId_Notes_id_fk", + "tableFrom": "UserToPinnedNotes", + "tableTo": "Notes", + "columnsFrom": ["userId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "UserToPinnedNotes_noteId_Users_id_fk": { + "name": "UserToPinnedNotes_noteId_Users_id_fk", + "tableFrom": "UserToPinnedNotes", + "tableTo": "Users", + "columnsFrom": ["noteId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + } + }, + "enums": {}, + "schemas": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} diff --git a/drizzle/meta/0011_snapshot.json b/drizzle/meta/0011_snapshot.json new file mode 100644 index 00000000..cd0e18b2 --- /dev/null +++ b/drizzle/meta/0011_snapshot.json @@ -0,0 +1,1760 @@ +{ + "id": "bee93a52-c8c3-4e4e-a342-e350e8357e38", + "prevId": "bed9861c-61a1-4421-a0f3-469977e2c11d", + "version": "5", + "dialect": "pg", + "tables": { + "Applications": { + "name": "Applications", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "uuid_generate_v7()" + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "website": { + "name": "website", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "vapid_key": { + "name": "vapid_key", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "client_id": { + "name": "client_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "secret": { + "name": "secret", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "scopes": { + "name": "scopes", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "redirect_uris": { + "name": "redirect_uris", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "Applications_client_id_index": { + "name": "Applications_client_id_index", + "columns": [ + "client_id" + ], + "isUnique": true + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "Attachments": { + "name": "Attachments", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "uuid_generate_v7()" + }, + "url": { + "name": "url", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "remote_url": { + "name": "remote_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "thumbnail_url": { + "name": "thumbnail_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mime_type": { + "name": "mime_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "blurhash": { + "name": "blurhash", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "sha256": { + "name": "sha256", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "fps": { + "name": "fps", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "duration": { + "name": "duration", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "width": { + "name": "width", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "height": { + "name": "height", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "size": { + "name": "size", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "noteId": { + "name": "noteId", + "type": "uuid", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "Attachments_noteId_Notes_id_fk": { + "name": "Attachments_noteId_Notes_id_fk", + "tableFrom": "Attachments", + "tableTo": "Notes", + "columnsFrom": [ + "noteId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "EmojiToNote": { + "name": "EmojiToNote", + "schema": "", + "columns": { + "emojiId": { + "name": "emojiId", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "noteId": { + "name": "noteId", + "type": "uuid", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "EmojiToNote_emojiId_noteId_index": { + "name": "EmojiToNote_emojiId_noteId_index", + "columns": [ + "emojiId", + "noteId" + ], + "isUnique": true + }, + "EmojiToNote_noteId_index": { + "name": "EmojiToNote_noteId_index", + "columns": [ + "noteId" + ], + "isUnique": false + } + }, + "foreignKeys": { + "EmojiToNote_emojiId_Emojis_id_fk": { + "name": "EmojiToNote_emojiId_Emojis_id_fk", + "tableFrom": "EmojiToNote", + "tableTo": "Emojis", + "columnsFrom": [ + "emojiId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "EmojiToNote_noteId_Notes_id_fk": { + "name": "EmojiToNote_noteId_Notes_id_fk", + "tableFrom": "EmojiToNote", + "tableTo": "Notes", + "columnsFrom": [ + "noteId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "EmojiToUser": { + "name": "EmojiToUser", + "schema": "", + "columns": { + "emojiId": { + "name": "emojiId", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "userId": { + "name": "userId", + "type": "uuid", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "EmojiToUser_emojiId_userId_index": { + "name": "EmojiToUser_emojiId_userId_index", + "columns": [ + "emojiId", + "userId" + ], + "isUnique": true + }, + "EmojiToUser_userId_index": { + "name": "EmojiToUser_userId_index", + "columns": [ + "userId" + ], + "isUnique": false + } + }, + "foreignKeys": { + "EmojiToUser_emojiId_Emojis_id_fk": { + "name": "EmojiToUser_emojiId_Emojis_id_fk", + "tableFrom": "EmojiToUser", + "tableTo": "Emojis", + "columnsFrom": [ + "emojiId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "EmojiToUser_userId_Users_id_fk": { + "name": "EmojiToUser_userId_Users_id_fk", + "tableFrom": "EmojiToUser", + "tableTo": "Users", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "Emojis": { + "name": "Emojis", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "uuid_generate_v7()" + }, + "shortcode": { + "name": "shortcode", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "url": { + "name": "url", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "visible_in_picker": { + "name": "visible_in_picker", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "alt": { + "name": "alt", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "content_type": { + "name": "content_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "instanceId": { + "name": "instanceId", + "type": "uuid", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "Emojis_instanceId_Instances_id_fk": { + "name": "Emojis_instanceId_Instances_id_fk", + "tableFrom": "Emojis", + "tableTo": "Instances", + "columnsFrom": [ + "instanceId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "Flags": { + "name": "Flags", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "uuid_generate_v7()" + }, + "flag_type": { + "name": "flag_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'other'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "noteId": { + "name": "noteId", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "userId": { + "name": "userId", + "type": "uuid", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "Flags_noteId_Notes_id_fk": { + "name": "Flags_noteId_Notes_id_fk", + "tableFrom": "Flags", + "tableTo": "Notes", + "columnsFrom": [ + "noteId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "Flags_userId_Users_id_fk": { + "name": "Flags_userId_Users_id_fk", + "tableFrom": "Flags", + "tableTo": "Users", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "Instances": { + "name": "Instances", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "uuid_generate_v7()" + }, + "base_url": { + "name": "base_url", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "version": { + "name": "version", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "logo": { + "name": "logo", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "disable_automoderation": { + "name": "disable_automoderation", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "Likes": { + "name": "Likes", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "uuid_generate_v7()" + }, + "likerId": { + "name": "likerId", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "likedId": { + "name": "likedId", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "Likes_likerId_Users_id_fk": { + "name": "Likes_likerId_Users_id_fk", + "tableFrom": "Likes", + "tableTo": "Users", + "columnsFrom": [ + "likerId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "Likes_likedId_Notes_id_fk": { + "name": "Likes_likedId_Notes_id_fk", + "tableFrom": "Likes", + "tableTo": "Notes", + "columnsFrom": [ + "likedId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "LysandObject": { + "name": "LysandObject", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "uuid_generate_v7()" + }, + "remote_id": { + "name": "remote_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "uri": { + "name": "uri", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "authorId": { + "name": "authorId", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "extra_data": { + "name": "extra_data", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "extensions": { + "name": "extensions", + "type": "jsonb", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "LysandObject_remote_id_index": { + "name": "LysandObject_remote_id_index", + "columns": [ + "remote_id" + ], + "isUnique": true + }, + "LysandObject_uri_index": { + "name": "LysandObject_uri_index", + "columns": [ + "uri" + ], + "isUnique": true + } + }, + "foreignKeys": { + "LysandObject_authorId_LysandObject_id_fk": { + "name": "LysandObject_authorId_LysandObject_id_fk", + "tableFrom": "LysandObject", + "tableTo": "LysandObject", + "columnsFrom": [ + "authorId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "ModNotes": { + "name": "ModNotes", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "uuid_generate_v7()" + }, + "noteId": { + "name": "noteId", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "userId": { + "name": "userId", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "modId": { + "name": "modId", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "note": { + "name": "note", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "ModNotes_noteId_Notes_id_fk": { + "name": "ModNotes_noteId_Notes_id_fk", + "tableFrom": "ModNotes", + "tableTo": "Notes", + "columnsFrom": [ + "noteId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "ModNotes_userId_Users_id_fk": { + "name": "ModNotes_userId_Users_id_fk", + "tableFrom": "ModNotes", + "tableTo": "Users", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "ModNotes_modId_Users_id_fk": { + "name": "ModNotes_modId_Users_id_fk", + "tableFrom": "ModNotes", + "tableTo": "Users", + "columnsFrom": [ + "modId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "ModTags": { + "name": "ModTags", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "uuid_generate_v7()" + }, + "noteId": { + "name": "noteId", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "userId": { + "name": "userId", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "modId": { + "name": "modId", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "tag": { + "name": "tag", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "ModTags_noteId_Notes_id_fk": { + "name": "ModTags_noteId_Notes_id_fk", + "tableFrom": "ModTags", + "tableTo": "Notes", + "columnsFrom": [ + "noteId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "ModTags_userId_Users_id_fk": { + "name": "ModTags_userId_Users_id_fk", + "tableFrom": "ModTags", + "tableTo": "Users", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "ModTags_modId_Users_id_fk": { + "name": "ModTags_modId_Users_id_fk", + "tableFrom": "ModTags", + "tableTo": "Users", + "columnsFrom": [ + "modId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "NoteToMentions": { + "name": "NoteToMentions", + "schema": "", + "columns": { + "noteId": { + "name": "noteId", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "userId": { + "name": "userId", + "type": "uuid", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "NoteToMentions_noteId_userId_index": { + "name": "NoteToMentions_noteId_userId_index", + "columns": [ + "noteId", + "userId" + ], + "isUnique": true + }, + "NoteToMentions_userId_index": { + "name": "NoteToMentions_userId_index", + "columns": [ + "userId" + ], + "isUnique": false + } + }, + "foreignKeys": { + "NoteToMentions_noteId_Notes_id_fk": { + "name": "NoteToMentions_noteId_Notes_id_fk", + "tableFrom": "NoteToMentions", + "tableTo": "Notes", + "columnsFrom": [ + "noteId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "NoteToMentions_userId_Users_id_fk": { + "name": "NoteToMentions_userId_Users_id_fk", + "tableFrom": "NoteToMentions", + "tableTo": "Users", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "Notes": { + "name": "Notes", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "uuid_generate_v7()" + }, + "uri": { + "name": "uri", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "authorId": { + "name": "authorId", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updatedAt": { + "name": "updatedAt", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "reblogId": { + "name": "reblogId", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "content": { + "name": "content", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "content_type": { + "name": "content_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'text/plain'" + }, + "visibility": { + "name": "visibility", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "replyId": { + "name": "replyId", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "quoteId": { + "name": "quoteId", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "sensitive": { + "name": "sensitive", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "spoiler_text": { + "name": "spoiler_text", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "applicationId": { + "name": "applicationId", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "content_source": { + "name": "content_source", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + } + }, + "indexes": { + "Notes_uri_index": { + "name": "Notes_uri_index", + "columns": [ + "uri" + ], + "isUnique": true + } + }, + "foreignKeys": { + "Notes_authorId_Users_id_fk": { + "name": "Notes_authorId_Users_id_fk", + "tableFrom": "Notes", + "tableTo": "Users", + "columnsFrom": [ + "authorId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "Notes_applicationId_Applications_id_fk": { + "name": "Notes_applicationId_Applications_id_fk", + "tableFrom": "Notes", + "tableTo": "Applications", + "columnsFrom": [ + "applicationId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "cascade" + }, + "Notes_reblogId_Notes_id_fk": { + "name": "Notes_reblogId_Notes_id_fk", + "tableFrom": "Notes", + "tableTo": "Notes", + "columnsFrom": [ + "reblogId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "Notes_replyId_Notes_id_fk": { + "name": "Notes_replyId_Notes_id_fk", + "tableFrom": "Notes", + "tableTo": "Notes", + "columnsFrom": [ + "replyId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "cascade" + }, + "Notes_quoteId_Notes_id_fk": { + "name": "Notes_quoteId_Notes_id_fk", + "tableFrom": "Notes", + "tableTo": "Notes", + "columnsFrom": [ + "quoteId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "Notifications": { + "name": "Notifications", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "uuid_generate_v7()" + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "notifiedId": { + "name": "notifiedId", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "accountId": { + "name": "accountId", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "noteId": { + "name": "noteId", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "dismissed": { + "name": "dismissed", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + } + }, + "indexes": {}, + "foreignKeys": { + "Notifications_notifiedId_Users_id_fk": { + "name": "Notifications_notifiedId_Users_id_fk", + "tableFrom": "Notifications", + "tableTo": "Users", + "columnsFrom": [ + "notifiedId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "Notifications_accountId_Users_id_fk": { + "name": "Notifications_accountId_Users_id_fk", + "tableFrom": "Notifications", + "tableTo": "Users", + "columnsFrom": [ + "accountId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "Notifications_noteId_Notes_id_fk": { + "name": "Notifications_noteId_Notes_id_fk", + "tableFrom": "Notifications", + "tableTo": "Notes", + "columnsFrom": [ + "noteId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "OpenIdAccounts": { + "name": "OpenIdAccounts", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "uuid_generate_v7()" + }, + "userId": { + "name": "userId", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "server_id": { + "name": "server_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "issuer_id": { + "name": "issuer_id", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "OpenIdAccounts_userId_Users_id_fk": { + "name": "OpenIdAccounts_userId_Users_id_fk", + "tableFrom": "OpenIdAccounts", + "tableTo": "Users", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "OpenIdLoginFlows": { + "name": "OpenIdLoginFlows", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "uuid_generate_v7()" + }, + "code_verifier": { + "name": "code_verifier", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "applicationId": { + "name": "applicationId", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "issuer_id": { + "name": "issuer_id", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "OpenIdLoginFlows_applicationId_Applications_id_fk": { + "name": "OpenIdLoginFlows_applicationId_Applications_id_fk", + "tableFrom": "OpenIdLoginFlows", + "tableTo": "Applications", + "columnsFrom": [ + "applicationId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "Relationships": { + "name": "Relationships", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "uuid_generate_v7()" + }, + "ownerId": { + "name": "ownerId", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "subjectId": { + "name": "subjectId", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "following": { + "name": "following", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "showing_reblogs": { + "name": "showing_reblogs", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "notifying": { + "name": "notifying", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "followed_by": { + "name": "followed_by", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "blocking": { + "name": "blocking", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "blocked_by": { + "name": "blocked_by", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "muting": { + "name": "muting", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "muting_notifications": { + "name": "muting_notifications", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "requested": { + "name": "requested", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "domain_blocking": { + "name": "domain_blocking", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "endorsed": { + "name": "endorsed", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "languages": { + "name": "languages", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "note": { + "name": "note", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "Relationships_ownerId_Users_id_fk": { + "name": "Relationships_ownerId_Users_id_fk", + "tableFrom": "Relationships", + "tableTo": "Users", + "columnsFrom": [ + "ownerId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "Relationships_subjectId_Users_id_fk": { + "name": "Relationships_subjectId_Users_id_fk", + "tableFrom": "Relationships", + "tableTo": "Users", + "columnsFrom": [ + "subjectId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "Tokens": { + "name": "Tokens", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "uuid_generate_v7()" + }, + "token_type": { + "name": "token_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "scope": { + "name": "scope", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "access_token": { + "name": "access_token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "code": { + "name": "code", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "userId": { + "name": "userId", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "applicationId": { + "name": "applicationId", + "type": "uuid", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "Tokens_userId_Users_id_fk": { + "name": "Tokens_userId_Users_id_fk", + "tableFrom": "Tokens", + "tableTo": "Users", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "Tokens_applicationId_Applications_id_fk": { + "name": "Tokens_applicationId_Applications_id_fk", + "tableFrom": "Tokens", + "tableTo": "Applications", + "columnsFrom": [ + "applicationId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "UserToPinnedNotes": { + "name": "UserToPinnedNotes", + "schema": "", + "columns": { + "userId": { + "name": "userId", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "noteId": { + "name": "noteId", + "type": "uuid", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "UserToPinnedNotes_userId_noteId_index": { + "name": "UserToPinnedNotes_userId_noteId_index", + "columns": [ + "userId", + "noteId" + ], + "isUnique": true + }, + "UserToPinnedNotes_noteId_index": { + "name": "UserToPinnedNotes_noteId_index", + "columns": [ + "noteId" + ], + "isUnique": false + } + }, + "foreignKeys": { + "UserToPinnedNotes_userId_Users_id_fk": { + "name": "UserToPinnedNotes_userId_Users_id_fk", + "tableFrom": "UserToPinnedNotes", + "tableTo": "Users", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "UserToPinnedNotes_noteId_Notes_id_fk": { + "name": "UserToPinnedNotes_noteId_Notes_id_fk", + "tableFrom": "UserToPinnedNotes", + "tableTo": "Notes", + "columnsFrom": [ + "noteId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "Users": { + "name": "Users", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "uuid_generate_v7()" + }, + "uri": { + "name": "uri", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "display_name": { + "name": "display_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "note": { + "name": "note", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "is_admin": { + "name": "is_admin", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "endpoints": { + "name": "endpoints", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "source": { + "name": "source", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "avatar": { + "name": "avatar", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "header": { + "name": "header", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "is_bot": { + "name": "is_bot", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "is_locked": { + "name": "is_locked", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "is_discoverable": { + "name": "is_discoverable", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "sanctions": { + "name": "sanctions", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "public_key": { + "name": "public_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "private_key": { + "name": "private_key", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "instanceId": { + "name": "instanceId", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "disable_automoderation": { + "name": "disable_automoderation", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + } + }, + "indexes": { + "Users_uri_index": { + "name": "Users_uri_index", + "columns": [ + "uri" + ], + "isUnique": true + }, + "Users_username_index": { + "name": "Users_username_index", + "columns": [ + "username" + ], + "isUnique": true + }, + "Users_email_index": { + "name": "Users_email_index", + "columns": [ + "email" + ], + "isUnique": true + } + }, + "foreignKeys": { + "Users_instanceId_Instances_id_fk": { + "name": "Users_instanceId_Instances_id_fk", + "tableFrom": "Users", + "tableTo": "Instances", + "columnsFrom": [ + "instanceId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + } + }, + "enums": {}, + "schemas": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/drizzle/meta/_journal.json b/drizzle/meta/_journal.json index cabac106..15ac61fa 100644 --- a/drizzle/meta/_journal.json +++ b/drizzle/meta/_journal.json @@ -1,69 +1,90 @@ { - "version": "5", - "dialect": "pg", - "entries": [ - { - "idx": 0, - "version": "5", - "when": 1712805159664, - "tag": "0000_illegal_living_lightning", - "breakpoints": true - }, - { - "idx": 1, - "version": "5", - "when": 1713055774123, - "tag": "0001_salty_night_thrasher", - "breakpoints": true - }, - { - "idx": 2, - "version": "5", - "when": 1713056370431, - "tag": "0002_stiff_ares", - "breakpoints": true - }, - { - "idx": 3, - "version": "5", - "when": 1713056528340, - "tag": "0003_spicy_arachne", - "breakpoints": true - }, - { - "idx": 4, - "version": "5", - "when": 1713056712218, - "tag": "0004_burly_lockjaw", - "breakpoints": true - }, - { - "idx": 5, - "version": "5", - "when": 1713056917973, - "tag": "0005_sleepy_puma", - "breakpoints": true - }, - { - "idx": 6, - "version": "5", - "when": 1713057159867, - "tag": "0006_messy_network", - "breakpoints": true - }, - { - "idx": 7, - "version": "5", - "when": 1713227918208, - "tag": "0007_naive_sleeper", - "breakpoints": true - }, - { - "idx": 8, - "version": "5", - "when": 1713246700119, - "tag": "0008_flawless_brother_voodoo", - "breakpoints": true - } - ] -} + "version": "5", + "dialect": "pg", + "entries": [ + { + "idx": 0, + "version": "5", + "when": 1712805159664, + "tag": "0000_illegal_living_lightning", + "breakpoints": true + }, + { + "idx": 1, + "version": "5", + "when": 1713055774123, + "tag": "0001_salty_night_thrasher", + "breakpoints": true + }, + { + "idx": 2, + "version": "5", + "when": 1713056370431, + "tag": "0002_stiff_ares", + "breakpoints": true + }, + { + "idx": 3, + "version": "5", + "when": 1713056528340, + "tag": "0003_spicy_arachne", + "breakpoints": true + }, + { + "idx": 4, + "version": "5", + "when": 1713056712218, + "tag": "0004_burly_lockjaw", + "breakpoints": true + }, + { + "idx": 5, + "version": "5", + "when": 1713056917973, + "tag": "0005_sleepy_puma", + "breakpoints": true + }, + { + "idx": 6, + "version": "5", + "when": 1713057159867, + "tag": "0006_messy_network", + "breakpoints": true + }, + { + "idx": 7, + "version": "5", + "when": 1713227918208, + "tag": "0007_naive_sleeper", + "breakpoints": true + }, + { + "idx": 8, + "version": "5", + "when": 1713246700119, + "tag": "0008_flawless_brother_voodoo", + "breakpoints": true + }, + { + "idx": 9, + "version": "5", + "when": 1713327832438, + "tag": "0009_easy_slyde", + "breakpoints": true + }, + { + "idx": 10, + "version": "5", + "when": 1713327880929, + "tag": "0010_daffy_frightful_four", + "breakpoints": true + }, + { + "idx": 11, + "version": "5", + "when": 1713333611707, + "tag": "0011_special_the_fury", + "breakpoints": true + } + ] +} \ No newline at end of file diff --git a/drizzle/schema.ts b/drizzle/schema.ts index 94eadac8..3da1ed0d 100644 --- a/drizzle/schema.ts +++ b/drizzle/schema.ts @@ -11,31 +11,32 @@ import { uniqueIndex, uuid, } from "drizzle-orm/pg-core"; +import type { Source as APISource } from "~types/mastodon/source"; -export const emoji = pgTable("Emoji", { +export const Emojis = pgTable("Emojis", { id: uuid("id").default(sql`uuid_generate_v7()`).primaryKey().notNull(), shortcode: text("shortcode").notNull(), url: text("url").notNull(), visibleInPicker: boolean("visible_in_picker").notNull(), alt: text("alt"), contentType: text("content_type").notNull(), - instanceId: uuid("instanceId").references(() => instance.id, { + instanceId: uuid("instanceId").references(() => Instances.id, { onDelete: "cascade", onUpdate: "cascade", }), }); -export const like = pgTable("Like", { +export const Likes = pgTable("Likes", { id: uuid("id").default(sql`uuid_generate_v7()`).primaryKey().notNull(), likerId: uuid("likerId") .notNull() - .references(() => user.id, { + .references(() => Users.id, { onDelete: "cascade", onUpdate: "cascade", }), likedId: uuid("likedId") .notNull() - .references(() => status.id, { + .references(() => Notes.id, { onDelete: "cascade", onUpdate: "cascade", }), @@ -44,7 +45,7 @@ export const like = pgTable("Like", { .notNull(), }); -export const lysandObject = pgTable( +export const LysandObjects = pgTable( "LysandObject", { id: uuid("id").default(sql`uuid_generate_v7()`).primaryKey().notNull(), @@ -72,17 +73,17 @@ export const lysandObject = pgTable( }, ); -export const relationship = pgTable("Relationship", { +export const Relationships = pgTable("Relationships", { id: uuid("id").default(sql`uuid_generate_v7()`).primaryKey().notNull(), ownerId: uuid("ownerId") .notNull() - .references(() => user.id, { + .references(() => Users.id, { onDelete: "cascade", onUpdate: "cascade", }), subjectId: uuid("subjectId") .notNull() - .references(() => user.id, { + .references(() => Users.id, { onDelete: "cascade", onUpdate: "cascade", }), @@ -110,8 +111,8 @@ export const relationship = pgTable("Relationship", { .notNull(), }); -export const application = pgTable( - "Application", +export const Applications = pgTable( + "Applications", { id: uuid("id").default(sql`uuid_generate_v7()`).primaryKey().notNull(), name: text("name").notNull(), @@ -129,12 +130,12 @@ export const application = pgTable( }, ); -export const applicationRelations = relations(application, ({ many }) => ({ - tokens: many(token), - loginFlows: many(openIdLoginFlow), +export const ApplicationsRelations = relations(Applications, ({ many }) => ({ + tokens: many(Tokens), + loginFlows: many(OpenIdLoginFlows), })); -export const token = pgTable("Token", { +export const Tokens = pgTable("Tokens", { id: uuid("id").default(sql`uuid_generate_v7()`).primaryKey().notNull(), tokenType: text("token_type").notNull(), scope: text("scope").notNull(), @@ -143,17 +144,17 @@ export const token = pgTable("Token", { createdAt: timestamp("created_at", { precision: 3, mode: "string" }) .defaultNow() .notNull(), - userId: uuid("userId").references(() => user.id, { + userId: uuid("userId").references(() => Users.id, { onDelete: "cascade", onUpdate: "cascade", }), - applicationId: uuid("applicationId").references(() => application.id, { + applicationId: uuid("applicationId").references(() => Applications.id, { onDelete: "cascade", onUpdate: "cascade", }), }); -export const attachment = pgTable("Attachment", { +export const Attachments = pgTable("Attachments", { id: uuid("id").default(sql`uuid_generate_v7()`).primaryKey().notNull(), url: text("url").notNull(), remoteUrl: text("remote_url"), @@ -167,13 +168,13 @@ export const attachment = pgTable("Attachment", { width: integer("width"), height: integer("height"), size: integer("size"), - statusId: uuid("statusId").references(() => status.id, { + noteId: uuid("noteId").references(() => Notes.id, { onDelete: "cascade", onUpdate: "cascade", }), }); -export const notification = pgTable("Notification", { +export const Notifications = pgTable("Notifications", { id: uuid("id").default(sql`uuid_generate_v7()`).primaryKey().notNull(), type: text("type").notNull(), createdAt: timestamp("createdAt", { precision: 3, mode: "string" }) @@ -181,31 +182,31 @@ export const notification = pgTable("Notification", { .notNull(), notifiedId: uuid("notifiedId") .notNull() - .references(() => user.id, { + .references(() => Users.id, { onDelete: "cascade", onUpdate: "cascade", }), accountId: uuid("accountId") .notNull() - .references(() => user.id, { + .references(() => Users.id, { onDelete: "cascade", onUpdate: "cascade", }), - statusId: uuid("statusId").references(() => status.id, { + noteId: uuid("noteId").references(() => Notes.id, { onDelete: "cascade", onUpdate: "cascade", }), dismissed: boolean("dismissed").default(false).notNull(), }); -export const status = pgTable( - "Status", +export const Notes = pgTable( + "Notes", { id: uuid("id").default(sql`uuid_generate_v7()`).primaryKey().notNull(), uri: text("uri"), authorId: uuid("authorId") .notNull() - .references(() => user.id, { + .references(() => Users.id, { onDelete: "cascade", onUpdate: "cascade", }), @@ -222,11 +223,11 @@ export const status = pgTable( content: text("content").default("").notNull(), contentType: text("content_type").default("text/plain").notNull(), visibility: text("visibility").notNull(), - inReplyToPostId: uuid("inReplyToPostId"), - quotingPostId: uuid("quotingPostId"), + replyId: uuid("replyId"), + quotingId: uuid("quoteId"), sensitive: boolean("sensitive").notNull(), spoilerText: text("spoiler_text").default("").notNull(), - applicationId: uuid("applicationId").references(() => application.id, { + applicationId: uuid("applicationId").references(() => Applications.id, { onDelete: "set null", onUpdate: "cascade", }), @@ -235,20 +236,20 @@ export const status = pgTable( (table) => { return { uriKey: uniqueIndex().on(table.uri), - statusReblogIdFkey: foreignKey({ + noteReblogIdFkey: foreignKey({ columns: [table.reblogId], foreignColumns: [table.id], }) .onUpdate("cascade") .onDelete("cascade"), - statusInReplyToPostIdFkey: foreignKey({ - columns: [table.inReplyToPostId], + noteReplyIdFkey: foreignKey({ + columns: [table.replyId], foreignColumns: [table.id], }) .onUpdate("cascade") .onDelete("set null"), - statusQuotingPostIdFkey: foreignKey({ - columns: [table.quotingPostId], + noteQuotingIdFkey: foreignKey({ + columns: [table.quotingId], foreignColumns: [table.id], }) .onUpdate("cascade") @@ -257,7 +258,7 @@ export const status = pgTable( }, ); -export const instance = pgTable("Instance", { +export const Instances = pgTable("Instances", { id: uuid("id").default(sql`uuid_generate_v7()`).primaryKey().notNull(), baseUrl: text("base_url").notNull(), name: text("name").notNull(), @@ -268,9 +269,9 @@ export const instance = pgTable("Instance", { .notNull(), }); -export const openIdAccount = pgTable("OpenIdAccount", { +export const OpenIdAccounts = pgTable("OpenIdAccounts", { id: uuid("id").default(sql`uuid_generate_v7()`).primaryKey().notNull(), - userId: uuid("userId").references(() => user.id, { + userId: uuid("userId").references(() => Users.id, { onDelete: "set null", onUpdate: "cascade", }), @@ -278,8 +279,8 @@ export const openIdAccount = pgTable("OpenIdAccount", { issuerId: text("issuer_id").notNull(), }); -export const user = pgTable( - "User", +export const Users = pgTable( + "Users", { id: uuid("id").default(sql`uuid_generate_v7()`).primaryKey().notNull(), uri: text("uri"), @@ -298,7 +299,7 @@ export const user = pgTable( inbox: string; outbox: string; }> | null>(), - source: jsonb("source").notNull(), + source: jsonb("source").notNull().$type(), avatar: text("avatar").notNull(), header: text("header").notNull(), createdAt: timestamp("created_at", { precision: 3, mode: "string" }) @@ -316,7 +317,7 @@ export const user = pgTable( sanctions: text("sanctions").array(), publicKey: text("public_key").notNull(), privateKey: text("private_key"), - instanceId: uuid("instanceId").references(() => instance.id, { + instanceId: uuid("instanceId").references(() => Instances.id, { onDelete: "cascade", onUpdate: "cascade", }), @@ -333,55 +334,55 @@ export const user = pgTable( }, ); -export const openIdLoginFlow = pgTable("OpenIdLoginFlow", { +export const OpenIdLoginFlows = pgTable("OpenIdLoginFlows", { id: uuid("id").default(sql`uuid_generate_v7()`).primaryKey().notNull(), codeVerifier: text("code_verifier").notNull(), - applicationId: uuid("applicationId").references(() => application.id, { + applicationId: uuid("applicationId").references(() => Applications.id, { onDelete: "cascade", onUpdate: "cascade", }), issuerId: text("issuer_id").notNull(), }); -export const openIdLoginFlowRelations = relations( - openIdLoginFlow, +export const OpenIdLoginFlowsRelations = relations( + OpenIdLoginFlows, ({ one }) => ({ - application: one(application, { - fields: [openIdLoginFlow.applicationId], - references: [application.id], + application: one(Applications, { + fields: [OpenIdLoginFlows.applicationId], + references: [Applications.id], }), }), ); -export const flag = pgTable("Flag", { +export const Flags = pgTable("Flags", { id: uuid("id").default(sql`uuid_generate_v7()`).primaryKey().notNull(), flagType: text("flag_type").default("other").notNull(), createdAt: timestamp("created_at", { precision: 3, mode: "string" }) .defaultNow() .notNull(), - flaggeStatusId: uuid("flaggeStatusId").references(() => status.id, { + noteId: uuid("noteId").references(() => Notes.id, { onDelete: "cascade", onUpdate: "cascade", }), - flaggedUserId: uuid("flaggedUserId").references(() => user.id, { + userId: uuid("userId").references(() => Users.id, { onDelete: "cascade", onUpdate: "cascade", }), }); -export const modNote = pgTable("ModNote", { +export const ModNotes = pgTable("ModNotes", { id: uuid("id").default(sql`uuid_generate_v7()`).primaryKey().notNull(), - notedStatusId: uuid("notedStatusId").references(() => status.id, { + nodeId: uuid("noteId").references(() => Notes.id, { onDelete: "cascade", onUpdate: "cascade", }), - notedUserId: uuid("notedUserId").references(() => user.id, { + userId: uuid("userId").references(() => Users.id, { onDelete: "cascade", onUpdate: "cascade", }), modId: uuid("modId") .notNull() - .references(() => user.id, { + .references(() => Users.id, { onDelete: "cascade", onUpdate: "cascade", }), @@ -391,19 +392,19 @@ export const modNote = pgTable("ModNote", { .notNull(), }); -export const modTag = pgTable("ModTag", { +export const ModTags = pgTable("ModTags", { id: uuid("id").default(sql`uuid_generate_v7()`).primaryKey().notNull(), - taggedStatusId: uuid("taggedStatusId").references(() => status.id, { + noteId: uuid("noteId").references(() => Notes.id, { onDelete: "cascade", onUpdate: "cascade", }), - taggedUserId: uuid("taggedUserId").references(() => user.id, { + userId: uuid("userId").references(() => Users.id, { onDelete: "cascade", onUpdate: "cascade", }), modId: uuid("modId") .notNull() - .references(() => user.id, { + .references(() => Users.id, { onDelete: "cascade", onUpdate: "cascade", }), @@ -413,18 +414,18 @@ export const modTag = pgTable("ModTag", { .notNull(), }); -export const emojiToUser = pgTable( +export const EmojiToUser = pgTable( "EmojiToUser", { emojiId: uuid("emojiId") .notNull() - .references(() => emoji.id, { + .references(() => Emojis.id, { onDelete: "cascade", onUpdate: "cascade", }), userId: uuid("userId") .notNull() - .references(() => user.id, { + .references(() => Users.id, { onDelete: "cascade", onUpdate: "cascade", }), @@ -437,267 +438,267 @@ export const emojiToUser = pgTable( }, ); -export const emojiToUserRelations = relations(emojiToUser, ({ one }) => ({ - emoji: one(emoji, { - fields: [emojiToUser.emojiId], - references: [emoji.id], +export const EmojiToUserRelations = relations(EmojiToUser, ({ one }) => ({ + emoji: one(Emojis, { + fields: [EmojiToUser.emojiId], + references: [Emojis.id], }), - user: one(user, { - fields: [emojiToUser.userId], - references: [user.id], + user: one(Users, { + fields: [EmojiToUser.userId], + references: [Users.id], }), })); -export const emojiToStatus = pgTable( - "EmojiToStatus", +export const EmojiToNote = pgTable( + "EmojiToNote", { emojiId: uuid("emojiId") .notNull() - .references(() => emoji.id, { + .references(() => Emojis.id, { onDelete: "cascade", onUpdate: "cascade", }), - statusId: uuid("statusId") + noteId: uuid("noteId") .notNull() - .references(() => status.id, { + .references(() => Notes.id, { onDelete: "cascade", onUpdate: "cascade", }), }, (table) => { return { - abUnique: uniqueIndex().on(table.emojiId, table.statusId), - bIdx: index().on(table.statusId), + abUnique: uniqueIndex().on(table.emojiId, table.noteId), + bIdx: index().on(table.noteId), }; }, ); -export const statusToMentions = pgTable( - "StatusToMentions", +export const NoteToMentions = pgTable( + "NoteToMentions", { - statusId: uuid("statusId") + noteId: uuid("noteId") .notNull() - .references(() => status.id, { + .references(() => Notes.id, { onDelete: "cascade", onUpdate: "cascade", }), userId: uuid("userId") .notNull() - .references(() => user.id, { + .references(() => Users.id, { onDelete: "cascade", onUpdate: "cascade", }), }, (table) => { return { - abUnique: uniqueIndex().on(table.statusId, table.userId), + abUnique: uniqueIndex().on(table.noteId, table.userId), bIdx: index().on(table.userId), }; }, ); -export const userPinnedNotes = pgTable( +export const UserToPinnedNotes = pgTable( "UserToPinnedNotes", { userId: uuid("userId") .notNull() - .references(() => status.id, { + .references(() => Users.id, { onDelete: "cascade", onUpdate: "cascade", }), - statusId: uuid("statusId") + noteId: uuid("noteId") .notNull() - .references(() => user.id, { + .references(() => Notes.id, { onDelete: "cascade", onUpdate: "cascade", }), }, (table) => { return { - abUnique: uniqueIndex().on(table.userId, table.statusId), - bIdx: index().on(table.statusId), + abUnique: uniqueIndex().on(table.userId, table.noteId), + bIdx: index().on(table.noteId), }; }, ); -export const attachmentRelations = relations(attachment, ({ one }) => ({ - status: one(status, { - fields: [attachment.statusId], - references: [status.id], +export const AttachmentsRelations = relations(Attachments, ({ one }) => ({ + notes: one(Notes, { + fields: [Attachments.noteId], + references: [Notes.id], }), })); -export const userRelations = relations(user, ({ many, one }) => ({ - emojis: many(emojiToUser), - pinnedNotes: many(userPinnedNotes), - statuses: many(status, { - relationName: "StatusToAuthor", +export const UsersRelations = relations(Users, ({ many, one }) => ({ + emojis: many(EmojiToUser), + pinnedNotes: many(UserToPinnedNotes), + notes: many(Notes, { + relationName: "NoteToAuthor", }), - likes: many(like), - relationships: many(relationship, { + likes: many(Likes), + relationships: many(Relationships, { relationName: "RelationshipToOwner", }), - relationshipSubjects: many(relationship, { + relationshipSubjects: many(Relationships, { relationName: "RelationshipToSubject", }), - notificationsMade: many(notification, { + notificationsMade: many(Notifications, { relationName: "NotificationToAccount", }), - notificationsReceived: many(notification, { + notificationsReceived: many(Notifications, { relationName: "NotificationToNotified", }), - openIdAccounts: many(openIdAccount), - flags: many(flag), - modNotes: many(modNote), - modTags: many(modTag), - tokens: many(token), - instance: one(instance, { - fields: [user.instanceId], - references: [instance.id], + openIdAccounts: many(OpenIdAccounts), + flags: many(Flags), + modNotes: many(ModNotes), + modTags: many(ModTags), + tokens: many(Tokens), + instance: one(Instances, { + fields: [Users.instanceId], + references: [Instances.id], }), - mentionedIn: many(statusToMentions), + mentionedIn: many(NoteToMentions), })); -export const relationshipRelations = relations(relationship, ({ one }) => ({ - owner: one(user, { - fields: [relationship.ownerId], - references: [user.id], +export const RelationshipsRelations = relations(Relationships, ({ one }) => ({ + owner: one(Users, { + fields: [Relationships.ownerId], + references: [Users.id], relationName: "RelationshipToOwner", }), - subject: one(user, { - fields: [relationship.subjectId], - references: [user.id], + subject: one(Users, { + fields: [Relationships.subjectId], + references: [Users.id], relationName: "RelationshipToSubject", }), })); -export const tokenRelations = relations(token, ({ one }) => ({ - user: one(user, { - fields: [token.userId], - references: [user.id], +export const TokensRelations = relations(Tokens, ({ one }) => ({ + user: one(Users, { + fields: [Tokens.userId], + references: [Users.id], }), - application: one(application, { - fields: [token.applicationId], - references: [application.id], + application: one(Applications, { + fields: [Tokens.applicationId], + references: [Applications.id], }), })); -export const statusToUserRelations = relations(statusToMentions, ({ one }) => ({ - status: one(status, { - fields: [statusToMentions.statusId], - references: [status.id], +export const NotesToUsersRelations = relations(NoteToMentions, ({ one }) => ({ + note: one(Notes, { + fields: [NoteToMentions.noteId], + references: [Notes.id], }), - user: one(user, { - fields: [statusToMentions.userId], - references: [user.id], + user: one(Users, { + fields: [NoteToMentions.userId], + references: [Users.id], }), })); -export const userPinnedNotesRelations = relations( - userPinnedNotes, +export const UserToPinnedNotesRelations = relations( + UserToPinnedNotes, ({ one }) => ({ - status: one(status, { - fields: [userPinnedNotes.statusId], - references: [status.id], + note: one(Notes, { + fields: [UserToPinnedNotes.noteId], + references: [Notes.id], }), - user: one(user, { - fields: [userPinnedNotes.userId], - references: [user.id], + user: one(Users, { + fields: [UserToPinnedNotes.userId], + references: [Users.id], }), }), ); -export const statusRelations = relations(status, ({ many, one }) => ({ - emojis: many(emojiToStatus), - author: one(user, { - fields: [status.authorId], - references: [user.id], - relationName: "StatusToAuthor", +export const NotesRelations = relations(Notes, ({ many, one }) => ({ + emojis: many(EmojiToNote), + author: one(Users, { + fields: [Notes.authorId], + references: [Users.id], + relationName: "NoteToAuthor", }), - attachments: many(attachment), - mentions: many(statusToMentions), - reblog: one(status, { - fields: [status.reblogId], - references: [status.id], - relationName: "StatusToReblog", + attachments: many(Attachments), + mentions: many(NoteToMentions), + reblog: one(Notes, { + fields: [Notes.reblogId], + references: [Notes.id], + relationName: "NoteToReblogs", }), - usersThatHavePinned: many(userPinnedNotes), - inReplyTo: one(status, { - fields: [status.inReplyToPostId], - references: [status.id], - relationName: "StatusToReplying", + usersThatHavePinned: many(UserToPinnedNotes), + reply: one(Notes, { + fields: [Notes.replyId], + references: [Notes.id], + relationName: "NoteToReplies", }), - quoting: one(status, { - fields: [status.quotingPostId], - references: [status.id], - relationName: "StatusToQuoting", + quote: one(Notes, { + fields: [Notes.quotingId], + references: [Notes.id], + relationName: "NoteToQuotes", }), - application: one(application, { - fields: [status.applicationId], - references: [application.id], + application: one(Applications, { + fields: [Notes.applicationId], + references: [Applications.id], }), - quotes: many(status, { - relationName: "StatusToQuoting", + quotes: many(Notes, { + relationName: "NoteToQuotes", }), - replies: many(status, { - relationName: "StatusToReplying", + replies: many(Notes, { + relationName: "NoteToReplies", }), - likes: many(like), - reblogs: many(status, { - relationName: "StatusToReblog", + likes: many(Likes), + reblogs: many(Notes, { + relationName: "NoteToReblogs", }), - notifications: many(notification), + notifications: many(Notifications), })); -export const notificationRelations = relations(notification, ({ one }) => ({ - account: one(user, { - fields: [notification.accountId], - references: [user.id], +export const NotificationsRelations = relations(Notifications, ({ one }) => ({ + account: one(Users, { + fields: [Notifications.accountId], + references: [Users.id], relationName: "NotificationToAccount", }), - notified: one(user, { - fields: [notification.notifiedId], - references: [user.id], + notified: one(Users, { + fields: [Notifications.notifiedId], + references: [Users.id], relationName: "NotificationToNotified", }), - status: one(status, { - fields: [notification.statusId], - references: [status.id], + note: one(Notes, { + fields: [Notifications.noteId], + references: [Notes.id], }), })); -export const likeRelations = relations(like, ({ one }) => ({ - liker: one(user, { - fields: [like.likerId], - references: [user.id], +export const LikesRelations = relations(Likes, ({ one }) => ({ + liker: one(Users, { + fields: [Likes.likerId], + references: [Users.id], }), - liked: one(status, { - fields: [like.likedId], - references: [status.id], + liked: one(Notes, { + fields: [Likes.likedId], + references: [Notes.id], }), })); -export const emojiRelations = relations(emoji, ({ one, many }) => ({ - instance: one(instance, { - fields: [emoji.instanceId], - references: [instance.id], +export const EmojisRelations = relations(Emojis, ({ one, many }) => ({ + instance: one(Instances, { + fields: [Emojis.instanceId], + references: [Instances.id], }), - users: many(emojiToUser), - statuses: many(emojiToStatus), + users: many(EmojiToUser), + notes: many(EmojiToNote), })); -export const instanceRelations = relations(instance, ({ many }) => ({ - users: many(user), - emojis: many(emoji), +export const InstancesRelations = relations(Instances, ({ many }) => ({ + users: many(Users), + emojis: many(Emojis), })); -export const emojiToStatusRelations = relations(emojiToStatus, ({ one }) => ({ - emoji: one(emoji, { - fields: [emojiToStatus.emojiId], - references: [emoji.id], +export const EmojisToNotesRelations = relations(EmojiToNote, ({ one }) => ({ + emoji: one(Emojis, { + fields: [EmojiToNote.emojiId], + references: [Emojis.id], }), - status: one(status, { - fields: [emojiToStatus.statusId], - references: [status.id], + note: one(Notes, { + fields: [EmojiToNote.noteId], + references: [Notes.id], }), })); diff --git a/index.ts b/index.ts index f399f3bc..5467de08 100644 --- a/index.ts +++ b/index.ts @@ -4,7 +4,7 @@ import { config } from "config-manager"; import { count } from "drizzle-orm"; import { LogLevel, LogManager, type MultiLogManager } from "log-manager"; import { db, setupDatabase } from "~drizzle/db"; -import { status } from "~drizzle/schema"; +import { Notes } from "~drizzle/schema"; import { createServer } from "~server"; const timeAtStart = performance.now(); @@ -35,7 +35,7 @@ try { .select({ count: count(), }) - .from(status) + .from(Notes) )[0].count; } catch (e) { const error = e as Error; diff --git a/packages/database-interface/note.ts b/packages/database-interface/note.ts index ac110e23..9b5be2f7 100644 --- a/packages/database-interface/note.ts +++ b/packages/database-interface/note.ts @@ -43,13 +43,14 @@ import { } from "~database/entities/User"; import { db } from "~drizzle/db"; import { - attachment, - emojiToStatus, - notification, - status, - statusToMentions, - user, - userRelations, + Attachments, + EmojiToNote, + NoteToMentions, + Notes, + Notifications, + UserToPinnedNotes, + Users, + UsersRelations, } from "~drizzle/schema"; import { config } from "~packages/config-manager"; import type { Attachment as APIAttachment } from "~types/mastodon/attachment"; @@ -64,16 +65,16 @@ export class Note { static async fromId(id: string | null): Promise { if (!id) return null; - return await Note.fromSql(eq(status.id, id)); + return await Note.fromSql(eq(Notes.id, id)); } static async fromIds(ids: string[]): Promise { - return await Note.manyFromSql(inArray(status.id, ids)); + return await Note.manyFromSql(inArray(Notes.id, ids)); } static async fromSql( sql: SQL | undefined, - orderBy: SQL | undefined = desc(status.id), + orderBy: SQL | undefined = desc(Notes.id), ) { const found = await findFirstNote({ where: sql, @@ -86,7 +87,7 @@ export class Note { static async manyFromSql( sql: SQL | undefined, - orderBy: SQL | undefined = desc(status.id), + orderBy: SQL | undefined = desc(Notes.id), limit?: number, offset?: number, ) { @@ -121,11 +122,10 @@ export class Note { const usersThatCanSeePost = await findManyUsers({ where: (user, { isNotNull }) => isNotNull(user.instanceId), with: { - ...userRelations, relationships: { where: (relationship, { eq, and }) => and( - eq(relationship.subjectId, user.id), + eq(relationship.subjectId, Users.id), eq(relationship.following, true), ), }, @@ -163,24 +163,37 @@ export class Note { } async getReplyChildren() { - return await Note.manyFromSql( - eq(status.inReplyToPostId, this.status.id), - ); + return await Note.manyFromSql(eq(Notes.replyId, this.status.id)); + } + + async pin(pinner: User) { + return ( + await db + .insert(UserToPinnedNotes) + .values({ + noteId: this.status.id, + userId: pinner.id, + }) + .returning() + )[0]; } async unpin(unpinner: User) { - return await db - .delete(statusToMentions) - .where( - and( - eq(statusToMentions.statusId, this.status.id), - eq(statusToMentions.userId, unpinner.id), - ), - ); + return ( + await db + .delete(UserToPinnedNotes) + .where( + and( + eq(NoteToMentions.noteId, this.status.id), + eq(NoteToMentions.userId, unpinner.id), + ), + ) + .returning() + )[0]; } - static async insert(values: InferInsertModel) { - return (await db.insert(status).values(values).returning())[0]; + static async insert(values: InferInsertModel) { + return (await db.insert(Notes).values(values).returning())[0]; } static async fromData( @@ -225,18 +238,18 @@ export class Note { sensitive: is_sensitive, spoilerText: spoiler_text, uri: uri || null, - inReplyToPostId: replyId ?? null, - quotingPostId: quoteId ?? null, + replyId: replyId ?? null, + quotingId: quoteId ?? null, applicationId: application?.id ?? null, }); // Connect emojis for (const emoji of foundEmojis) { await db - .insert(emojiToStatus) + .insert(EmojiToNote) .values({ emojiId: emoji.id, - statusId: newNote.id, + noteId: newNote.id, }) .execute(); } @@ -244,9 +257,9 @@ export class Note { // Connect mentions for (const mention of mentions ?? []) { await db - .insert(statusToMentions) + .insert(NoteToMentions) .values({ - statusId: newNote.id, + noteId: newNote.id, userId: mention.id, }) .execute(); @@ -255,21 +268,21 @@ export class Note { // Set attachment parents if (media_attachments && media_attachments.length > 0) { await db - .update(attachment) + .update(Attachments) .set({ - statusId: newNote.id, + noteId: newNote.id, }) - .where(inArray(attachment.id, media_attachments)); + .where(inArray(Attachments.id, media_attachments)); } // Send notifications for mentioned local users for (const mention of mentions ?? []) { if (mention.instanceId === null) { - await db.insert(notification).values({ + await db.insert(Notifications).values({ accountId: author.id, notifiedId: mention.id, type: "mention", - statusId: newNote.id, + noteId: newNote.id, }); } } @@ -319,29 +332,29 @@ export class Note { // Connect emojis await db - .delete(emojiToStatus) - .where(eq(emojiToStatus.statusId, this.status.id)); + .delete(EmojiToNote) + .where(eq(EmojiToNote.noteId, this.status.id)); for (const emoji of foundEmojis) { await db - .insert(emojiToStatus) + .insert(EmojiToNote) .values({ emojiId: emoji.id, - statusId: this.status.id, + noteId: this.status.id, }) .execute(); } // Connect mentions await db - .delete(statusToMentions) - .where(eq(statusToMentions.statusId, this.status.id)); + .delete(NoteToMentions) + .where(eq(NoteToMentions.noteId, this.status.id)); for (const mention of mentions ?? []) { await db - .insert(statusToMentions) + .insert(NoteToMentions) .values({ - statusId: this.status.id, + noteId: this.status.id, userId: mention.id, }) .execute(); @@ -350,11 +363,11 @@ export class Note { // Set attachment parents if (media_attachments && media_attachments.length > 0) { await db - .update(attachment) + .update(Attachments) .set({ - statusId: this.status.id, + noteId: this.status.id, }) - .where(inArray(attachment.id, media_attachments)); + .where(inArray(Attachments.id, media_attachments)); } return await Note.fromId(newNote.id); @@ -363,8 +376,8 @@ export class Note { async delete() { return ( await db - .delete(status) - .where(eq(status.id, this.status.id)) + .delete(Notes) + .where(eq(Notes.id, this.status.id)) .returning() )[0]; } @@ -372,18 +385,15 @@ export class Note { async update(newStatus: Partial) { return ( await db - .update(status) + .update(Notes) .set(newStatus) - .where(eq(status.id, this.status.id)) + .where(eq(Notes.id, this.status.id)) .returning() )[0]; } static async deleteMany(ids: string[]) { - return await db - .delete(status) - .where(inArray(status.id, ids)) - .returning(); + return await db.delete(Notes).where(inArray(Notes.id, ids)).returning(); } /** @@ -397,11 +407,11 @@ export class Note { if (this.getStatus().visibility === "unlisted") return true; if (this.getStatus().visibility === "private") { return user - ? await db.query.relationship.findFirst({ + ? await db.query.Relationships.findFirst({ where: (relationship, { and, eq }) => and( eq(relationship.ownerId, user?.id), - eq(relationship.subjectId, status.authorId), + eq(relationship.subjectId, Notes.authorId), eq(relationship.following, true), ), }) @@ -416,10 +426,10 @@ export class Note { async toAPI(userFetching?: UserWithRelations | null): Promise { const data = this.getStatus(); const wasPinnedByUser = userFetching - ? !!(await db.query.userPinnedNotes.findFirst({ + ? !!(await db.query.UserToPinnedNotes.findFirst({ where: (relation, { and, eq }) => and( - eq(relation.statusId, data.id), + eq(relation.noteId, data.id), eq(relation.userId, userFetching?.id), ), })) @@ -428,14 +438,14 @@ export class Note { const wasRebloggedByUser = userFetching ? !!(await Note.fromSql( and( - eq(status.authorId, userFetching?.id), - eq(status.reblogId, data.id), + eq(Notes.authorId, userFetching?.id), + eq(Notes.reblogId, data.id), ), )) : false; const wasMutedByUser = userFetching - ? !!(await db.query.relationship.findFirst({ + ? !!(await db.query.Relationships.findFirst({ where: (relationship, { and, eq }) => and( eq(relationship.ownerId, userFetching.id), @@ -468,8 +478,8 @@ export class Note { return { id: data.id, - in_reply_to_id: data.inReplyToPostId || null, - in_reply_to_account_id: data.inReplyTo?.authorId || null, + in_reply_to_id: data.replyId || null, + in_reply_to_account_id: data.reply?.authorId || null, account: userToAPI(data.author), created_at: new Date(data.createdAt).toISOString(), application: data.application @@ -511,9 +521,9 @@ export class Note { visibility: data.visibility as APIStatus["visibility"], url: data.uri || this.getMastoURI(), bookmarked: false, - quote: !!data.quotingPostId, + quote: !!data.quotingId, // @ts-expect-error Pleroma extension - quote_id: data.quotingPostId || undefined, + quote_id: data.quotingId || undefined, }; } @@ -546,8 +556,8 @@ export class Note { ), is_sensitive: status.sensitive, mentions: status.mentions.map((mention) => mention.uri || ""), - quotes: getStatusUri(status.quoting) ?? undefined, - replies_to: getStatusUri(status.inReplyTo) ?? undefined, + quotes: getStatusUri(status.quote) ?? undefined, + replies_to: getStatusUri(status.reply) ?? undefined, subject: status.spoilerText, visibility: status.visibility as Lysand.Visibility, extensions: { @@ -567,10 +577,8 @@ export class Note { let currentStatus: Note = this; - while (currentStatus.getStatus().inReplyToPostId) { - const parent = await Note.fromId( - currentStatus.getStatus().inReplyToPostId, - ); + while (currentStatus.getStatus().replyId) { + const parent = await Note.fromId(currentStatus.getStatus().replyId); if (!parent) { break; diff --git a/packages/database-interface/timeline.ts b/packages/database-interface/timeline.ts index b1b6d1c2..f18b37e1 100644 --- a/packages/database-interface/timeline.ts +++ b/packages/database-interface/timeline.ts @@ -1,5 +1,5 @@ import { type SQL, gt } from "drizzle-orm"; -import { status } from "~drizzle/schema"; +import { Notes } from "~drizzle/schema"; import { config } from "~packages/config-manager"; import { Note } from "./note"; @@ -43,7 +43,7 @@ export class Timeline { switch (this.type) { case TimelineType.NOTE: { const objectBefore = await Note.fromSql( - gt(status.id, objects[0].getStatus().id), + gt(Notes.id, objects[0].getStatus().id), ); if (objectBefore) { @@ -57,7 +57,7 @@ export class Timeline { if (objects.length >= (limit ?? 20)) { const objectAfter = await Note.fromSql( gt( - status.id, + Notes.id, objects[objects.length - 1].getStatus().id, ), ); diff --git a/packages/server-handler/index.ts b/packages/server-handler/index.ts index 36569df0..b3eedf0e 100644 --- a/packages/server-handler/index.ts +++ b/packages/server-handler/index.ts @@ -77,7 +77,7 @@ export const processRoute = async ( return null; }); - if (!route) { + if (!route?.meta) { return errorResponse("Route not found", 404); } diff --git a/server/api/api/auth/login/index.ts b/server/api/api/auth/login/index.ts index 4018f4d9..5461d229 100644 --- a/server/api/api/auth/login/index.ts +++ b/server/api/api/auth/login/index.ts @@ -4,7 +4,7 @@ import { z } from "zod"; import { TokenType } from "~database/entities/Token"; import { findFirstUser } from "~database/entities/User"; import { db } from "~drizzle/db"; -import { token } from "~drizzle/schema"; +import { Tokens } from "~drizzle/schema"; export const meta = applyConfig({ allowedMethods: ["POST"], @@ -62,7 +62,7 @@ export default apiRoute( ) return redirectToLogin("Invalid username or password"); - const application = await db.query.application.findFirst({ + const application = await db.query.Applications.findFirst({ where: (app, { eq }) => eq(app.clientId, client_id), }); @@ -70,7 +70,7 @@ export default apiRoute( const code = randomBytes(32).toString("hex"); - await db.insert(token).values({ + await db.insert(Tokens).values({ accessToken: randomBytes(64).toString("base64url"), code: code, scope: scopes.join(" "), diff --git a/server/api/api/auth/mastodon-login/index.ts b/server/api/api/auth/mastodon-login/index.ts index b31eb8d1..9cd0f342 100644 --- a/server/api/api/auth/mastodon-login/index.ts +++ b/server/api/api/auth/mastodon-login/index.ts @@ -4,7 +4,7 @@ import { z } from "zod"; import { TokenType } from "~database/entities/Token"; import { findFirstUser } from "~database/entities/User"; import { db } from "~drizzle/db"; -import { token } from "~drizzle/schema"; +import { Tokens } from "~drizzle/schema"; import { config } from "~packages/config-manager"; export const meta = applyConfig({ @@ -54,7 +54,7 @@ export default apiRoute( const code = randomBytes(32).toString("hex"); const accessToken = randomBytes(64).toString("base64url"); - await db.insert(token).values({ + await db.insert(Tokens).values({ accessToken, code: code, scope: "read write follow push", diff --git a/server/api/api/auth/mastodon-logout/index.ts b/server/api/api/auth/mastodon-logout/index.ts index 0161618a..23a4c055 100644 --- a/server/api/api/auth/mastodon-logout/index.ts +++ b/server/api/api/auth/mastodon-logout/index.ts @@ -1,10 +1,4 @@ -import { randomBytes } from "node:crypto"; import { apiRoute, applyConfig } from "@api"; -import { z } from "zod"; -import { TokenType } from "~database/entities/Token"; -import { findFirstUser } from "~database/entities/User"; -import { db } from "~drizzle/db"; -import { token } from "~drizzle/schema"; import { config } from "~packages/config-manager"; export const meta = applyConfig({ diff --git a/server/api/api/auth/redirect/index.ts b/server/api/api/auth/redirect/index.ts index 7ccbfada..7a992b88 100644 --- a/server/api/api/auth/redirect/index.ts +++ b/server/api/api/auth/redirect/index.ts @@ -1,7 +1,7 @@ import { apiRoute, applyConfig } from "@api"; import { and, eq } from "drizzle-orm"; import { db } from "~drizzle/db"; -import { application, token } from "~drizzle/schema"; +import { Applications, Tokens } from "~drizzle/schema"; export const meta = applyConfig({ allowedMethods: ["POST"], @@ -18,10 +18,7 @@ export const meta = applyConfig({ /** * OAuth Code flow */ -export default apiRoute<{ - email: string; - password: string; -}>(async (req, matchedRoute) => { +export default apiRoute(async (req, matchedRoute) => { const redirect_uri = decodeURIComponent(matchedRoute.query.redirect_uri); const client_id = matchedRoute.query.client_id; const code = matchedRoute.query.code; @@ -37,9 +34,9 @@ export default apiRoute<{ const foundToken = await db .select() - .from(token) - .leftJoin(application, eq(token.applicationId, application.id)) - .where(and(eq(token.code, code), eq(application.clientId, client_id))) + .from(Tokens) + .leftJoin(Applications, eq(Tokens.applicationId, Applications.id)) + .where(and(eq(Tokens.code, code), eq(Applications.clientId, client_id))) .limit(1); if (!foundToken || foundToken.length <= 0) diff --git a/server/api/api/v1/accounts/[id]/block.test.ts b/server/api/api/v1/accounts/[id]/block.test.ts new file mode 100644 index 00000000..e78242ae --- /dev/null +++ b/server/api/api/v1/accounts/[id]/block.test.ts @@ -0,0 +1,98 @@ +import { afterAll, describe, expect, test } from "bun:test"; +import { config } from "config-manager"; +import { + deleteOldTestUsers, + getTestUsers, + sendTestRequest, +} from "~tests/utils"; +import { meta } from "./block"; +import type { Relationship as APIRelationship } from "~types/mastodon/relationship"; + +await deleteOldTestUsers(); + +const { users, tokens, deleteUsers } = await getTestUsers(2); + +afterAll(async () => { + await deleteUsers(); +}); + +// /api/v1/accounts/:id/block +describe(meta.route, () => { + test("should return 401 if not authenticated", async () => { + const response = await sendTestRequest( + new Request( + new URL( + meta.route.replace(":id", users[1].id), + config.http.base_url, + ), + { + method: "POST", + }, + ), + ); + expect(response.status).toBe(401); + }); + + test("should return 404 if user not found", async () => { + const response = await sendTestRequest( + new Request( + new URL( + meta.route.replace( + ":id", + "00000000-0000-0000-0000-000000000000", + ), + config.http.base_url, + ), + { + method: "POST", + headers: { + Authorization: `Bearer ${tokens[0].accessToken}`, + }, + }, + ), + ); + expect(response.status).toBe(404); + }); + + test("should block user", async () => { + const response = await sendTestRequest( + new Request( + new URL( + meta.route.replace(":id", users[1].id), + config.http.base_url, + ), + { + method: "POST", + headers: { + Authorization: `Bearer ${tokens[0].accessToken}`, + }, + }, + ), + ); + expect(response.status).toBe(200); + + const relationship = (await response.json()) as APIRelationship; + expect(relationship.blocking).toBe(true); + }); + + test("should return 200 if user already blocked", async () => { + const response = await sendTestRequest( + new Request( + new URL( + meta.route.replace(":id", users[1].id), + config.http.base_url, + ), + { + method: "POST", + headers: { + Authorization: `Bearer ${tokens[0].accessToken}`, + }, + }, + ), + ); + expect(response.status).toBe(200); + + const relationship = (await response.json()) as APIRelationship; + expect(relationship.blocking).toBe(true); + }); +}); diff --git a/server/api/api/v1/accounts/[id]/block.ts b/server/api/api/v1/accounts/[id]/block.ts index 0e9a05dc..3fcd4d9d 100644 --- a/server/api/api/v1/accounts/[id]/block.ts +++ b/server/api/api/v1/accounts/[id]/block.ts @@ -7,7 +7,7 @@ import { getRelationshipToOtherUser, } from "~database/entities/User"; import { db } from "~drizzle/db"; -import { relationship } from "~drizzle/schema"; +import { Relationships } from "~drizzle/schema"; export const meta = applyConfig({ allowedMethods: ["POST"], @@ -48,11 +48,11 @@ export default apiRoute(async (req, matchedRoute, extraData) => { } await db - .update(relationship) + .update(Relationships) .set({ blocking: true, }) - .where(eq(relationship.id, foundRelationship.id)); + .where(eq(Relationships.id, foundRelationship.id)); return jsonResponse(relationshipToAPI(foundRelationship)); }); diff --git a/server/api/api/v1/accounts/[id]/follow.test.ts b/server/api/api/v1/accounts/[id]/follow.test.ts new file mode 100644 index 00000000..3b4ef482 --- /dev/null +++ b/server/api/api/v1/accounts/[id]/follow.test.ts @@ -0,0 +1,98 @@ +import { afterAll, describe, expect, test } from "bun:test"; +import { config } from "config-manager"; +import { + deleteOldTestUsers, + getTestUsers, + sendTestRequest, +} from "~tests/utils"; +import { meta } from "./follow"; +import type { Relationship as APIRelationship } from "~types/mastodon/relationship"; + +await deleteOldTestUsers(); + +const { users, tokens, deleteUsers } = await getTestUsers(2); + +afterAll(async () => { + await deleteUsers(); +}); + +// /api/v1/accounts/:id/follow +describe(meta.route, () => { + test("should return 401 if not authenticated", async () => { + const response = await sendTestRequest( + new Request( + new URL( + meta.route.replace(":id", users[1].id), + config.http.base_url, + ), + { + method: "POST", + }, + ), + ); + expect(response.status).toBe(401); + }); + + test("should return 404 if user not found", async () => { + const response = await sendTestRequest( + new Request( + new URL( + meta.route.replace( + ":id", + "00000000-0000-0000-0000-000000000000", + ), + config.http.base_url, + ), + { + method: "POST", + headers: { + Authorization: `Bearer ${tokens[0].accessToken}`, + }, + }, + ), + ); + expect(response.status).toBe(404); + }); + + test("should follow user", async () => { + const response = await sendTestRequest( + new Request( + new URL( + meta.route.replace(":id", users[1].id), + config.http.base_url, + ), + { + method: "POST", + headers: { + Authorization: `Bearer ${tokens[0].accessToken}`, + }, + }, + ), + ); + expect(response.status).toBe(200); + + const relationship = (await response.json()) as APIRelationship; + expect(relationship.following).toBe(true); + }); + + test("should return 200 if user already followed", async () => { + const response = await sendTestRequest( + new Request( + new URL( + meta.route.replace(":id", users[1].id), + config.http.base_url, + ), + { + method: "POST", + headers: { + Authorization: `Bearer ${tokens[0].accessToken}`, + }, + }, + ), + ); + expect(response.status).toBe(200); + + const relationship = (await response.json()) as APIRelationship; + expect(relationship.following).toBe(true); + }); +}); diff --git a/server/api/api/v1/accounts/[id]/followers.ts b/server/api/api/v1/accounts/[id]/followers.ts index 4c65ad07..0b4edbaf 100644 --- a/server/api/api/v1/accounts/[id]/followers.ts +++ b/server/api/api/v1/accounts/[id]/followers.ts @@ -57,7 +57,7 @@ export default apiRoute( max_id ? lt(follower.id, max_id) : undefined, since_id ? gte(follower.id, since_id) : undefined, min_id ? gt(follower.id, min_id) : undefined, - sql`EXISTS (SELECT 1 FROM "Relationship" WHERE "Relationship"."subjectId" = ${otherUser.id} AND "Relationship"."ownerId" = ${follower.id} AND "Relationship"."following" = true)`, + sql`EXISTS (SELECT 1 FROM "Relationships" WHERE "Relationships"."subjectId" = ${otherUser.id} AND "Relationships"."ownerId" = ${follower.id} AND "Relationships"."following" = true)`, ), // @ts-expect-error Yes I KNOW the types are wrong orderBy: (liker, { desc }) => desc(liker.id), diff --git a/server/api/api/v1/accounts/[id]/following.ts b/server/api/api/v1/accounts/[id]/following.ts index 6ec9630b..530a2656 100644 --- a/server/api/api/v1/accounts/[id]/following.ts +++ b/server/api/api/v1/accounts/[id]/following.ts @@ -57,7 +57,7 @@ export default apiRoute( max_id ? lt(following.id, max_id) : undefined, since_id ? gte(following.id, since_id) : undefined, min_id ? gt(following.id, min_id) : undefined, - sql`EXISTS (SELECT 1 FROM "Relationship" WHERE "Relationship"."subjectId" = ${following.id} AND "Relationship"."ownerId" = ${otherUser.id} AND "Relationship"."following" = true)`, + sql`EXISTS (SELECT 1 FROM "Relationships" WHERE "Relationships"."subjectId" = ${following.id} AND "Relationships"."ownerId" = ${otherUser.id} AND "Relationships"."following" = true)`, ), // @ts-expect-error Yes I KNOW the types are wrong orderBy: (liker, { desc }) => desc(liker.id), diff --git a/server/api/api/v1/accounts/[id]/mute.test.ts b/server/api/api/v1/accounts/[id]/mute.test.ts new file mode 100644 index 00000000..efeb82eb --- /dev/null +++ b/server/api/api/v1/accounts/[id]/mute.test.ts @@ -0,0 +1,98 @@ +import { afterAll, describe, expect, test } from "bun:test"; +import { config } from "config-manager"; +import { + deleteOldTestUsers, + getTestUsers, + sendTestRequest, +} from "~tests/utils"; +import { meta } from "./mute"; +import type { Relationship as APIRelationship } from "~types/mastodon/relationship"; + +await deleteOldTestUsers(); + +const { users, tokens, deleteUsers } = await getTestUsers(2); + +afterAll(async () => { + await deleteUsers(); +}); + +// /api/v1/accounts/:id/mute +describe(meta.route, () => { + test("should return 401 if not authenticated", async () => { + const response = await sendTestRequest( + new Request( + new URL( + meta.route.replace(":id", users[1].id), + config.http.base_url, + ), + { + method: "POST", + }, + ), + ); + expect(response.status).toBe(401); + }); + + test("should return 404 if user not found", async () => { + const response = await sendTestRequest( + new Request( + new URL( + meta.route.replace( + ":id", + "00000000-0000-0000-0000-000000000000", + ), + config.http.base_url, + ), + { + method: "POST", + headers: { + Authorization: `Bearer ${tokens[0].accessToken}`, + }, + }, + ), + ); + expect(response.status).toBe(404); + }); + + test("should mute user", async () => { + const response = await sendTestRequest( + new Request( + new URL( + meta.route.replace(":id", users[1].id), + config.http.base_url, + ), + { + method: "POST", + headers: { + Authorization: `Bearer ${tokens[0].accessToken}`, + }, + }, + ), + ); + expect(response.status).toBe(200); + + const relationship = (await response.json()) as APIRelationship; + expect(relationship.muting).toBe(true); + }); + + test("should return 200 if user already muted", async () => { + const response = await sendTestRequest( + new Request( + new URL( + meta.route.replace(":id", users[1].id), + config.http.base_url, + ), + { + method: "POST", + headers: { + Authorization: `Bearer ${tokens[0].accessToken}`, + }, + }, + ), + ); + expect(response.status).toBe(200); + + const relationship = (await response.json()) as APIRelationship; + expect(relationship.muting).toBe(true); + }); +}); diff --git a/server/api/api/v1/accounts/[id]/mute.ts b/server/api/api/v1/accounts/[id]/mute.ts index 37ed50cb..93173ffd 100644 --- a/server/api/api/v1/accounts/[id]/mute.ts +++ b/server/api/api/v1/accounts/[id]/mute.ts @@ -8,7 +8,7 @@ import { getRelationshipToOtherUser, } from "~database/entities/User"; import { db } from "~drizzle/db"; -import { relationship } from "~drizzle/schema"; +import { Relationships } from "~drizzle/schema"; export const meta = applyConfig({ allowedMethods: ["POST"], @@ -66,12 +66,12 @@ export default apiRoute( } await db - .update(relationship) + .update(Relationships) .set({ muting: true, mutingNotifications: notifications ?? true, }) - .where(eq(relationship.id, foundRelationship.id)); + .where(eq(Relationships.id, foundRelationship.id)); // TODO: Implement duration diff --git a/server/api/api/v1/accounts/[id]/note.ts b/server/api/api/v1/accounts/[id]/note.ts index 487db35f..83bfae02 100644 --- a/server/api/api/v1/accounts/[id]/note.ts +++ b/server/api/api/v1/accounts/[id]/note.ts @@ -8,7 +8,7 @@ import { getRelationshipToOtherUser, } from "~database/entities/User"; import { db } from "~drizzle/db"; -import { relationship } from "~drizzle/schema"; +import { Relationships } from "~drizzle/schema"; export const meta = applyConfig({ allowedMethods: ["POST"], @@ -58,11 +58,11 @@ export default apiRoute( foundRelationship.note = comment ?? ""; await db - .update(relationship) + .update(Relationships) .set({ note: foundRelationship.note, }) - .where(eq(relationship.id, foundRelationship.id)); + .where(eq(Relationships.id, foundRelationship.id)); return jsonResponse(relationshipToAPI(foundRelationship)); }, diff --git a/server/api/api/v1/accounts/[id]/pin.ts b/server/api/api/v1/accounts/[id]/pin.ts index b561b264..50e1cd70 100644 --- a/server/api/api/v1/accounts/[id]/pin.ts +++ b/server/api/api/v1/accounts/[id]/pin.ts @@ -7,7 +7,7 @@ import { getRelationshipToOtherUser, } from "~database/entities/User"; import { db } from "~drizzle/db"; -import { relationship } from "~drizzle/schema"; +import { Relationships } from "~drizzle/schema"; export const meta = applyConfig({ allowedMethods: ["POST"], @@ -48,11 +48,11 @@ export default apiRoute(async (req, matchedRoute, extraData) => { foundRelationship.endorsed = true; await db - .update(relationship) + .update(Relationships) .set({ endorsed: true, }) - .where(eq(relationship.id, foundRelationship.id)); + .where(eq(Relationships.id, foundRelationship.id)); } return jsonResponse(relationshipToAPI(foundRelationship)); 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 bce07eda..8ce9c3a6 100644 --- a/server/api/api/v1/accounts/[id]/remove_from_followers.ts +++ b/server/api/api/v1/accounts/[id]/remove_from_followers.ts @@ -7,7 +7,7 @@ import { getRelationshipToOtherUser, } from "~database/entities/User"; import { db } from "~drizzle/db"; -import { relationship } from "~drizzle/schema"; +import { Relationships } from "~drizzle/schema"; export const meta = applyConfig({ allowedMethods: ["POST"], @@ -48,23 +48,23 @@ export default apiRoute(async (req, matchedRoute, extraData) => { foundRelationship.followedBy = false; await db - .update(relationship) + .update(Relationships) .set({ followedBy: false, }) - .where(eq(relationship.id, foundRelationship.id)); + .where(eq(Relationships.id, foundRelationship.id)); if (otherUser.instanceId === null) { // Also remove from followers list await db - .update(relationship) + .update(Relationships) .set({ following: false, }) .where( and( - eq(relationship.ownerId, otherUser.id), - eq(relationship.subjectId, self.id), + eq(Relationships.ownerId, otherUser.id), + eq(Relationships.subjectId, self.id), ), ); } diff --git a/server/api/api/v1/accounts/[id]/statuses.test.ts b/server/api/api/v1/accounts/[id]/statuses.test.ts new file mode 100644 index 00000000..d3301b10 --- /dev/null +++ b/server/api/api/v1/accounts/[id]/statuses.test.ts @@ -0,0 +1,200 @@ +import { afterAll, beforeAll, describe, expect, test } from "bun:test"; +import { config } from "config-manager"; +import { + deleteOldTestUsers, + getTestStatuses, + getTestUsers, + sendTestRequest, +} from "~tests/utils"; +import type { Account as APIAccount } from "~types/mastodon/account"; +import { meta } from "./statuses"; +import type { Status as APIStatus } from "~types/mastodon/status"; +import { db } from "~drizzle/db"; + +await deleteOldTestUsers(); + +const { users, tokens, deleteUsers } = await getTestUsers(5); +const timeline = (await getTestStatuses(40, users[1])).toReversed(); +const timeline2 = (await getTestStatuses(40, users[2])).toReversed(); + +afterAll(async () => { + await deleteUsers(); +}); + +beforeAll(async () => { + const response = await sendTestRequest( + new Request( + new URL( + `/api/v1/statuses/${timeline2[0].id}/reblog`, + config.http.base_url, + ), + { + method: "POST", + headers: { + Authorization: `Bearer ${tokens[1].accessToken}`, + }, + }, + ), + ); + + expect(response.status).toBe(200); +}); + +// /api/v1/accounts/:id/statuses +describe(meta.route, () => { + test("should return 200 with statuses", async () => { + const response = await sendTestRequest( + new Request( + new URL( + meta.route.replace(":id", users[1].id), + config.http.base_url, + ), + { + headers: { + Authorization: `Bearer ${tokens[0].accessToken}`, + }, + }, + ), + ); + + expect(response.status).toBe(200); + + const data = (await response.json()) as APIStatus[]; + + expect(data.length).toBe(20); + // Should have reblogs + expect(data[0].reblog).toBeDefined(); + }); + + test("should exclude reblogs", async () => { + const response = await sendTestRequest( + new Request( + new URL( + `${meta.route.replace( + ":id", + users[1].id, + )}?exclude_reblogs=true`, + config.http.base_url, + ), + { + headers: { + Authorization: `Bearer ${tokens[0].accessToken}`, + }, + }, + ), + ); + + expect(response.status).toBe(200); + + const data = (await response.json()) as APIStatus[]; + + expect(data.length).toBe(20); + // Should not have reblogs + expect(data[0].reblog).toBeNull(); + }); + + test("should exclude replies", async () => { + // Create reply + const replyResponse = await sendTestRequest( + new Request(new URL("/api/v1/statuses", config.http.base_url), { + method: "POST", + headers: { + Authorization: `Bearer ${tokens[1].accessToken}`, + "Content-Type": "application/json", + }, + body: JSON.stringify({ + status: "Reply", + in_reply_to_id: timeline[0].id, + federate: false, + }), + }), + ); + + expect(replyResponse.status).toBe(200); + + const response = await sendTestRequest( + new Request( + new URL( + `${meta.route.replace( + ":id", + users[1].id, + )}?exclude_replies=true`, + config.http.base_url, + ), + { + headers: { + Authorization: `Bearer ${tokens[0].accessToken}`, + }, + }, + ), + ); + + expect(response.status).toBe(200); + + const data = (await response.json()) as APIStatus[]; + + expect(data.length).toBe(20); + // Should not have replies + expect(data[0].in_reply_to_id).toBeNull(); + }); + + test("should only include pins", async () => { + const response = await sendTestRequest( + new Request( + new URL( + `${meta.route.replace(":id", users[1].id)}?pinned=true`, + config.http.base_url, + ), + { + headers: { + Authorization: `Bearer ${tokens[0].accessToken}`, + }, + }, + ), + ); + + expect(response.status).toBe(200); + + const data = (await response.json()) as APIStatus[]; + + expect(data.length).toBe(0); + + // Create pin + const pinResponse = await sendTestRequest( + new Request( + new URL( + `/api/v1/statuses/${timeline[3].id}/pin`, + config.http.base_url, + ), + { + method: "POST", + headers: { + Authorization: `Bearer ${tokens[1].accessToken}`, + }, + }, + ), + ); + + expect(pinResponse.status).toBe(200); + + const response2 = await sendTestRequest( + new Request( + new URL( + `${meta.route.replace(":id", users[1].id)}?pinned=true`, + config.http.base_url, + ), + { + headers: { + Authorization: `Bearer ${tokens[0].accessToken}`, + }, + }, + ), + ); + + expect(response2.status).toBe(200); + + const data2 = (await response2.json()) as APIStatus[]; + + expect(data2.length).toBe(1); + }); +}); diff --git a/server/api/api/v1/accounts/[id]/statuses.ts b/server/api/api/v1/accounts/[id]/statuses.ts index fbab9f89..a4d348ce 100644 --- a/server/api/api/v1/accounts/[id]/statuses.ts +++ b/server/api/api/v1/accounts/[id]/statuses.ts @@ -3,7 +3,7 @@ import { errorResponse, jsonResponse } from "@response"; import { and, eq, gt, gte, isNull, lt, sql } from "drizzle-orm"; import { z } from "zod"; import { findFirstUser } from "~database/entities/User"; -import { status } from "~drizzle/schema"; +import { Notes } from "~drizzle/schema"; import { Timeline } from "~packages/database-interface/timeline"; export const meta = applyConfig({ @@ -41,7 +41,6 @@ export default apiRoute( return errorResponse("Invalid ID, must be of type UUIDv7", 404); } - // TODO: Add pinned const { max_id, min_id, @@ -49,6 +48,7 @@ export default apiRoute( limit, exclude_reblogs, only_media, + exclude_replies, pinned, } = extraData.parsedRequest; @@ -58,41 +58,20 @@ export default apiRoute( if (!user) return errorResponse("User not found", 404); - if (pinned) { - const { objects, link } = await Timeline.getNoteTimeline( - and( - max_id ? lt(status.id, max_id) : undefined, - since_id ? gte(status.id, since_id) : undefined, - min_id ? gt(status.id, min_id) : undefined, - eq(status.authorId, id), - sql`EXISTS (SELECT 1 FROM "UserToPinnedNotes" WHERE "UserToPinnedNotes"."statusId" = ${status.id} AND "UserToPinnedNotes"."userId" = ${user.id})`, - only_media - ? sql`EXISTS (SELECT 1 FROM "Attachment" WHERE "Attachment"."statusId" = ${status.id})` - : undefined, - ), - limit, - req.url, - ); - - return jsonResponse( - await Promise.all(objects.map((note) => note.toAPI(user))), - 200, - { - Link: link, - }, - ); - } - const { objects, link } = await Timeline.getNoteTimeline( and( - max_id ? lt(status.id, max_id) : undefined, - since_id ? gte(status.id, since_id) : undefined, - min_id ? gt(status.id, min_id) : undefined, - eq(status.authorId, id), + max_id ? lt(Notes.id, max_id) : undefined, + since_id ? gte(Notes.id, since_id) : undefined, + min_id ? gt(Notes.id, min_id) : undefined, + eq(Notes.authorId, id), only_media - ? sql`EXISTS (SELECT 1 FROM "Attachment" WHERE "Attachment"."statusId" = ${status.id})` + ? sql`EXISTS (SELECT 1 FROM "Attachments" WHERE "Attachments"."noteId" = ${Notes.id})` : undefined, - exclude_reblogs ? isNull(status.reblogId) : undefined, + pinned + ? sql`EXISTS (SELECT 1 FROM "UserToPinnedNotes" WHERE "UserToPinnedNotes"."noteId" = ${Notes.id} AND "UserToPinnedNotes"."userId" = ${user.id})` + : undefined, + exclude_reblogs ? isNull(Notes.reblogId) : undefined, + exclude_replies ? isNull(Notes.replyId) : undefined, ), limit, req.url, diff --git a/server/api/api/v1/accounts/[id]/unblock.ts b/server/api/api/v1/accounts/[id]/unblock.ts index 309970f0..535127de 100644 --- a/server/api/api/v1/accounts/[id]/unblock.ts +++ b/server/api/api/v1/accounts/[id]/unblock.ts @@ -7,7 +7,7 @@ import { getRelationshipToOtherUser, } from "~database/entities/User"; import { db } from "~drizzle/db"; -import { relationship } from "~drizzle/schema"; +import { Relationships } from "~drizzle/schema"; export const meta = applyConfig({ allowedMethods: ["POST"], @@ -44,11 +44,11 @@ export default apiRoute(async (req, matchedRoute, extraData) => { foundRelationship.blocking = false; await db - .update(relationship) + .update(Relationships) .set({ blocking: false, }) - .where(eq(relationship.id, foundRelationship.id)); + .where(eq(Relationships.id, foundRelationship.id)); } return jsonResponse(relationshipToAPI(foundRelationship)); diff --git a/server/api/api/v1/accounts/[id]/unfollow.ts b/server/api/api/v1/accounts/[id]/unfollow.ts index 0287aeaa..dccaba3a 100644 --- a/server/api/api/v1/accounts/[id]/unfollow.ts +++ b/server/api/api/v1/accounts/[id]/unfollow.ts @@ -7,7 +7,7 @@ import { getRelationshipToOtherUser, } from "~database/entities/User"; import { db } from "~drizzle/db"; -import { relationship } from "~drizzle/schema"; +import { Relationships } from "~drizzle/schema"; export const meta = applyConfig({ allowedMethods: ["POST"], @@ -48,12 +48,12 @@ export default apiRoute(async (req, matchedRoute, extraData) => { foundRelationship.following = false; await db - .update(relationship) + .update(Relationships) .set({ following: false, requested: false, }) - .where(eq(relationship.id, foundRelationship.id)); + .where(eq(Relationships.id, foundRelationship.id)); } return jsonResponse(relationshipToAPI(foundRelationship)); diff --git a/server/api/api/v1/accounts/[id]/unmute.test.ts b/server/api/api/v1/accounts/[id]/unmute.test.ts new file mode 100644 index 00000000..798be4ad --- /dev/null +++ b/server/api/api/v1/accounts/[id]/unmute.test.ts @@ -0,0 +1,115 @@ +import { afterAll, beforeAll, describe, expect, test } from "bun:test"; +import { config } from "config-manager"; +import { + deleteOldTestUsers, + getTestUsers, + sendTestRequest, +} from "~tests/utils"; +import { meta } from "./unmute"; +import type { Relationship as APIRelationship } from "~types/mastodon/relationship"; + +await deleteOldTestUsers(); + +const { users, tokens, deleteUsers } = await getTestUsers(2); + +afterAll(async () => { + await deleteUsers(); +}); + +beforeAll(async () => { + await sendTestRequest( + new Request( + new URL( + `/api/v1/accounts/${users[0].id}/mute`, + config.http.base_url, + ), + { + method: "POST", + headers: { + Authorization: `Bearer ${tokens[1].accessToken}`, + }, + }, + ), + ); +}); + +// /api/v1/accounts/:id/unmute +describe(meta.route, () => { + test("should return 401 if not authenticated", async () => { + const response = await sendTestRequest( + new Request( + new URL( + meta.route.replace(":id", users[1].id), + config.http.base_url, + ), + { + method: "POST", + }, + ), + ); + expect(response.status).toBe(401); + }); + + test("should return 404 if user not found", async () => { + const response = await sendTestRequest( + new Request( + new URL( + meta.route.replace( + ":id", + "00000000-0000-0000-0000-000000000000", + ), + config.http.base_url, + ), + { + method: "POST", + headers: { + Authorization: `Bearer ${tokens[0].accessToken}`, + }, + }, + ), + ); + expect(response.status).toBe(404); + }); + + test("should unmute user", async () => { + const response = await sendTestRequest( + new Request( + new URL( + meta.route.replace(":id", users[1].id), + config.http.base_url, + ), + { + method: "POST", + headers: { + Authorization: `Bearer ${tokens[0].accessToken}`, + }, + }, + ), + ); + expect(response.status).toBe(200); + + const relationship = (await response.json()) as APIRelationship; + expect(relationship.muting).toBe(false); + }); + + test("should return 200 if user already unmuted", async () => { + const response = await sendTestRequest( + new Request( + new URL( + meta.route.replace(":id", users[1].id), + config.http.base_url, + ), + { + method: "POST", + headers: { + Authorization: `Bearer ${tokens[0].accessToken}`, + }, + }, + ), + ); + expect(response.status).toBe(200); + + const relationship = (await response.json()) as APIRelationship; + expect(relationship.muting).toBe(false); + }); +}); diff --git a/server/api/api/v1/accounts/[id]/unmute.ts b/server/api/api/v1/accounts/[id]/unmute.ts index 1e448329..22116528 100644 --- a/server/api/api/v1/accounts/[id]/unmute.ts +++ b/server/api/api/v1/accounts/[id]/unmute.ts @@ -7,7 +7,7 @@ import { getRelationshipToOtherUser, } from "~database/entities/User"; import { db } from "~drizzle/db"; -import { relationship } from "~drizzle/schema"; +import { Relationships } from "~drizzle/schema"; export const meta = applyConfig({ allowedMethods: ["POST"], @@ -49,12 +49,12 @@ export default apiRoute(async (req, matchedRoute, extraData) => { foundRelationship.mutingNotifications = false; await db - .update(relationship) + .update(Relationships) .set({ muting: false, mutingNotifications: false, }) - .where(eq(relationship.id, foundRelationship.id)); + .where(eq(Relationships.id, foundRelationship.id)); } return jsonResponse(relationshipToAPI(foundRelationship)); diff --git a/server/api/api/v1/accounts/[id]/unpin.ts b/server/api/api/v1/accounts/[id]/unpin.ts index fb21d450..291e6b13 100644 --- a/server/api/api/v1/accounts/[id]/unpin.ts +++ b/server/api/api/v1/accounts/[id]/unpin.ts @@ -7,7 +7,7 @@ import { getRelationshipToOtherUser, } from "~database/entities/User"; import { db } from "~drizzle/db"; -import { relationship } from "~drizzle/schema"; +import { Relationships } from "~drizzle/schema"; export const meta = applyConfig({ allowedMethods: ["POST"], @@ -48,11 +48,11 @@ export default apiRoute(async (req, matchedRoute, extraData) => { foundRelationship.endorsed = false; await db - .update(relationship) + .update(Relationships) .set({ endorsed: false, }) - .where(eq(relationship.id, foundRelationship.id)); + .where(eq(Relationships.id, foundRelationship.id)); } return jsonResponse(relationshipToAPI(foundRelationship)); diff --git a/server/api/api/v1/accounts/familiar_followers/index.ts b/server/api/api/v1/accounts/familiar_followers/index.ts index 70818a1e..c24b4a8d 100644 --- a/server/api/api/v1/accounts/familiar_followers/index.ts +++ b/server/api/api/v1/accounts/familiar_followers/index.ts @@ -32,7 +32,7 @@ export default apiRoute( const { id: ids } = extraData.parsedRequest; - const idFollowerRelationships = await db.query.relationship.findMany({ + const idFollowerRelationships = await db.query.Relationships.findMany({ columns: { ownerId: true, }, @@ -48,7 +48,7 @@ export default apiRoute( } // Find users that you follow in idFollowerRelationships - const relevantRelationships = await db.query.relationship.findMany({ + const relevantRelationships = await db.query.Relationships.findMany({ columns: { subjectId: true, }, diff --git a/server/api/api/v1/accounts/relationships/index.ts b/server/api/api/v1/accounts/relationships/index.ts index d922a51d..9d57afa9 100644 --- a/server/api/api/v1/accounts/relationships/index.ts +++ b/server/api/api/v1/accounts/relationships/index.ts @@ -36,7 +36,7 @@ export default apiRoute( const { id: ids } = extraData.parsedRequest; - const relationships = await db.query.relationship.findMany({ + const relationships = await db.query.Relationships.findMany({ where: (relationship, { inArray, and, eq }) => and( inArray(relationship.subjectId, ids), diff --git a/server/api/api/v1/accounts/search/index.ts b/server/api/api/v1/accounts/search/index.ts index b1b9e15f..4c88529a 100644 --- a/server/api/api/v1/accounts/search/index.ts +++ b/server/api/api/v1/accounts/search/index.ts @@ -19,7 +19,7 @@ import { resolveWebFinger, userToAPI, } from "~database/entities/User"; -import { user } from "~drizzle/schema"; +import { Users } from "~drizzle/schema"; export const meta = applyConfig({ allowedMethods: ["GET"], @@ -94,7 +94,7 @@ export default apiRoute( like(account.displayName, `%${q}%`), like(account.username, `%${q}%`), following - ? sql`EXISTS (SELECT 1 FROM "Relationship" WHERE "Relationship"."subjectId" = ${user.id} AND "Relationship"."ownerId" = ${account.id} AND "Relationship"."following" = true)` + ? sql`EXISTS (SELECT 1 FROM "Relationships" WHERE "Relationships"."subjectId" = ${Users.id} AND "Relationships"."ownerId" = ${account.id} AND "Relationships"."following" = true)` : undefined, ), offset, diff --git a/server/api/api/v1/accounts/update_credentials/index.ts b/server/api/api/v1/accounts/update_credentials/index.ts index 403a693d..c56768da 100644 --- a/server/api/api/v1/accounts/update_credentials/index.ts +++ b/server/api/api/v1/accounts/update_credentials/index.ts @@ -13,7 +13,7 @@ import { getUrl } from "~database/entities/Attachment"; import { parseEmojis } from "~database/entities/Emoji"; import { findFirstUser, userToAPI } from "~database/entities/User"; import { db } from "~drizzle/db"; -import { emojiToUser, user } from "~drizzle/schema"; +import { EmojiToUser, Users } from "~drizzle/schema"; import type { Source as APISource } from "~types/mastodon/source"; export const meta = applyConfig({ @@ -194,7 +194,7 @@ export default apiRoute( ); await db - .update(user) + .update(Users) .set({ displayName: self.displayName, note: self.note, @@ -205,22 +205,22 @@ export default apiRoute( isDiscoverable: self.isDiscoverable, source: self.source || undefined, }) - .where(eq(user.id, self.id)); + .where(eq(Users.id, self.id)); // Connect emojis, if any for (const emoji of self.emojis) { await db - .delete(emojiToUser) + .delete(EmojiToUser) .where( and( - eq(emojiToUser.emojiId, emoji.id), - eq(emojiToUser.userId, self.id), + eq(EmojiToUser.emojiId, emoji.id), + eq(EmojiToUser.userId, self.id), ), ) .execute(); await db - .insert(emojiToUser) + .insert(EmojiToUser) .values({ emojiId: emoji.id, userId: self.id, diff --git a/server/api/api/v1/apps/index.ts b/server/api/api/v1/apps/index.ts index 74e000b0..a6d24bf8 100644 --- a/server/api/api/v1/apps/index.ts +++ b/server/api/api/v1/apps/index.ts @@ -3,7 +3,7 @@ import { apiRoute, applyConfig } from "@api"; import { errorResponse, jsonResponse } from "@response"; import { z } from "zod"; import { db } from "~drizzle/db"; -import { application } from "~drizzle/schema"; +import { Applications } from "~drizzle/schema"; export const meta = applyConfig({ allowedMethods: ["POST"], @@ -34,7 +34,7 @@ export default apiRoute( const app = ( await db - .insert(application) + .insert(Applications) .values({ name: client_name || "", redirectUris: redirect_uris || "", diff --git a/server/api/api/v1/blocks/index.ts b/server/api/api/v1/blocks/index.ts index bcf09bf2..b8553bd4 100644 --- a/server/api/api/v1/blocks/index.ts +++ b/server/api/api/v1/blocks/index.ts @@ -46,7 +46,7 @@ export default apiRoute( max_id ? lt(subject.id, max_id) : undefined, since_id ? gte(subject.id, since_id) : undefined, min_id ? gt(subject.id, min_id) : undefined, - sql`EXISTS (SELECT 1 FROM "Relationship" WHERE "Relationship"."subjectId" = ${subject.id} AND "Relationship"."ownerId" = ${user.id} AND "Relationship"."blocking" = true)`, + sql`EXISTS (SELECT 1 FROM "Relationships" WHERE "Relationships"."subjectId" = ${subject.id} AND "Relationships"."ownerId" = ${user.id} AND "Relationships"."blocking" = true)`, ), limit, // @ts-expect-error Yes I KNOW the types are wrong diff --git a/server/api/api/v1/custom_emojis/index.ts b/server/api/api/v1/custom_emojis/index.ts index 99916cee..f6a2ab7d 100644 --- a/server/api/api/v1/custom_emojis/index.ts +++ b/server/api/api/v1/custom_emojis/index.ts @@ -16,7 +16,7 @@ export const meta = applyConfig({ }); export default apiRoute(async () => { - const emojis = await db.query.emoji.findMany({ + const emojis = await db.query.Emojis.findMany({ where: (emoji, { isNull }) => isNull(emoji.instanceId), with: { instance: true, diff --git a/server/api/api/v1/favourites/index.ts b/server/api/api/v1/favourites/index.ts index ac8f6074..2ca0146d 100644 --- a/server/api/api/v1/favourites/index.ts +++ b/server/api/api/v1/favourites/index.ts @@ -2,7 +2,7 @@ import { apiRoute, applyConfig, idValidator } from "@api"; import { errorResponse, jsonResponse } from "@response"; import { and, gt, gte, lt, sql } from "drizzle-orm"; import { z } from "zod"; -import { status } from "~drizzle/schema"; +import { Notes } from "~drizzle/schema"; import { Timeline } from "~packages/database-interface/timeline"; export const meta = applyConfig({ @@ -34,10 +34,10 @@ export default apiRoute( const { objects, link } = await Timeline.getNoteTimeline( and( - max_id ? lt(status.id, max_id) : undefined, - since_id ? gte(status.id, since_id) : undefined, - min_id ? gt(status.id, min_id) : undefined, - sql`EXISTS (SELECT 1 FROM "Like" WHERE "Like"."likedId" = ${status.id} AND "Like"."likerId" = ${user.id})`, + max_id ? lt(Notes.id, max_id) : undefined, + since_id ? gte(Notes.id, since_id) : undefined, + min_id ? gt(Notes.id, min_id) : undefined, + sql`EXISTS (SELECT 1 FROM "Likes" WHERE "Likes"."likedId" = ${Notes.id} AND "Likes"."likerId" = ${user.id})`, ), limit, req.url, diff --git a/server/api/api/v1/follow_requests/[account_id]/authorize.ts b/server/api/api/v1/follow_requests/[account_id]/authorize.ts index d4dda44b..48db4323 100644 --- a/server/api/api/v1/follow_requests/[account_id]/authorize.ts +++ b/server/api/api/v1/follow_requests/[account_id]/authorize.ts @@ -11,7 +11,7 @@ import { sendFollowAccept, } from "~database/entities/User"; import { db } from "~drizzle/db"; -import { relationship } from "~drizzle/schema"; +import { Relationships } from "~drizzle/schema"; export const meta = applyConfig({ allowedMethods: ["POST"], @@ -43,28 +43,28 @@ export default apiRoute(async (req, matchedRoute, extraData) => { // Authorize follow request await db - .update(relationship) + .update(Relationships) .set({ requested: false, following: true, }) .where( and( - eq(relationship.subjectId, user.id), - eq(relationship.ownerId, account.id), + eq(Relationships.subjectId, user.id), + eq(Relationships.ownerId, account.id), ), ); // Update followedBy for other user await db - .update(relationship) + .update(Relationships) .set({ followedBy: true, }) .where( and( - eq(relationship.subjectId, account.id), - eq(relationship.ownerId, user.id), + eq(Relationships.subjectId, account.id), + eq(Relationships.ownerId, user.id), ), ); diff --git a/server/api/api/v1/follow_requests/[account_id]/reject.ts b/server/api/api/v1/follow_requests/[account_id]/reject.ts index c41eb96d..356be9ae 100644 --- a/server/api/api/v1/follow_requests/[account_id]/reject.ts +++ b/server/api/api/v1/follow_requests/[account_id]/reject.ts @@ -11,7 +11,7 @@ import { sendFollowReject, } from "~database/entities/User"; import { db } from "~drizzle/db"; -import { relationship } from "~drizzle/schema"; +import { Relationships } from "~drizzle/schema"; export const meta = applyConfig({ allowedMethods: ["POST"], @@ -43,28 +43,28 @@ export default apiRoute(async (req, matchedRoute, extraData) => { // Reject follow request await db - .update(relationship) + .update(Relationships) .set({ requested: false, following: false, }) .where( and( - eq(relationship.subjectId, user.id), - eq(relationship.ownerId, account.id), + eq(Relationships.subjectId, user.id), + eq(Relationships.ownerId, account.id), ), ); // Update followedBy for other user await db - .update(relationship) + .update(Relationships) .set({ followedBy: false, }) .where( and( - eq(relationship.subjectId, account.id), - eq(relationship.ownerId, user.id), + eq(Relationships.subjectId, account.id), + eq(Relationships.ownerId, user.id), ), ); diff --git a/server/api/api/v1/follow_requests/index.ts b/server/api/api/v1/follow_requests/index.ts index fdc089fe..ef0c94e9 100644 --- a/server/api/api/v1/follow_requests/index.ts +++ b/server/api/api/v1/follow_requests/index.ts @@ -44,7 +44,7 @@ export default apiRoute( max_id ? lt(subject.id, max_id) : undefined, since_id ? gte(subject.id, since_id) : undefined, min_id ? gt(subject.id, min_id) : undefined, - sql`EXISTS (SELECT 1 FROM "Relationship" WHERE "Relationship"."subjectId" = ${user.id} AND "Relationship"."ownerId" = ${subject.id} AND "Relationship"."requested" = true)`, + sql`EXISTS (SELECT 1 FROM "Relationships" WHERE "Relationships"."subjectId" = ${user.id} AND "Relationships"."ownerId" = ${subject.id} AND "Relationships"."requested" = true)`, ), limit, // @ts-expect-error Yes I KNOW the types are wrong diff --git a/server/api/api/v1/instance/index.ts b/server/api/api/v1/instance/index.ts index 78d761e2..53fce16a 100644 --- a/server/api/api/v1/instance/index.ts +++ b/server/api/api/v1/instance/index.ts @@ -3,7 +3,7 @@ import { jsonResponse } from "@response"; import { and, count, countDistinct, eq, gte, isNull, sql } from "drizzle-orm"; import { findFirstUser, userToAPI } from "~database/entities/User"; import { db } from "~drizzle/db"; -import { instance, status, user } from "~drizzle/schema"; +import { Instances, Notes, Users } from "~drizzle/schema"; import manifest from "~package.json"; import type { Instance as APIInstance } from "~types/mastodon/instance"; @@ -30,9 +30,9 @@ export default apiRoute(async (req, matchedRoute, extraData) => { .select({ count: count(), }) - .from(status) + .from(Notes) .where( - sql`EXISTS (SELECT 1 FROM "User" WHERE "User"."id" = ${status.authorId} AND "User"."instanceId" IS NULL)`, + sql`EXISTS (SELECT 1 FROM "User" WHERE "User"."id" = ${Notes.authorId} AND "User"."instanceId" IS NULL)`, ) )[0].count; @@ -41,8 +41,8 @@ export default apiRoute(async (req, matchedRoute, extraData) => { .select({ count: count(), }) - .from(user) - .where(isNull(user.instanceId)) + .from(Users) + .where(isNull(Users.instanceId)) )[0].count; const contactAccount = await findFirstUser({ @@ -54,15 +54,15 @@ export default apiRoute(async (req, matchedRoute, extraData) => { const monthlyActiveUsers = ( await db .select({ - count: countDistinct(user), + count: countDistinct(Users), }) - .from(user) - .leftJoin(status, eq(user.id, status.authorId)) + .from(Users) + .leftJoin(Notes, eq(Users.id, Notes.authorId)) .where( and( - isNull(user.instanceId), + isNull(Users.instanceId), gte( - status.createdAt, + Notes.createdAt, new Date( Date.now() - 30 * 24 * 60 * 60 * 1000, ).toISOString(), @@ -76,7 +76,7 @@ export default apiRoute(async (req, matchedRoute, extraData) => { .select({ count: count(), }) - .from(instance) + .from(Instances) )[0].count; // TODO: fill in more values diff --git a/server/api/api/v1/media/[id]/index.ts b/server/api/api/v1/media/[id]/index.ts index f80efa8b..136faa67 100644 --- a/server/api/api/v1/media/[id]/index.ts +++ b/server/api/api/v1/media/[id]/index.ts @@ -8,7 +8,7 @@ import { LocalMediaBackend, S3MediaBackend } from "media-manager"; import { z } from "zod"; import { attachmentToAPI, getUrl } from "~database/entities/Attachment"; import { db } from "~drizzle/db"; -import { attachment } from "~drizzle/schema"; +import { Attachments } from "~drizzle/schema"; export const meta = applyConfig({ allowedMethods: ["GET", "PUT"], @@ -48,7 +48,7 @@ export default apiRoute( return errorResponse("Invalid ID, must be of type UUIDv7", 404); } - const foundAttachment = await db.query.attachment.findFirst({ + const foundAttachment = await db.query.Attachments.findFirst({ where: (attachment, { eq }) => eq(attachment.id, id), }); @@ -98,12 +98,12 @@ export default apiRoute( ) { const newAttachment = ( await db - .update(attachment) + .update(Attachments) .set({ description: descriptionText, thumbnailUrl, }) - .where(eq(attachment.id, id)) + .where(eq(Attachments.id, id)) .returning() )[0]; diff --git a/server/api/api/v1/media/index.ts b/server/api/api/v1/media/index.ts index 2349f94d..35af055b 100644 --- a/server/api/api/v1/media/index.ts +++ b/server/api/api/v1/media/index.ts @@ -9,7 +9,7 @@ import sharp from "sharp"; import { z } from "zod"; import { attachmentToAPI, getUrl } from "~database/entities/Attachment"; import { db } from "~drizzle/db"; -import { attachment } from "~drizzle/schema"; +import { Attachments } from "~drizzle/schema"; export const meta = applyConfig({ allowedMethods: ["POST"], @@ -128,7 +128,7 @@ export default apiRoute( const newAttachment = ( await db - .insert(attachment) + .insert(Attachments) .values({ url, thumbnailUrl, diff --git a/server/api/api/v1/mutes/index.test.ts b/server/api/api/v1/mutes/index.test.ts new file mode 100644 index 00000000..77405fca --- /dev/null +++ b/server/api/api/v1/mutes/index.test.ts @@ -0,0 +1,102 @@ +import { afterAll, beforeAll, describe, expect, test } from "bun:test"; +import { config } from "config-manager"; +import { + deleteOldTestUsers, + getTestUsers, + sendTestRequest, +} from "~tests/utils"; +import { meta } from "./index"; + +await deleteOldTestUsers(); + +const { users, tokens, deleteUsers } = await getTestUsers(3); + +afterAll(async () => { + await deleteUsers(); +}); + +beforeAll(async () => { + const response = await sendTestRequest( + new Request( + new URL( + `/api/v1/accounts/${users[1].id}/mute`, + config.http.base_url, + ), + { + method: "POST", + headers: { + Authorization: `Bearer ${tokens[0].accessToken}`, + }, + }, + ), + ); + expect(response.status).toBe(200); +}); + +// /api/v1/mutes +describe(meta.route, () => { + test("should return 401 if not authenticated", async () => { + const response = await sendTestRequest( + new Request( + new URL( + meta.route.replace(":id", users[1].id), + config.http.base_url, + ), + { + method: "GET", + }, + ), + ); + expect(response.status).toBe(401); + }); + + test("should return mutes", async () => { + const response = await sendTestRequest( + new Request(new URL(meta.route, config.http.base_url), { + method: "GET", + headers: { + Authorization: `Bearer ${tokens[0].accessToken}`, + }, + }), + ); + expect(response.status).toBe(200); + const body = await response.json(); + expect(body).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + id: users[1].id, + }), + ]), + ); + }); + + test("should return mutes after unmute", async () => { + const response = await sendTestRequest( + new Request( + new URL( + `/api/v1/accounts/${users[1].id}/unmute`, + config.http.base_url, + ), + { + method: "POST", + headers: { + Authorization: `Bearer ${tokens[0].accessToken}`, + }, + }, + ), + ); + expect(response.status).toBe(200); + + const response2 = await sendTestRequest( + new Request(new URL(meta.route, config.http.base_url), { + method: "GET", + headers: { + Authorization: `Bearer ${tokens[0].accessToken}`, + }, + }), + ); + expect(response2.status).toBe(200); + const body = await response2.json(); + expect(body).toEqual([]); + }); +}); diff --git a/server/api/api/v1/mutes/index.ts b/server/api/api/v1/mutes/index.ts index 0135fa95..68800c79 100644 --- a/server/api/api/v1/mutes/index.ts +++ b/server/api/api/v1/mutes/index.ts @@ -45,7 +45,7 @@ export default apiRoute( max_id ? lt(subject.id, max_id) : undefined, since_id ? gte(subject.id, since_id) : undefined, min_id ? gt(subject.id, min_id) : undefined, - sql`EXISTS (SELECT 1 FROM "Relationship" WHERE "Relationship"."subjectId" = ${subject.id} AND "Relationship"."ownerId" = ${user.id} AND "Relationship"."muting" = true)`, + sql`EXISTS (SELECT 1 FROM "Relationships" WHERE "Relationships"."subjectId" = ${subject.id} AND "Relationships"."ownerId" = ${user.id} AND "Relationships"."muting" = true)`, ), limit, // @ts-expect-error Yes I KNOW the types are wrong diff --git a/server/api/api/v1/notifications/[id]/dismiss.ts b/server/api/api/v1/notifications/[id]/dismiss.ts index 71863e2d..a4bea9e8 100644 --- a/server/api/api/v1/notifications/[id]/dismiss.ts +++ b/server/api/api/v1/notifications/[id]/dismiss.ts @@ -2,7 +2,7 @@ import { apiRoute, applyConfig, idValidator } from "@api"; import { errorResponse, jsonResponse } from "@response"; import { eq } from "drizzle-orm"; import { db } from "~drizzle/db"; -import { notification } from "~drizzle/schema"; +import { Notifications } from "~drizzle/schema"; export const meta = applyConfig({ allowedMethods: ["POST"], @@ -27,11 +27,11 @@ export default apiRoute(async (req, matchedRoute, extraData) => { if (!user) return errorResponse("Unauthorized", 401); await db - .update(notification) + .update(Notifications) .set({ dismissed: true, }) - .where(eq(notification.id, id)); + .where(eq(Notifications.id, id)); return jsonResponse({}); }); diff --git a/server/api/api/v1/notifications/clear/index.ts b/server/api/api/v1/notifications/clear/index.ts index f87641d5..df186dfd 100644 --- a/server/api/api/v1/notifications/clear/index.ts +++ b/server/api/api/v1/notifications/clear/index.ts @@ -2,7 +2,7 @@ import { apiRoute, applyConfig } from "@api"; import { errorResponse, jsonResponse } from "@response"; import { eq } from "drizzle-orm"; import { db } from "~drizzle/db"; -import { notification } from "~drizzle/schema"; +import { Notifications } from "~drizzle/schema"; export const meta = applyConfig({ allowedMethods: ["POST"], @@ -22,11 +22,11 @@ export default apiRoute(async (req, matchedRoute, extraData) => { if (!user) return errorResponse("Unauthorized", 401); await db - .update(notification) + .update(Notifications) .set({ dismissed: true, }) - .where(eq(notification.notifiedId, user.id)); + .where(eq(Notifications.notifiedId, user.id)); return jsonResponse({}); }); diff --git a/server/api/api/v1/notifications/destroy_multiple/index.ts b/server/api/api/v1/notifications/destroy_multiple/index.ts index f34bc782..335efc85 100644 --- a/server/api/api/v1/notifications/destroy_multiple/index.ts +++ b/server/api/api/v1/notifications/destroy_multiple/index.ts @@ -3,7 +3,7 @@ import { errorResponse, jsonResponse } from "@response"; import { inArray } from "drizzle-orm"; import { z } from "zod"; import { db } from "~drizzle/db"; -import { notification } from "~drizzle/schema"; +import { Notifications } from "~drizzle/schema"; export const meta = applyConfig({ allowedMethods: ["DELETE"], @@ -30,11 +30,11 @@ export default apiRoute( const { ids } = extraData.parsedRequest; await db - .update(notification) + .update(Notifications) .set({ dismissed: true, }) - .where(inArray(notification.id, ids)); + .where(inArray(Notifications.id, ids)); return jsonResponse({}); }, diff --git a/server/api/api/v1/profile/avatar.ts b/server/api/api/v1/profile/avatar.ts index acc13991..5353b441 100644 --- a/server/api/api/v1/profile/avatar.ts +++ b/server/api/api/v1/profile/avatar.ts @@ -3,7 +3,7 @@ import { errorResponse, jsonResponse } from "@response"; import { eq } from "drizzle-orm"; import { userToAPI } from "~database/entities/User"; import { db } from "~drizzle/db"; -import { user } from "~drizzle/schema"; +import { Users } from "~drizzle/schema"; export const meta = applyConfig({ allowedMethods: ["DELETE"], @@ -25,7 +25,7 @@ export default apiRoute(async (req, matchedRoute, extraData) => { if (!self) return errorResponse("Unauthorized", 401); - await db.update(user).set({ avatar: "" }).where(eq(user.id, self.id)); + await db.update(Users).set({ avatar: "" }).where(eq(Users.id, self.id)); return jsonResponse( userToAPI({ diff --git a/server/api/api/v1/profile/header.ts b/server/api/api/v1/profile/header.ts index d9cf9b54..8038a75e 100644 --- a/server/api/api/v1/profile/header.ts +++ b/server/api/api/v1/profile/header.ts @@ -3,7 +3,7 @@ import { errorResponse, jsonResponse } from "@response"; import { eq } from "drizzle-orm"; import { userToAPI } from "~database/entities/User"; import { db } from "~drizzle/db"; -import { user } from "~drizzle/schema"; +import { Users } from "~drizzle/schema"; export const meta = applyConfig({ allowedMethods: ["DELETE"], @@ -26,7 +26,7 @@ export default apiRoute(async (req, matchedRoute, extraData) => { if (!self) return errorResponse("Unauthorized", 401); // Delete user header - await db.update(user).set({ header: "" }).where(eq(user.id, self.id)); + await db.update(Users).set({ header: "" }).where(eq(Users.id, self.id)); return jsonResponse( userToAPI({ diff --git a/server/api/api/v1/statuses/[id]/context.ts b/server/api/api/v1/statuses/[id]/context.ts index 80108838..5df74103 100644 --- a/server/api/api/v1/statuses/[id]/context.ts +++ b/server/api/api/v1/statuses/[id]/context.ts @@ -34,14 +34,14 @@ export default apiRoute(async (req, matchedRoute, extraData) => { if (!foundStatus) return errorResponse("Record not found", 404); const relations = user - ? await db.query.relationship.findMany({ + ? await db.query.Relationships.findMany({ where: (relationship, { eq }) => eq(relationship.ownerId, user.id), }) : null; const relationSubjects = user - ? await db.query.relationship.findMany({ + ? await db.query.Relationships.findMany({ where: (relationship, { eq }) => eq(relationship.subjectId, user.id), }) diff --git a/server/api/api/v1/statuses/[id]/favourite.ts b/server/api/api/v1/statuses/[id]/favourite.ts index 775fa746..c6c1575b 100644 --- a/server/api/api/v1/statuses/[id]/favourite.ts +++ b/server/api/api/v1/statuses/[id]/favourite.ts @@ -36,7 +36,7 @@ export default apiRoute(async (req, matchedRoute, extraData) => { if (!status?.isViewableByUser(user)) return errorResponse("Record not found", 404); - const existingLike = await db.query.like.findFirst({ + const existingLike = await db.query.Likes.findFirst({ where: (like, { and, eq }) => and( eq(like.likedId, status.getStatus().id), diff --git a/server/api/api/v1/statuses/[id]/favourited_by.ts b/server/api/api/v1/statuses/[id]/favourited_by.ts index fc902285..0016abb9 100644 --- a/server/api/api/v1/statuses/[id]/favourited_by.ts +++ b/server/api/api/v1/statuses/[id]/favourited_by.ts @@ -57,9 +57,9 @@ export default apiRoute( max_id ? lt(liker.id, max_id) : undefined, since_id ? gte(liker.id, since_id) : undefined, min_id ? gt(liker.id, min_id) : undefined, - sql`EXISTS (SELECT 1 FROM "Like" WHERE "Like"."likedId" = ${ + sql`EXISTS (SELECT 1 FROM "Likes" WHERE "Likes"."likedId" = ${ status.getStatus().id - } AND "Like"."likerId" = ${liker.id})`, + } AND "Likes"."likerId" = ${liker.id})`, ), // @ts-expect-error Yes I KNOW the types are wrong orderBy: (liker, { desc }) => desc(liker.id), diff --git a/server/api/api/v1/statuses/[id]/index.ts b/server/api/api/v1/statuses/[id]/index.ts index b3b5449a..72cf3282 100644 --- a/server/api/api/v1/statuses/[id]/index.ts +++ b/server/api/api/v1/statuses/[id]/index.ts @@ -121,7 +121,7 @@ export default apiRoute( // Check if media attachments are all valid if (media_ids && media_ids.length > 0) { - const foundAttachments = await db.query.attachment.findMany({ + const foundAttachments = await db.query.Attachments.findMany({ where: (attachment, { inArray }) => inArray(attachment.id, media_ids), }); diff --git a/server/api/api/v1/statuses/[id]/pin.ts b/server/api/api/v1/statuses/[id]/pin.ts index 583dcaab..c7035353 100644 --- a/server/api/api/v1/statuses/[id]/pin.ts +++ b/server/api/api/v1/statuses/[id]/pin.ts @@ -1,7 +1,7 @@ import { apiRoute, applyConfig, idValidator } from "@api"; import { errorResponse, jsonResponse } from "@response"; import { db } from "~drizzle/db"; -import { statusToMentions } from "~drizzle/schema"; +import { NoteToMentions, UserToPinnedNotes } from "~drizzle/schema"; import { Note } from "~packages/database-interface/note"; export const meta = applyConfig({ @@ -40,10 +40,10 @@ export default apiRoute(async (req, matchedRoute, extraData) => { // Check if post is already pinned if ( - await db.query.userPinnedNotes.findFirst({ + await db.query.UserToPinnedNotes.findFirst({ where: (userPinnedNote, { and, eq }) => and( - eq(userPinnedNote.statusId, foundStatus.getStatus().id), + eq(userPinnedNote.noteId, foundStatus.getStatus().id), eq(userPinnedNote.userId, user.id), ), }) @@ -51,10 +51,7 @@ export default apiRoute(async (req, matchedRoute, extraData) => { return errorResponse("Already pinned", 422); } - await db.insert(statusToMentions).values({ - statusId: foundStatus.getStatus().id, - userId: user.id, - }); + await foundStatus.pin(user); return jsonResponse(await foundStatus.toAPI(user)); }); diff --git a/server/api/api/v1/statuses/[id]/reblog.ts b/server/api/api/v1/statuses/[id]/reblog.ts index 77ed97e2..2c77420b 100644 --- a/server/api/api/v1/statuses/[id]/reblog.ts +++ b/server/api/api/v1/statuses/[id]/reblog.ts @@ -3,7 +3,7 @@ import { errorResponse, jsonResponse } from "@response"; import { and, eq } from "drizzle-orm"; import { z } from "zod"; import { db } from "~drizzle/db"; -import { notification, status } from "~drizzle/schema"; +import { Notes, Notifications } from "~drizzle/schema"; import { Note } from "~packages/database-interface/note"; export const meta = applyConfig({ @@ -44,13 +44,12 @@ export default apiRoute( if (!foundStatus?.isViewableByUser(user)) return errorResponse("Record not found", 404); - const existingReblog = await db.query.status.findFirst({ - where: (status, { and, eq }) => - and( - eq(status.authorId, user.id), - eq(status.reblogId, status.id), - ), - }); + const existingReblog = await Note.fromSql( + and( + eq(Notes.authorId, user.id), + eq(Notes.reblogId, foundStatus.getStatus().id), + ), + ); if (existingReblog) { return errorResponse("Already reblogged", 422); @@ -77,11 +76,11 @@ export default apiRoute( // Create notification for reblog if reblogged user is on the same instance if (foundStatus.getAuthor().instanceId === user.instanceId) { - await db.insert(notification).values({ + await db.insert(Notifications).values({ accountId: user.id, notifiedId: foundStatus.getAuthor().id, type: "reblog", - statusId: foundStatus.getStatus().reblogId, + noteId: foundStatus.getStatus().reblogId, }); } diff --git a/server/api/api/v1/statuses/[id]/reblogged_by.ts b/server/api/api/v1/statuses/[id]/reblogged_by.ts index ed1df31d..703b0c64 100644 --- a/server/api/api/v1/statuses/[id]/reblogged_by.ts +++ b/server/api/api/v1/statuses/[id]/reblogged_by.ts @@ -57,9 +57,9 @@ export default apiRoute( max_id ? lt(reblogger.id, max_id) : undefined, since_id ? gte(reblogger.id, since_id) : undefined, min_id ? gt(reblogger.id, min_id) : undefined, - sql`EXISTS (SELECT 1 FROM "Status" WHERE "Status"."reblogId" = ${ + sql`EXISTS (SELECT 1 FROM "Notes" WHERE "Notes"."reblogId" = ${ status.getStatus().id - } AND "Status"."authorId" = ${reblogger.id})`, + } AND "Notes"."authorId" = ${reblogger.id})`, ), // @ts-expect-error Yes I KNOW the types are wrong orderBy: (liker, { desc }) => desc(liker.id), diff --git a/server/api/api/v1/statuses/[id]/unreblog.ts b/server/api/api/v1/statuses/[id]/unreblog.ts index f38a39ba..75680c40 100644 --- a/server/api/api/v1/statuses/[id]/unreblog.ts +++ b/server/api/api/v1/statuses/[id]/unreblog.ts @@ -1,7 +1,7 @@ import { apiRoute, applyConfig, idValidator } from "@api"; import { errorResponse, jsonResponse } from "@response"; import { and, eq } from "drizzle-orm"; -import { status } from "~drizzle/schema"; +import { Notes } from "~drizzle/schema"; import { Note } from "~packages/database-interface/note"; import type { Status as APIStatus } from "~types/mastodon/status"; @@ -38,8 +38,8 @@ export default apiRoute(async (req, matchedRoute, extraData) => { const existingReblog = await Note.fromSql( and( - eq(status.authorId, user.id), - eq(status.reblogId, foundStatus.getStatus().id), + eq(Notes.authorId, user.id), + eq(Notes.reblogId, foundStatus.getStatus().id), ), ); diff --git a/server/api/api/v1/statuses/index.ts b/server/api/api/v1/statuses/index.ts index d24f5138..09ce510e 100644 --- a/server/api/api/v1/statuses/index.ts +++ b/server/api/api/v1/statuses/index.ts @@ -70,7 +70,6 @@ export default apiRoute( const { status, media_ids, - "poll[expires_in]": expires_in, "poll[options]": options, in_reply_to_id, quote_id, @@ -119,12 +118,6 @@ export default apiRoute( sanitizedStatus = await sanitizeHtml(status ?? ""); } - // Get reply account and status if exists - const replyStatus: StatusWithRelations | null = - (await Note.fromId(in_reply_to_id ?? null))?.getStatus() ?? null; - const quote: StatusWithRelations | null = - (await Note.fromId(quote_id ?? null))?.getStatus() ?? null; - // Check if status body doesnt match filters if ( config.filters.note_content.some((filter) => status?.match(filter)) @@ -134,18 +127,31 @@ export default apiRoute( // Check if media attachments are all valid if (media_ids && media_ids.length > 0) { - const foundAttachments = await db.query.attachment - .findMany({ - where: (attachment, { inArray }) => - inArray(attachment.id, media_ids), - }) - .catch(() => []); + const foundAttachments = await db.query.Attachments.findMany({ + where: (attachment, { inArray }) => + inArray(attachment.id, media_ids), + }).catch(() => []); if (foundAttachments.length !== (media_ids ?? []).length) { return errorResponse("Invalid media IDs", 422); } } + // Check that in_reply_to_id and quote_id are real posts if provided + if (in_reply_to_id) { + const foundReply = await Note.fromId(in_reply_to_id); + if (!foundReply) { + return errorResponse("Invalid in_reply_to_id (not found)", 422); + } + } + + if (quote_id) { + const foundQuote = await Note.fromId(quote_id); + if (!foundQuote) { + return errorResponse("Invalid quote_id (not found)", 422); + } + } + const mentions = await parseTextMentions(sanitizedStatus); const newNote = await Note.fromData( diff --git a/server/api/api/v1/timelines/home.ts b/server/api/api/v1/timelines/home.ts index d3fd0941..9b4be191 100644 --- a/server/api/api/v1/timelines/home.ts +++ b/server/api/api/v1/timelines/home.ts @@ -2,7 +2,7 @@ import { apiRoute, applyConfig, idValidator } from "@api"; import { errorResponse, jsonResponse } from "@response"; import { and, eq, gt, gte, lt, or, sql } from "drizzle-orm"; import { z } from "zod"; -import { status } from "~drizzle/schema"; +import { Notes } from "~drizzle/schema"; import { Timeline } from "~packages/database-interface/timeline"; export const meta = applyConfig({ @@ -38,18 +38,16 @@ export default apiRoute( const { objects, link } = await Timeline.getNoteTimeline( and( and( - max_id ? lt(status.id, max_id) : undefined, - since_id ? gte(status.id, since_id) : undefined, - min_id ? gt(status.id, min_id) : undefined, + max_id ? lt(Notes.id, max_id) : undefined, + since_id ? gte(Notes.id, since_id) : undefined, + min_id ? gt(Notes.id, min_id) : undefined, ), or( - eq(status.authorId, user.id), - // All statuses where the user is mentioned, using table _StatusToUser which has a: status.id and b: user.id - // WHERE format (... = ...) - sql`EXISTS (SELECT 1 FROM "StatusToMentions" WHERE "StatusToMentions"."statusId" = ${status.id} AND "StatusToMentions"."userId" = ${user.id})`, + eq(Notes.authorId, user.id), + sql`EXISTS (SELECT 1 FROM "NoteToMentions" WHERE "NoteToMentions"."noteId" = ${Notes.id} AND "NoteToMentions"."userId" = ${user.id})`, // All statuses from users that the user is following // WHERE format (... = ...) - sql`EXISTS (SELECT 1 FROM "Relationship" WHERE "Relationship"."subjectId" = ${status.authorId} AND "Relationship"."ownerId" = ${user.id} AND "Relationship"."following" = true)`, + sql`EXISTS (SELECT 1 FROM "Relationships" WHERE "Relationships"."subjectId" = ${Notes.authorId} AND "Relationships"."ownerId" = ${user.id} AND "Relationships"."following" = true)`, ), ), limit, diff --git a/server/api/api/v1/timelines/public.ts b/server/api/api/v1/timelines/public.ts index c5b188df..44a027cb 100644 --- a/server/api/api/v1/timelines/public.ts +++ b/server/api/api/v1/timelines/public.ts @@ -2,7 +2,7 @@ import { apiRoute, applyConfig, idValidator } from "@api"; import { errorResponse, jsonResponse } from "@response"; import { and, gt, gte, lt, sql } from "drizzle-orm"; import { z } from "zod"; -import { status } from "~drizzle/schema"; +import { Notes } from "~drizzle/schema"; import { Timeline } from "~packages/database-interface/timeline"; export const meta = applyConfig({ @@ -39,18 +39,18 @@ export default apiRoute( const { objects, link } = await Timeline.getNoteTimeline( and( - max_id ? lt(status.id, max_id) : undefined, - since_id ? gte(status.id, since_id) : undefined, - min_id ? gt(status.id, min_id) : undefined, + max_id ? lt(Notes.id, max_id) : undefined, + since_id ? gte(Notes.id, since_id) : undefined, + min_id ? gt(Notes.id, min_id) : undefined, // use authorId to grab user, then use user.instanceId to filter local/remote statuses remote - ? sql`EXISTS (SELECT 1 FROM "User" WHERE "User"."id" = ${status.authorId} AND "User"."instanceId" IS NOT NULL)` + ? sql`EXISTS (SELECT 1 FROM "Users" WHERE "Users"."id" = ${Notes.authorId} AND "Users"."instanceId" IS NOT NULL)` : undefined, local - ? sql`EXISTS (SELECT 1 FROM "User" WHERE "User"."id" = ${status.authorId} AND "User"."instanceId" IS NULL)` + ? sql`EXISTS (SELECT 1 FROM "Users" WHERE "Users"."id" = ${Notes.authorId} AND "Users"."instanceId" IS NULL)` : undefined, only_media - ? sql`EXISTS (SELECT 1 FROM "Attachment" WHERE "Attachment"."statusId" = ${status.id})` + ? sql`EXISTS (SELECT 1 FROM "Attachments" WHERE "Attachments"."noteId" = ${Notes.id})` : undefined, ), limit, diff --git a/server/api/api/v2/instance/index.ts b/server/api/api/v2/instance/index.ts index 5d586c50..ce140d2a 100644 --- a/server/api/api/v2/instance/index.ts +++ b/server/api/api/v2/instance/index.ts @@ -3,7 +3,7 @@ import { jsonResponse } from "@response"; import { and, countDistinct, eq, gte, isNull } from "drizzle-orm"; import { findFirstUser, userToAPI } from "~database/entities/User"; import { db } from "~drizzle/db"; -import { status, user } from "~drizzle/schema"; +import { Notes, Users } from "~drizzle/schema"; import manifest from "~package.json"; export const meta = applyConfig({ @@ -32,15 +32,15 @@ export default apiRoute(async (req, matchedRoute, extraData) => { const monthlyActiveUsers = ( await db .select({ - count: countDistinct(user), + count: countDistinct(Users), }) - .from(user) - .leftJoin(status, eq(user.id, status.authorId)) + .from(Users) + .leftJoin(Notes, eq(Users.id, Notes.authorId)) .where( and( - isNull(user.instanceId), + isNull(Users.instanceId), gte( - status.createdAt, + Notes.createdAt, new Date( Date.now() - 30 * 24 * 60 * 60 * 1000, ).toISOString(), diff --git a/server/api/api/v2/media/index.ts b/server/api/api/v2/media/index.ts index 88dd7dce..18c51ac2 100644 --- a/server/api/api/v2/media/index.ts +++ b/server/api/api/v2/media/index.ts @@ -9,7 +9,7 @@ import sharp from "sharp"; import { z } from "zod"; import { attachmentToAPI, getUrl } from "~database/entities/Attachment"; import { db } from "~drizzle/db"; -import { attachment } from "~drizzle/schema"; +import { Attachments } from "~drizzle/schema"; export const meta = applyConfig({ allowedMethods: ["POST"], @@ -124,7 +124,7 @@ export default apiRoute( const newAttachment = ( await db - .insert(attachment) + .insert(Attachments) .values({ url, thumbnailUrl, diff --git a/server/api/api/v2/search/index.ts b/server/api/api/v2/search/index.ts index f129f5a7..fdaaeb3b 100644 --- a/server/api/api/v2/search/index.ts +++ b/server/api/api/v2/search/index.ts @@ -11,7 +11,7 @@ import { userToAPI, } from "~database/entities/User"; import { db } from "~drizzle/db"; -import { instance, status, user } from "~drizzle/schema"; +import { Instances, Notes, Users } from "~drizzle/schema"; import { Note } from "~packages/database-interface/note"; import { LogLevel } from "~packages/log-manager"; @@ -88,14 +88,14 @@ export default apiRoute( const accountId = ( await db .select({ - id: user.id, + id: Users.id, }) - .from(user) - .leftJoin(instance, eq(user.instanceId, instance.id)) + .from(Users) + .leftJoin(Instances, eq(Users.instanceId, Instances.id)) .where( and( - eq(user.username, username), - eq(instance.baseUrl, domain), + eq(Users.username, username), + eq(Instances.baseUrl, domain), ), ) )[0]?.id; @@ -181,16 +181,16 @@ export default apiRoute( const statuses = await Note.manyFromSql( and( inArray( - status.id, + Notes.id, statusResults.map((hit) => hit.id), ), - account_id ? eq(status.authorId, account_id) : undefined, + account_id ? eq(Notes.authorId, account_id) : undefined, self ? sql`EXISTS (SELECT 1 FROM Relationships WHERE Relationships.subjectId = ${ self?.id } AND Relationships.following = ${ following ? true : false - } AND Relationships.ownerId = ${status.authorId})` + } AND Relationships.ownerId = ${Notes.authorId})` : undefined, ), ); diff --git a/server/api/oauth/authorize-external/index.ts b/server/api/oauth/authorize-external/index.ts index 89dc0435..dd831b9f 100644 --- a/server/api/oauth/authorize-external/index.ts +++ b/server/api/oauth/authorize-external/index.ts @@ -7,7 +7,7 @@ import { processDiscoveryResponse, } from "oauth4webapi"; import { db } from "~drizzle/db"; -import { openIdLoginFlow } from "~drizzle/schema"; +import { OpenIdLoginFlows } from "~drizzle/schema"; export const meta = applyConfig({ allowedMethods: ["GET"], @@ -61,7 +61,7 @@ export default apiRoute(async (req, matchedRoute, extraData) => { const codeVerifier = generateRandomCodeVerifier(); - const application = await db.query.application.findFirst({ + const application = await db.query.Applications.findFirst({ where: (application, { eq }) => eq(application.clientId, clientId), }); @@ -72,7 +72,7 @@ export default apiRoute(async (req, matchedRoute, extraData) => { // Store into database const newFlow = ( await db - .insert(openIdLoginFlow) + .insert(OpenIdLoginFlows) .values({ codeVerifier, applicationId: application.id, diff --git a/server/api/oauth/callback/[issuer]/index.ts b/server/api/oauth/callback/[issuer]/index.ts index 4eae884b..7b2604b3 100644 --- a/server/api/oauth/callback/[issuer]/index.ts +++ b/server/api/oauth/callback/[issuer]/index.ts @@ -16,7 +16,7 @@ import { import { TokenType } from "~database/entities/Token"; import { findFirstUser } from "~database/entities/User"; import { db } from "~drizzle/db"; -import { token } from "~drizzle/schema"; +import { Tokens } from "~drizzle/schema"; export const meta = applyConfig({ allowedMethods: ["GET"], @@ -166,7 +166,7 @@ export default apiRoute(async (req, matchedRoute, extraData) => { const code = randomBytes(32).toString("hex"); - await db.insert(token).values({ + await db.insert(Tokens).values({ accessToken: randomBytes(64).toString("base64url"), code: code, scope: flow.application.scopes, diff --git a/server/api/oauth/token/index.ts b/server/api/oauth/token/index.ts index aaf7f1d1..60a79b65 100644 --- a/server/api/oauth/token/index.ts +++ b/server/api/oauth/token/index.ts @@ -45,7 +45,7 @@ export default apiRoute( ); // Get associated token - const application = await db.query.application.findFirst({ + const application = await db.query.Applications.findFirst({ where: (application, { eq, and }) => and( eq(application.clientId, client_id), @@ -61,7 +61,7 @@ export default apiRoute( 401, ); - const token = await db.query.token.findFirst({ + const token = await db.query.Tokens.findFirst({ where: (token, { eq }) => eq(token.code, code) && eq(token.applicationId, application.id), }); diff --git a/server/api/objects/[uuid]/index.ts b/server/api/objects/[uuid]/index.ts index 0e42b27b..46580aec 100644 --- a/server/api/objects/[uuid]/index.ts +++ b/server/api/objects/[uuid]/index.ts @@ -4,7 +4,7 @@ import { and, eq, inArray, sql } from "drizzle-orm"; import type * as Lysand from "lysand-types"; import { type Like, likeToLysand } from "~database/entities/Like"; import { db } from "~drizzle/db"; -import { status } from "~drizzle/schema"; +import { Notes } from "~drizzle/schema"; import { Note } from "~packages/database-interface/note"; export const meta = applyConfig({ @@ -27,15 +27,15 @@ export default apiRoute(async (req, matchedRoute) => { foundObject = await Note.fromSql( and( - eq(status.id, uuid), - inArray(status.visibility, ["public", "unlisted"]), + eq(Notes.id, uuid), + inArray(Notes.visibility, ["public", "unlisted"]), ), ); apiObject = foundObject ? foundObject.toLysand() : null; if (!foundObject) { foundObject = - (await db.query.like.findFirst({ + (await db.query.Likes.findFirst({ where: (like, { eq, and }) => and( eq(like.id, uuid), diff --git a/server/api/users/[uuid]/inbox/index.ts b/server/api/users/[uuid]/inbox/index.ts index 9705deaa..f00b971b 100644 --- a/server/api/users/[uuid]/inbox/index.ts +++ b/server/api/users/[uuid]/inbox/index.ts @@ -11,7 +11,7 @@ import { sendFollowAccept, } from "~database/entities/User"; import { db } from "~drizzle/db"; -import { notification, relationship } from "~drizzle/schema"; +import { Notifications, Relationships } from "~drizzle/schema"; import { LogLevel } from "~packages/log-manager"; export const meta = applyConfig({ @@ -161,7 +161,7 @@ export default apiRoute(async (req, matchedRoute, extraData) => { } await db - .update(relationship) + .update(Relationships) .set({ following: !user.isLocked, requested: user.isLocked, @@ -169,9 +169,9 @@ export default apiRoute(async (req, matchedRoute, extraData) => { notifying: true, languages: [], }) - .where(eq(relationship.id, foundRelationship.id)); + .where(eq(Relationships.id, foundRelationship.id)); - await db.insert(notification).values({ + await db.insert(Notifications).values({ accountId: account.id, type: user.isLocked ? "follow_request" : "follow", notifiedId: user.id, @@ -209,12 +209,12 @@ export default apiRoute(async (req, matchedRoute, extraData) => { } await db - .update(relationship) + .update(Relationships) .set({ following: true, requested: false, }) - .where(eq(relationship.id, foundRelationship.id)); + .where(eq(Relationships.id, foundRelationship.id)); return response("Follow request accepted", 200); } @@ -237,12 +237,12 @@ export default apiRoute(async (req, matchedRoute, extraData) => { } await db - .update(relationship) + .update(Relationships) .set({ requested: false, following: false, }) - .where(eq(relationship.id, foundRelationship.id)); + .where(eq(Relationships.id, foundRelationship.id)); return response("Follow request rejected", 200); } diff --git a/server/api/users/[uuid]/outbox/index.ts b/server/api/users/[uuid]/outbox/index.ts index 97a1cd5d..08d41219 100644 --- a/server/api/users/[uuid]/outbox/index.ts +++ b/server/api/users/[uuid]/outbox/index.ts @@ -2,7 +2,7 @@ import { apiRoute, applyConfig } from "@api"; import { jsonResponse } from "@response"; import { and, count, eq, inArray } from "drizzle-orm"; import { db } from "~drizzle/db"; -import { status } from "~drizzle/schema"; +import { Notes } from "~drizzle/schema"; import { Note } from "~packages/database-interface/note"; export const meta = applyConfig({ @@ -25,8 +25,8 @@ export default apiRoute(async (req, matchedRoute, extraData) => { const notes = await Note.manyFromSql( and( - eq(status.authorId, uuid), - inArray(status.visibility, ["public", "unlisted"]), + eq(Notes.authorId, uuid), + inArray(Notes.visibility, ["public", "unlisted"]), ), undefined, 20, @@ -37,11 +37,11 @@ export default apiRoute(async (req, matchedRoute, extraData) => { .select({ count: count(), }) - .from(status) + .from(Notes) .where( and( - eq(status.authorId, uuid), - inArray(status.visibility, ["public", "unlisted"]), + eq(Notes.authorId, uuid), + inArray(Notes.visibility, ["public", "unlisted"]), ), ); diff --git a/tests/api.test.ts b/tests/api.test.ts index cdaf906a..5a600e0a 100644 --- a/tests/api.test.ts +++ b/tests/api.test.ts @@ -2,7 +2,7 @@ import { afterAll, beforeAll, describe, expect, test } from "bun:test"; import { config } from "config-manager"; import { eq } from "drizzle-orm"; import { db } from "~drizzle/db"; -import { emoji } from "~drizzle/schema"; +import { Emojis } from "~drizzle/schema"; import type { Emoji as APIEmoji } from "~types/mastodon/emoji"; import type { Instance as APIInstance } from "~types/mastodon/instance"; import { getTestUsers, sendTestRequest, wrapRelativeUrl } from "./utils"; @@ -18,7 +18,7 @@ describe("API Tests", () => { describe("GET /api/v1/custom_emojis", () => { beforeAll(async () => { - await db.insert(emoji).values({ + await db.insert(Emojis).values({ shortcode: "test", url: "https://example.com/test.png", contentType: "image/png", @@ -55,7 +55,7 @@ describe("API Tests", () => { }); afterAll(async () => { - await db.delete(emoji).where(eq(emoji.shortcode, "test")); + await db.delete(Emojis).where(eq(Emojis.shortcode, "test")); }); }); }); diff --git a/tests/api/accounts.test.ts b/tests/api/accounts.test.ts index 4a6042a4..661264ff 100644 --- a/tests/api/accounts.test.ts +++ b/tests/api/accounts.test.ts @@ -124,68 +124,6 @@ describe("API Tests", () => { }); }); - describe("POST /api/v1/accounts/:id/follow", () => { - test("should follow the specified user and return an APIRelationship object", async () => { - const response = await sendTestRequest( - new Request( - wrapRelativeUrl( - `/api/v1/accounts/${user2.id}/follow`, - base_url, - ), - { - method: "POST", - headers: { - Authorization: `Bearer ${token.accessToken}`, - "Content-Type": "application/json", - }, - body: JSON.stringify({}), - }, - ), - ); - - expect(response.status).toBe(200); - expect(response.headers.get("content-type")).toBe( - "application/json", - ); - - const relationship = (await response.json()) as APIRelationship; - - expect(relationship.id).toBe(user2.id); - expect(relationship.following).toBe(true); - }); - }); - - describe("POST /api/v1/accounts/:id/unfollow", () => { - test("should unfollow the specified user and return an APIRelationship object", async () => { - const response = await sendTestRequest( - new Request( - wrapRelativeUrl( - `/api/v1/accounts/${user2.id}/unfollow`, - base_url, - ), - { - method: "POST", - headers: { - Authorization: `Bearer ${token.accessToken}`, - "Content-Type": "application/json", - }, - body: JSON.stringify({}), - }, - ), - ); - - expect(response.status).toBe(200); - expect(response.headers.get("content-type")).toBe( - "application/json", - ); - - const account = (await response.json()) as APIRelationship; - - expect(account.id).toBe(user2.id); - expect(account.following).toBe(false); - }); - }); - describe("POST /api/v1/accounts/:id/remove_from_followers", () => { test("should remove the specified user from the authenticated user's followers and return an APIRelationship object", async () => { const response = await sendTestRequest( @@ -302,123 +240,6 @@ describe("API Tests", () => { }); }); - describe("POST /api/v1/accounts/:id/mute with notifications parameter", () => { - test("should mute the specified user and return an APIRelationship object with notifications set to false", async () => { - const response = await sendTestRequest( - new Request( - wrapRelativeUrl( - `/api/v1/accounts/${user2.id}/mute`, - base_url, - ), - { - method: "POST", - headers: { - Authorization: `Bearer ${token.accessToken}`, - "Content-Type": "application/json", - }, - body: JSON.stringify({ notifications: true }), - }, - ), - ); - - expect(response.status).toBe(200); - expect(response.headers.get("content-type")).toBe( - "application/json", - ); - - const account = (await response.json()) as APIRelationship; - - expect(account.id).toBe(user2.id); - expect(account.muting).toBe(true); - expect(account.muting_notifications).toBe(true); - }); - - test("should mute the specified user and return an APIRelationship object with notifications set to true", async () => { - const response = await sendTestRequest( - new Request( - wrapRelativeUrl( - `/api/v1/accounts/${user2.id}/mute`, - base_url, - ), - { - method: "POST", - headers: { - Authorization: `Bearer ${token.accessToken}`, - "Content-Type": "application/json", - }, - body: JSON.stringify({ notifications: false }), - }, - ), - ); - - expect(response.status).toBe(200); - expect(response.headers.get("content-type")).toBe( - "application/json", - ); - - const account = (await response.json()) as APIRelationship; - - expect(account.id).toBe(user2.id); - expect(account.muting).toBe(true); - expect(account.muting_notifications).toBe(true); - }); - }); - - describe("GET /api/v1/mutes", () => { - test("should return an array of APIAccount objects for the user's muted accounts", async () => { - const response = await sendTestRequest( - new Request(wrapRelativeUrl("/api/v1/mutes", base_url), { - method: "GET", - headers: { - Authorization: `Bearer ${token.accessToken}`, - }, - }), - ); - - expect(response.status).toBe(200); - expect(response.headers.get("content-type")).toBe( - "application/json", - ); - - const body = (await response.json()) as APIAccount[]; - - expect(Array.isArray(body)).toBe(true); - expect(body.length).toBe(1); - expect(body[0].id).toBe(user2.id); - }); - }); - - describe("POST /api/v1/accounts/:id/unmute", () => { - test("should unmute the specified user and return an APIRelationship object", async () => { - const response = await sendTestRequest( - new Request( - wrapRelativeUrl( - `/api/v1/accounts/${user2.id}/unmute`, - base_url, - ), - { - method: "POST", - headers: { - Authorization: `Bearer ${token.accessToken}`, - "Content-Type": "application/json", - }, - body: JSON.stringify({}), - }, - ), - ); - - expect(response.status).toBe(200); - expect(response.headers.get("content-type")).toBe( - "application/json", - ); - - const account = (await response.json()) as APIRelationship; - - expect(account.id).toBe(user2.id); - expect(account.muting).toBe(false); - }); - }); - describe("POST /api/v1/accounts/:id/pin", () => { test("should pin the specified user and return an APIRelationship object", async () => { const response = await sendTestRequest( diff --git a/tests/api/statuses.test.ts b/tests/api/statuses.test.ts index 8c7f1ca4..cbc14914 100644 --- a/tests/api/statuses.test.ts +++ b/tests/api/statuses.test.ts @@ -344,35 +344,6 @@ describe("API Tests", () => { }); }); - describe("GET /api/v1/statuses/:id/favourited_by", () => { - test("should return an array of User objects who favourited the specified status", async () => { - const response = await sendTestRequest( - new Request( - wrapRelativeUrl( - `${base_url}/api/v1/statuses/${status?.id}/favourited_by`, - base_url, - ), - { - method: "GET", - headers: { - Authorization: `Bearer ${token.accessToken}`, - }, - }, - ), - ); - - expect(response.status).toBe(200); - expect(response.headers.get("content-type")).toBe( - "application/json", - ); - - const users = (await response.json()) as APIAccount[]; - - expect(users.length).toBe(1); - expect(users[0].id).toBe(user.id); - }); - }); - describe("POST /api/v1/statuses/:id/unfavourite", () => { test("should unfavourite the specified status object", async () => { // Unfavourite the status diff --git a/tests/utils.ts b/tests/utils.ts index 824c3a56..d46f6daf 100644 --- a/tests/utils.ts +++ b/tests/utils.ts @@ -7,7 +7,7 @@ import { createNewLocalUser, } from "~database/entities/User"; import { db } from "~drizzle/db"; -import { status, token, user } from "~drizzle/schema"; +import { Notes, Tokens, Users } from "~drizzle/schema"; import { server } from "~index"; import { Note } from "~packages/database-interface/note"; /** @@ -26,7 +26,7 @@ export function wrapRelativeUrl(url: string, base_url: string) { export const deleteOldTestUsers = async () => { // Deletes all users that match the test username (test-<32 random characters>) - await db.delete(user).where(like(user.username, "test-%")); + await db.delete(Users).where(like(Users.username, "test-%")); }; export const getTestUsers = async (count: number) => { @@ -51,7 +51,7 @@ export const getTestUsers = async (count: number) => { } const tokens = await db - .insert(token) + .insert(Tokens) .values( users.map((u) => ({ accessToken: randomBytes(32).toString("hex"), @@ -69,9 +69,9 @@ export const getTestUsers = async (count: number) => { tokens, passwords, deleteUsers: async () => { - await db.delete(user).where( + await db.delete(Users).where( inArray( - user.id, + Users.id, users.map((u) => u.id), ), ); @@ -107,10 +107,10 @@ export const getTestStatuses = async ( return ( await Note.manyFromSql( inArray( - status.id, + Notes.id, statuses.map((s) => s.id), ), - asc(status.id), + asc(Notes.id), ) ).map((n) => n.getStatus()); }; diff --git a/utils/meilisearch.ts b/utils/meilisearch.ts index 8cea1abb..2234385b 100644 --- a/utils/meilisearch.ts +++ b/utils/meilisearch.ts @@ -6,7 +6,7 @@ import { Meilisearch } from "meilisearch"; import type { Status } from "~database/entities/Status"; import type { User } from "~database/entities/User"; import { db } from "~drizzle/db"; -import { status, user } from "~drizzle/schema"; +import { Notes, Users } from "~drizzle/schema"; export const meilisearch = new Meilisearch({ host: `${config.meilisearch.host}:${config.meilisearch.port}`, @@ -83,7 +83,7 @@ export const getNthDatabaseAccountBatch = ( n: number, batchSize = 1000, ): Promise[]> => { - return db.query.user.findMany({ + return db.query.Users.findMany({ offset: n * batchSize, limit: batchSize, columns: { @@ -101,7 +101,7 @@ export const getNthDatabaseStatusBatch = ( n: number, batchSize = 1000, ): Promise[]> => { - return db.query.status.findMany({ + return db.query.Notes.findMany({ offset: n * batchSize, limit: batchSize, columns: { @@ -123,7 +123,7 @@ export const rebuildSearchIndexes = async ( .select({ count: count(), }) - .from(user) + .from(Users) )[0].count; for (let i = 0; i < accountCount / batchSize; i++) { @@ -156,7 +156,7 @@ export const rebuildSearchIndexes = async ( .select({ count: count(), }) - .from(status) + .from(Notes) )[0].count; for (let i = 0; i < statusCount / batchSize; i++) { diff --git a/utils/timelines.ts b/utils/timelines.ts index c291ee0c..69e879ed 100644 --- a/utils/timelines.ts +++ b/utils/timelines.ts @@ -15,7 +15,7 @@ export async function fetchTimeline( args: | Parameters[0] | Parameters[0] - | Parameters[0], + | Parameters[0], req: Request, ) { // BEFORE: Before in a top-to-bottom order, so the most recent posts