From ddb3cfc978fa58af352565fc7153912a97e2a662 Mon Sep 17 00:00:00 2001 From: Jesse Wierzbinski Date: Sun, 4 May 2025 16:38:37 +0200 Subject: [PATCH] perf(api): :zap: Store user and post metrics directly in database instead of recalculating them on-the-fly --- api/api/v1/accounts/[id]/follow.test.ts | 13 +- api/api/v1/accounts/[id]/unfollow.test.ts | 60 + api/api/v1/statuses/[id]/reblog.test.ts | 25 + api/api/v1/statuses/[id]/unreblog.test.ts | 24 + classes/database/like.ts | 14 +- classes/database/note.ts | 43 +- classes/database/notification.ts | 3 - classes/database/relationship.ts | 37 +- classes/database/user.ts | 60 + classes/functions/status.ts | 41 +- classes/functions/user.ts | 42 +- classes/inbox/processor.ts | 17 +- drizzle/migrations/0050_thick_lester.sql | 6 + drizzle/migrations/meta/0050_snapshot.json | 2384 ++++++++++++++++++++ drizzle/migrations/meta/_journal.json | 7 + drizzle/schema.ts | 6 + 16 files changed, 2676 insertions(+), 106 deletions(-) create mode 100644 api/api/v1/accounts/[id]/unfollow.test.ts create mode 100644 drizzle/migrations/0050_thick_lester.sql create mode 100644 drizzle/migrations/meta/0050_snapshot.json diff --git a/api/api/v1/accounts/[id]/follow.test.ts b/api/api/v1/accounts/[id]/follow.test.ts index e87a64a9..6331816e 100644 --- a/api/api/v1/accounts/[id]/follow.test.ts +++ b/api/api/v1/accounts/[id]/follow.test.ts @@ -1,13 +1,12 @@ import { afterAll, describe, expect, test } from "bun:test"; import { generateClient, getTestUsers } from "~/tests/utils"; -const { users, deleteUsers } = await getTestUsers(2); +const { users, deleteUsers } = await getTestUsers(3); afterAll(async () => { await deleteUsers(); }); -// /api/v1/accounts/:id/follow describe("/api/v1/accounts/:id/follow", () => { test("should return 401 if not authenticated", async () => { await using client = await generateClient(); @@ -35,6 +34,16 @@ describe("/api/v1/accounts/:id/follow", () => { const { ok } = await client.followAccount(users[1].id); expect(ok).toBe(true); + + const { ok: ok2, data: data2 } = await client.getAccount(users[1].id); + + expect(ok2).toBe(true); + expect(data2.followers_count).toBe(1); + + const { ok: ok3, data: data3 } = await client.getAccount(users[0].id); + + expect(ok3).toBe(true); + expect(data3.following_count).toBe(1); }); test("should return 200 if user already followed", async () => { diff --git a/api/api/v1/accounts/[id]/unfollow.test.ts b/api/api/v1/accounts/[id]/unfollow.test.ts new file mode 100644 index 00000000..754c3fa1 --- /dev/null +++ b/api/api/v1/accounts/[id]/unfollow.test.ts @@ -0,0 +1,60 @@ +import { afterAll, describe, expect, test } from "bun:test"; +import { generateClient, getTestUsers } from "~/tests/utils"; + +const { users, deleteUsers } = await getTestUsers(3); + +afterAll(async () => { + await deleteUsers(); +}); + +describe("/api/v1/accounts/:id/unfollow", () => { + test("should return 401 if not authenticated", async () => { + await using client = await generateClient(); + + const { ok, raw } = await client.unfollowAccount(users[1].id); + + expect(ok).toBe(false); + expect(raw.status).toBe(401); + }); + + test("should return 404 if user not found", async () => { + await using client = await generateClient(users[0]); + + const { ok, raw } = await client.unfollowAccount( + "00000000-0000-0000-0000-000000000000", + ); + + expect(ok).toBe(false); + expect(raw.status).toBe(404); + }); + + test("should unfollow user", async () => { + await using client = await generateClient(users[0]); + + const { ok } = await client.followAccount(users[1].id); + + expect(ok).toBe(true); + + const { ok: ok2 } = await client.unfollowAccount(users[1].id); + + expect(ok2).toBe(true); + + const { ok: ok3, data } = await client.getAccount(users[1].id); + + expect(ok3).toBe(true); + expect(data.followers_count).toBe(0); + + const { ok: ok4, data: data4 } = await client.getAccount(users[0].id); + + expect(ok4).toBe(true); + expect(data4.following_count).toBe(0); + }); + + test("should return 200 if user already followed", async () => { + await using client = await generateClient(users[0]); + + const { ok } = await client.followAccount(users[1].id); + + expect(ok).toBe(true); + }); +}); diff --git a/api/api/v1/statuses/[id]/reblog.test.ts b/api/api/v1/statuses/[id]/reblog.test.ts index 12f997f2..26c447a7 100644 --- a/api/api/v1/statuses/[id]/reblog.test.ts +++ b/api/api/v1/statuses/[id]/reblog.test.ts @@ -46,4 +46,29 @@ describe("POST /api/v1/statuses/:id/reblog", () => { expect(ok).toBe(true); expect(data.reblog?.id).toBe(statuses[0].id); }); + + test("should update reblog count on original status", async () => { + await using client = await generateClient(users[0]); + + // Unreblog the status first + await client.unreblogStatus(statuses[0].id); + + // Check that the reblog count is 0 + const { ok: ok1, data: data1 } = await client.getStatus(statuses[0].id); + + expect(ok1).toBe(true); + expect(data1.reblogs_count).toBe(0); + + const { ok: ok2, data: data2 } = await client.reblogStatus( + statuses[0].id, + ); + + expect(ok2).toBe(true); + expect(data2.reblog?.reblogs_count).toBe(1); + + const { ok: ok3, data: data3 } = await client.getStatus(statuses[0].id); + + expect(ok3).toBe(true); + expect(data3.reblogs_count).toBe(1); + }); }); diff --git a/api/api/v1/statuses/[id]/unreblog.test.ts b/api/api/v1/statuses/[id]/unreblog.test.ts index 2a98e3ae..3d89db0a 100644 --- a/api/api/v1/statuses/[id]/unreblog.test.ts +++ b/api/api/v1/statuses/[id]/unreblog.test.ts @@ -48,4 +48,28 @@ describe("POST /api/v1/statuses/:id/unreblog", () => { expect(ok).toBe(true); expect(data.reblog).toBeNull(); }); + + test("should update reblog count on original status", async () => { + await using client = await generateClient(users[0]); + + // Reblog the status first + await client.reblogStatus(statuses[0].id); + + // Check that the reblog count is 1 + const { ok: ok1, data: data1 } = await client.getStatus(statuses[0].id); + + expect(ok1).toBe(true); + expect(data1.reblogs_count).toBe(1); + + const { ok: ok2, data: data2 } = await client.unreblogStatus( + statuses[0].id, + ); + + expect(ok2).toBe(true); + expect(data2.reblogs_count).toBe(0); + + const { ok: ok3, data: data3 } = await client.getStatus(statuses[0].id); + expect(ok3).toBe(true); + expect(data3.reblogs_count).toBe(0); + }); }); diff --git a/classes/database/like.ts b/classes/database/like.ts index f7dbbf1d..9dc42277 100644 --- a/classes/database/like.ts +++ b/classes/database/like.ts @@ -106,12 +106,8 @@ export class Like extends BaseInterface { return this.update(this.data); } - public async delete(ids?: string[]): Promise { - if (Array.isArray(ids)) { - await db.delete(Likes).where(inArray(Likes.id, ids)); - } else { - await db.delete(Likes).where(eq(Likes.id, this.id)); - } + public async delete(): Promise { + await db.delete(Likes).where(eq(Likes.id, this.id)); } public static async insert( @@ -119,13 +115,13 @@ export class Like extends BaseInterface { ): Promise { const inserted = (await db.insert(Likes).values(data).returning())[0]; - const role = await Like.fromId(inserted.id); + const like = await Like.fromId(inserted.id); - if (!role) { + if (!like) { throw new Error("Failed to insert like"); } - return role; + return like; } public get id(): string { diff --git a/classes/database/note.ts b/classes/database/note.ts index b3ced792..08985323 100644 --- a/classes/database/note.ts +++ b/classes/database/note.ts @@ -2,6 +2,7 @@ import type { Status } from "@versia/client/schemas"; import { db, Instance } from "@versia/kit/db"; import { EmojiToNote, + Likes, MediasToNotes, Notes, NoteToMentions, @@ -49,9 +50,6 @@ type NoteTypeWithRelations = NoteType & { reply: NoteType | null; quote: NoteType | null; application: typeof Application.$type | null; - reblogCount: number; - likeCount: number; - replyCount: number; pinned: boolean; reblogged: boolean; muted: boolean; @@ -104,6 +102,16 @@ export class Note extends BaseInterface { throw new Error("Failed to insert status"); } + // Update author's status count + await note.author.recalculateStatusCount(); + + if (note.data.replyId) { + // Update the reply's reply count + await new Note( + note.data.reply as typeof Note.$type, + ).recalculateReplyCount(); + } + return note; } @@ -308,6 +316,24 @@ export class Note extends BaseInterface { return this.author.local; } + public async recalculateReblogCount(): Promise { + const reblogCount = await db.$count(Notes, eq(Notes.reblogId, this.id)); + + await this.update({ reblogCount }); + } + + public async recalculateLikeCount(): Promise { + const likeCount = await db.$count(Likes, eq(Likes.likedId, this.id)); + + await this.update({ likeCount }); + } + + public async recalculateReplyCount(): Promise { + const replyCount = await db.$count(Notes, eq(Notes.replyId, this.id)); + + await this.update({ replyCount }); + } + /** * Updates the emojis associated with this note in the database * @@ -531,12 +557,11 @@ export class Note extends BaseInterface { return note; } - public async delete(ids?: string[]): Promise { - if (Array.isArray(ids)) { - await db.delete(Notes).where(inArray(Notes.id, ids)); - } else { - await db.delete(Notes).where(eq(Notes.id, this.id)); - } + public async delete(): Promise { + await db.delete(Notes).where(eq(Notes.id, this.id)); + + // Update author's status count + await this.author.recalculateStatusCount(); } public async update( diff --git a/classes/database/notification.ts b/classes/database/notification.ts index 403164f0..bfaecf5f 100644 --- a/classes/database/notification.ts +++ b/classes/database/notification.ts @@ -12,7 +12,6 @@ import { import type { z } from "zod"; import { transformOutputToUserWithRelations, - userExtrasTemplate, userRelations, } from "../functions/user.ts"; import { BaseInterface } from "./base.ts"; @@ -78,7 +77,6 @@ export class Notification extends BaseInterface< with: { ...userRelations, }, - extras: userExtrasTemplate("Notifications_account"), }, }, }); @@ -112,7 +110,6 @@ export class Notification extends BaseInterface< with: { ...userRelations, }, - extras: userExtrasTemplate("Notifications_account"), }, }, extras: extra?.extras, diff --git a/classes/database/relationship.ts b/classes/database/relationship.ts index 6715bc08..82d288cf 100644 --- a/classes/database/relationship.ts +++ b/classes/database/relationship.ts @@ -1,6 +1,6 @@ import type { Relationship as RelationshipSchema } from "@versia/client/schemas"; import { db } from "@versia/kit/db"; -import { Relationships } from "@versia/kit/tables"; +import { Relationships, Users } from "@versia/kit/tables"; import { randomUUIDv7 } from "bun"; import { and, @@ -10,6 +10,7 @@ import { type InferSelectModel, inArray, type SQL, + sql, } from "drizzle-orm"; import { z } from "zod"; import { BaseInterface } from "./base.ts"; @@ -246,6 +247,40 @@ export class Relationship extends BaseInterface< .set(newRelationship) .where(eq(Relationships.id, this.id)); + // If a user follows another user, update followerCount and followingCount + if (newRelationship.following && !this.data.following) { + await db + .update(Users) + .set({ + followingCount: sql`${Users.followingCount} + 1`, + }) + .where(eq(Users.id, this.data.ownerId)); + + await db + .update(Users) + .set({ + followerCount: sql`${Users.followerCount} + 1`, + }) + .where(eq(Users.id, this.data.subjectId)); + } + + // If a user unfollows another user, update followerCount and followingCount + if (!newRelationship.following && this.data.following) { + await db + .update(Users) + .set({ + followingCount: sql`${Users.followingCount} - 1`, + }) + .where(eq(Users.id, this.data.ownerId)); + + await db + .update(Users) + .set({ + followerCount: sql`${Users.followerCount} - 1`, + }) + .where(eq(Users.id, this.data.subjectId)); + } + const updated = await Relationship.fromId(this.data.id); if (!updated) { diff --git a/classes/database/user.ts b/classes/database/user.ts index a93b53b1..ad1524ed 100644 --- a/classes/database/user.ts +++ b/classes/database/user.ts @@ -13,6 +13,7 @@ import { Notes, NoteToMentions, Notifications, + Relationships, Users, UserToPinnedNotes, } from "@versia/kit/tables"; @@ -203,6 +204,12 @@ export class User extends BaseInterface { languages: options?.languages, }); + if (!otherUser.data.isLocked) { + // Update the follower count + await otherUser.recalculateFollowerCount(); + await this.recalculateFollowingCount(); + } + if (otherUser.remote) { await deliveryQueue.add(DeliveryJobType.FederateEntity, { entity: { @@ -237,6 +244,9 @@ export class User extends BaseInterface { }); } + await this.recalculateFollowingCount(); + await followee.recalculateFollowerCount(); + await relationship.update({ following: false, }); @@ -262,6 +272,9 @@ export class User extends BaseInterface { throw new Error("Followee must be a local user"); } + await follower.recalculateFollowerCount(); + await this.recalculateFollowingCount(); + const entity = new VersiaEntities.FollowAccept({ type: "FollowAccept", id: crypto.randomUUID(), @@ -504,6 +517,8 @@ export class User extends BaseInterface { uri: uri?.href, }); + await note.recalculateReblogCount(); + // Refetch the note *again* to get the proper value of .reblogged const finalNewReblog = await Note.fromId(newReblog.id, this?.id); @@ -555,6 +570,8 @@ export class User extends BaseInterface { await reblogToDelete.delete(); + await note.recalculateReblogCount(); + if (note.author.local) { // Remove any eventual notifications for this reblog await db @@ -586,6 +603,45 @@ export class User extends BaseInterface { } } + public async recalculateFollowerCount(): Promise { + const followerCount = await db.$count( + Relationships, + and( + eq(Relationships.subjectId, this.id), + eq(Relationships.following, true), + ), + ); + + await this.update({ + followerCount, + }); + } + + public async recalculateFollowingCount(): Promise { + const followingCount = await db.$count( + Relationships, + and( + eq(Relationships.ownerId, this.id), + eq(Relationships.following, true), + ), + ); + + await this.update({ + followingCount, + }); + } + + public async recalculateStatusCount(): Promise { + const statusCount = await db.$count( + Notes, + and(eq(Notes.authorId, this.id)), + ); + + await this.update({ + statusCount, + }); + } + /** * Like a note. * @@ -611,6 +667,8 @@ export class User extends BaseInterface { uri: uri?.href, }); + await note.recalculateLikeCount(); + if (note.author.local) { // Notify the user that their post has been favourited await note.author.notify("favourite", this, note); @@ -650,6 +708,8 @@ export class User extends BaseInterface { await likeToDelete.delete(); + await note.recalculateLikeCount(); + if (note.author.local) { // Remove any eventual notifications for this like await likeToDelete.clearRelatedNotifications(); diff --git a/classes/functions/status.ts b/classes/functions/status.ts index bc2f289d..3f604fb7 100644 --- a/classes/functions/status.ts +++ b/classes/functions/status.ts @@ -20,11 +20,7 @@ import { mentionValidator } from "@/api"; import { sanitizeHtml, sanitizeHtmlInline } from "@/sanitization"; import { config } from "~/config.ts"; import type * as VersiaEntities from "~/packages/sdk/entities/index.ts"; -import { - transformOutputToUserWithRelations, - userExtrasTemplate, - userRelations, -} from "./user.ts"; +import { transformOutputToUserWithRelations, userRelations } from "./user.ts"; /** * Wrapper against the Status object to make it easier to work with @@ -58,7 +54,6 @@ export const findManyNotes = async ( with: { ...userRelations, }, - extras: userExtrasTemplate("Notes_author"), }, mentions: { with: { @@ -92,9 +87,6 @@ export const findManyNotes = async ( with: { user: { with: userRelations, - extras: userExtrasTemplate( - "Notes_reblog_mentions_user", - ), }, }, }, @@ -102,22 +94,9 @@ export const findManyNotes = async ( with: { ...userRelations, }, - extras: userExtrasTemplate("Notes_reblog_author"), }, }, extras: { - reblogCount: - sql`(SELECT COUNT(*) FROM "Notes" WHERE "Notes"."reblogId" = "Notes_reblog".id)`.as( - "reblog_count", - ), - likeCount: - sql`(SELECT COUNT(*) FROM "Likes" WHERE "Likes"."likedId" = "Notes_reblog".id)`.as( - "like_count", - ), - replyCount: - sql`(SELECT COUNT(*) FROM "Notes" WHERE "Notes"."replyId" = "Notes_reblog".id)`.as( - "reply_count", - ), pinned: userId ? sql`EXISTS (SELECT 1 FROM "UserToPinnedNotes" WHERE "UserToPinnedNotes"."noteId" = "Notes_reblog".id AND "UserToPinnedNotes"."userId" = ${userId})`.as( "pinned", @@ -144,18 +123,6 @@ export const findManyNotes = async ( quote: true, }, extras: { - reblogCount: - sql`(SELECT COUNT(*) FROM "Notes" WHERE "Notes"."reblogId" = "Notes".id)`.as( - "reblog_count", - ), - likeCount: - sql`(SELECT COUNT(*) FROM "Likes" WHERE "Likes"."likedId" = "Notes".id)`.as( - "like_count", - ), - replyCount: - sql`(SELECT COUNT(*) FROM "Notes" WHERE "Notes"."replyId" = "Notes".id)`.as( - "reply_count", - ), pinned: userId ? sql`EXISTS (SELECT 1 FROM "UserToPinnedNotes" WHERE "UserToPinnedNotes"."noteId" = "Notes".id AND "UserToPinnedNotes"."userId" = ${userId})`.as( "pinned", @@ -200,17 +167,11 @@ export const findManyNotes = async ( (attachment) => attachment.media, ), emojis: (post.reblog.emojis ?? []).map((emoji) => emoji.emoji), - reblogCount: Number(post.reblog.reblogCount), - likeCount: Number(post.reblog.likeCount), - replyCount: Number(post.reblog.replyCount), pinned: Boolean(post.reblog.pinned), reblogged: Boolean(post.reblog.reblogged), muted: Boolean(post.reblog.muted), liked: Boolean(post.reblog.liked), }, - reblogCount: Number(post.reblogCount), - likeCount: Number(post.likeCount), - replyCount: Number(post.replyCount), pinned: Boolean(post.pinned), reblogged: Boolean(post.reblogged), muted: Boolean(post.muted), diff --git a/classes/functions/user.ts b/classes/functions/user.ts index 48f43fe0..a9cb5726 100644 --- a/classes/functions/user.ts +++ b/classes/functions/user.ts @@ -9,7 +9,7 @@ import { type User, } from "@versia/kit/db"; import type { Users } from "@versia/kit/tables"; -import { type InferSelectModel, type SQL, sql } from "drizzle-orm"; +import type { InferSelectModel } from "drizzle-orm"; export const userRelations = { instance: true, @@ -32,42 +32,6 @@ export const userRelations = { }, } as const; -export const userExtras = { - followerCount: - sql`(SELECT COUNT(*) FROM "Relationships" "relationships" WHERE ("relationships"."ownerId" = "Users".id AND "relationships"."following" = true))`.as( - "follower_count", - ), - followingCount: - sql`(SELECT COUNT(*) FROM "Relationships" "relationshipSubjects" WHERE ("relationshipSubjects"."subjectId" = "Users".id AND "relationshipSubjects"."following" = true))`.as( - "following_count", - ), - statusCount: - sql`(SELECT COUNT(*) FROM "Notes" WHERE "Notes"."authorId" = "Users".id)`.as( - "status_count", - ), -}; - -export const userExtrasTemplate = ( - name: string, -): { - followerCount: SQL.Aliased; - followingCount: SQL.Aliased; - statusCount: SQL.Aliased; -} => ({ - // @ts-expect-error sql is a template tag, so it gets confused when we use it as a function - followerCount: sql([ - `(SELECT COUNT(*) FROM "Relationships" "relationships" WHERE ("relationships"."ownerId" = "${name}".id AND "relationships"."following" = true))`, - ]).as("follower_count"), - // @ts-expect-error sql is a template tag, so it gets confused when we use it as a function - followingCount: sql([ - `(SELECT COUNT(*) FROM "Relationships" "relationshipSubjects" WHERE ("relationshipSubjects"."subjectId" = "${name}".id AND "relationshipSubjects"."following" = true))`, - ]).as("following_count"), - // @ts-expect-error sql is a template tag, so it gets confused when we use it as a function - statusCount: sql([ - `(SELECT COUNT(*) FROM "Notes" WHERE "Notes"."authorId" = "${name}".id)`, - ]).as("status_count"), -}); - export interface AuthData { user: User | null; token: Token | null; @@ -131,10 +95,6 @@ export const findManyUsers = async ( ...userRelations, ...query?.with, }, - extras: { - ...userExtras, - ...query?.extras, - }, }); return output.map((user) => transformOutputToUserWithRelations(user)); diff --git a/classes/inbox/processor.ts b/classes/inbox/processor.ts index a0b8d6b3..e435bb09 100644 --- a/classes/inbox/processor.ts +++ b/classes/inbox/processor.ts @@ -417,7 +417,22 @@ export class InboxProcessor { ); } - await like.delete(); + const likeAuthor = await User.fromId(like.data.likerId); + const liked = await Note.fromId(like.data.likedId); + + if (!liked) { + throw new ApiError( + 404, + "Liked Note not found or not owned by sender", + ); + } + + if (!likeAuthor) { + throw new ApiError(404, "Like author not found"); + } + + await likeAuthor.unlike(liked); + return; } case "pub.versia:shares/Share": { diff --git a/drizzle/migrations/0050_thick_lester.sql b/drizzle/migrations/0050_thick_lester.sql new file mode 100644 index 00000000..275b2fc5 --- /dev/null +++ b/drizzle/migrations/0050_thick_lester.sql @@ -0,0 +1,6 @@ +ALTER TABLE "Notes" ADD COLUMN "reblog_count" integer DEFAULT 0 NOT NULL;--> statement-breakpoint +ALTER TABLE "Notes" ADD COLUMN "like_count" integer DEFAULT 0 NOT NULL;--> statement-breakpoint +ALTER TABLE "Notes" ADD COLUMN "reply_count" integer DEFAULT 0 NOT NULL;--> statement-breakpoint +ALTER TABLE "Users" ADD COLUMN "follower_count" integer DEFAULT 0 NOT NULL;--> statement-breakpoint +ALTER TABLE "Users" ADD COLUMN "following_count" integer DEFAULT 0 NOT NULL;--> statement-breakpoint +ALTER TABLE "Users" ADD COLUMN "status_count" integer DEFAULT 0 NOT NULL; \ No newline at end of file diff --git a/drizzle/migrations/meta/0050_snapshot.json b/drizzle/migrations/meta/0050_snapshot.json new file mode 100644 index 00000000..d14fde1a --- /dev/null +++ b/drizzle/migrations/meta/0050_snapshot.json @@ -0,0 +1,2384 @@ +{ + "id": "13fdf09c-59ef-4e1e-9e59-f87676154810", + "prevId": "5c885ccf-fbaa-4ba4-9d7d-03d3c175d56e", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.Applications": { + "name": "Applications", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "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_uri": { + "name": "redirect_uri", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "Applications_client_id_index": { + "name": "Applications_client_id_index", + "columns": [ + { + "expression": "client_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.Challenges": { + "name": "Challenges", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "challenge": { + "name": "challenge", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": true, + "default": "NOW() + INTERVAL '5 minutes'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.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": [ + { + "expression": "emojiId", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "noteId", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "EmojiToNote_noteId_index": { + "name": "EmojiToNote_noteId_index", + "columns": [ + { + "expression": "noteId", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "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": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.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": [ + { + "expression": "emojiId", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "userId", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "EmojiToUser_userId_index": { + "name": "EmojiToUser_userId_index", + "columns": [ + { + "expression": "userId", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "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": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.Emojis": { + "name": "Emojis", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "shortcode": { + "name": "shortcode", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "mediaId": { + "name": "mediaId", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "visible_in_picker": { + "name": "visible_in_picker", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "instanceId": { + "name": "instanceId", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "ownerId": { + "name": "ownerId", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "category": { + "name": "category", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "Emojis_mediaId_Medias_id_fk": { + "name": "Emojis_mediaId_Medias_id_fk", + "tableFrom": "Emojis", + "tableTo": "Medias", + "columnsFrom": ["mediaId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "Emojis_instanceId_Instances_id_fk": { + "name": "Emojis_instanceId_Instances_id_fk", + "tableFrom": "Emojis", + "tableTo": "Instances", + "columnsFrom": ["instanceId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "Emojis_ownerId_Users_id_fk": { + "name": "Emojis_ownerId_Users_id_fk", + "tableFrom": "Emojis", + "tableTo": "Users", + "columnsFrom": ["ownerId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.FilterKeywords": { + "name": "FilterKeywords", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "filterId": { + "name": "filterId", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "keyword": { + "name": "keyword", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "whole_word": { + "name": "whole_word", + "type": "boolean", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "FilterKeywords_filterId_Filters_id_fk": { + "name": "FilterKeywords_filterId_Filters_id_fk", + "tableFrom": "FilterKeywords", + "tableTo": "Filters", + "columnsFrom": ["filterId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.Filters": { + "name": "Filters", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "userId": { + "name": "userId", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "context": { + "name": "context", + "type": "text[]", + "primaryKey": false, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "filter_action": { + "name": "filter_action", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "Filters_userId_Users_id_fk": { + "name": "Filters_userId_Users_id_fk", + "tableFrom": "Filters", + "tableTo": "Users", + "columnsFrom": ["userId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.Flags": { + "name": "Flags", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "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": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.Instances": { + "name": "Instances", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "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": false + }, + "disable_automoderation": { + "name": "disable_automoderation", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "protocol": { + "name": "protocol", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'versia'" + }, + "inbox": { + "name": "inbox", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "public_key": { + "name": "public_key", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "extensions": { + "name": "extensions", + "type": "jsonb", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.Likes": { + "name": "Likes", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "uri": { + "name": "uri", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "likerId": { + "name": "likerId", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "likedId": { + "name": "likedId", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "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": { + "Likes_uri_unique": { + "name": "Likes_uri_unique", + "nullsNotDistinct": false, + "columns": ["uri"] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.Markers": { + "name": "Markers", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "noteId": { + "name": "noteId", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "notificationId": { + "name": "notificationId", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "userId": { + "name": "userId", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "timeline": { + "name": "timeline", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "Markers_noteId_Notes_id_fk": { + "name": "Markers_noteId_Notes_id_fk", + "tableFrom": "Markers", + "tableTo": "Notes", + "columnsFrom": ["noteId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "Markers_notificationId_Notifications_id_fk": { + "name": "Markers_notificationId_Notifications_id_fk", + "tableFrom": "Markers", + "tableTo": "Notifications", + "columnsFrom": ["notificationId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "Markers_userId_Users_id_fk": { + "name": "Markers_userId_Users_id_fk", + "tableFrom": "Markers", + "tableTo": "Users", + "columnsFrom": ["userId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.Medias": { + "name": "Medias", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "content": { + "name": "content", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "original_content": { + "name": "original_content", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "thumbnail": { + "name": "thumbnail", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "blurhash": { + "name": "blurhash", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.MediasToNote": { + "name": "MediasToNote", + "schema": "", + "columns": { + "mediaId": { + "name": "mediaId", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "noteId": { + "name": "noteId", + "type": "uuid", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "MediasToNote_mediaId_index": { + "name": "MediasToNote_mediaId_index", + "columns": [ + { + "expression": "mediaId", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "MediasToNote_noteId_index": { + "name": "MediasToNote_noteId_index", + "columns": [ + { + "expression": "noteId", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "MediasToNote_mediaId_Medias_id_fk": { + "name": "MediasToNote_mediaId_Medias_id_fk", + "tableFrom": "MediasToNote", + "tableTo": "Medias", + "columnsFrom": ["mediaId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "MediasToNote_noteId_Notes_id_fk": { + "name": "MediasToNote_noteId_Notes_id_fk", + "tableFrom": "MediasToNote", + "tableTo": "Notes", + "columnsFrom": ["noteId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.ModNotes": { + "name": "ModNotes", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "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": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.ModTags": { + "name": "ModTags", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "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": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.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": [ + { + "expression": "noteId", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "userId", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "NoteToMentions_userId_index": { + "name": "NoteToMentions_userId_index", + "columns": [ + { + "expression": "userId", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "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": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.Notes": { + "name": "Notes", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "uri": { + "name": "uri", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "authorId": { + "name": "authorId", + "type": "uuid", + "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()" + }, + "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 + }, + "reblog_count": { + "name": "reblog_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "like_count": { + "name": "like_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "reply_count": { + "name": "reply_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "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, + "default": false + }, + "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": {}, + "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_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": "cascade", + "onUpdate": "cascade" + }, + "Notes_quoteId_Notes_id_fk": { + "name": "Notes_quoteId_Notes_id_fk", + "tableFrom": "Notes", + "tableTo": "Notes", + "columnsFrom": ["quoteId"], + "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" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "Notes_uri_unique": { + "name": "Notes_uri_unique", + "nullsNotDistinct": false, + "columns": ["uri"] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.Notifications": { + "name": "Notifications", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "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": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.OpenIdAccounts": { + "name": "OpenIdAccounts", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "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": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.OpenIdLoginFlows": { + "name": "OpenIdLoginFlows", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "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": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.PushSubscriptions": { + "name": "PushSubscriptions", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "endpoint": { + "name": "endpoint", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "public_key": { + "name": "public_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "auth_secret": { + "name": "auth_secret", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "alerts": { + "name": "alerts", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "policy": { + "name": "policy", + "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()" + }, + "tokenId": { + "name": "tokenId", + "type": "uuid", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "PushSubscriptions_tokenId_Tokens_id_fk": { + "name": "PushSubscriptions_tokenId_Tokens_id_fk", + "tableFrom": "PushSubscriptions", + "tableTo": "Tokens", + "columnsFrom": ["tokenId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "PushSubscriptions_tokenId_unique": { + "name": "PushSubscriptions_tokenId_unique", + "nullsNotDistinct": false, + "columns": ["tokenId"] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.Reaction": { + "name": "Reaction", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "uri": { + "name": "uri", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "emojiId": { + "name": "emojiId", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "emoji_text": { + "name": "emoji_text", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "noteId": { + "name": "noteId", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "authorId": { + "name": "authorId", + "type": "uuid", + "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": { + "Reaction_emojiId_Emojis_id_fk": { + "name": "Reaction_emojiId_Emojis_id_fk", + "tableFrom": "Reaction", + "tableTo": "Emojis", + "columnsFrom": ["emojiId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "Reaction_noteId_Notes_id_fk": { + "name": "Reaction_noteId_Notes_id_fk", + "tableFrom": "Reaction", + "tableTo": "Notes", + "columnsFrom": ["noteId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "Reaction_authorId_Users_id_fk": { + "name": "Reaction_authorId_Users_id_fk", + "tableFrom": "Reaction", + "tableTo": "Users", + "columnsFrom": ["authorId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "Reaction_uri_unique": { + "name": "Reaction_uri_unique", + "nullsNotDistinct": false, + "columns": ["uri"] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.Relationships": { + "name": "Relationships", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "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 + }, + "blocking": { + "name": "blocking", + "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": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.RoleToUsers": { + "name": "RoleToUsers", + "schema": "", + "columns": { + "roleId": { + "name": "roleId", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "userId": { + "name": "userId", + "type": "uuid", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "RoleToUsers_roleId_Roles_id_fk": { + "name": "RoleToUsers_roleId_Roles_id_fk", + "tableFrom": "RoleToUsers", + "tableTo": "Roles", + "columnsFrom": ["roleId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "RoleToUsers_userId_Users_id_fk": { + "name": "RoleToUsers_userId_Users_id_fk", + "tableFrom": "RoleToUsers", + "tableTo": "Users", + "columnsFrom": ["userId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.Roles": { + "name": "Roles", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "permissions": { + "name": "permissions", + "type": "text[]", + "primaryKey": false, + "notNull": true + }, + "priority": { + "name": "priority", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "visible": { + "name": "visible", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "icon": { + "name": "icon", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.Tokens": { + "name": "Tokens", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "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": false + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "client_id": { + "name": "client_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "redirect_uri": { + "name": "redirect_uri", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "id_token": { + "name": "id_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "userId": { + "name": "userId", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "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": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.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": [ + { + "expression": "userId", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "noteId", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "UserToPinnedNotes_noteId_index": { + "name": "UserToPinnedNotes_noteId_index", + "columns": [ + { + "expression": "noteId", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "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": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.Users": { + "name": "Users", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true + }, + "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": false + }, + "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 + }, + "email_verification_token": { + "name": "email_verification_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "password_reset_token": { + "name": "password_reset_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "fields": { + "name": "fields", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'" + }, + "endpoints": { + "name": "endpoints", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "source": { + "name": "source", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "avatarId": { + "name": "avatarId", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "headerId": { + "name": "headerId", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "follower_count": { + "name": "follower_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "following_count": { + "name": "following_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "status_count": { + "name": "status_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "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 + }, + "is_hiding_collections": { + "name": "is_hiding_collections", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "is_indexable": { + "name": "is_indexable", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "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": [ + { + "expression": "uri", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "Users_username_index": { + "name": "Users_username_index", + "columns": [ + { + "expression": "username", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "Users_email_index": { + "name": "Users_email_index", + "columns": [ + { + "expression": "email", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "Users_avatarId_Medias_id_fk": { + "name": "Users_avatarId_Medias_id_fk", + "tableFrom": "Users", + "tableTo": "Medias", + "columnsFrom": ["avatarId"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "cascade" + }, + "Users_headerId_Medias_id_fk": { + "name": "Users_headerId_Medias_id_fk", + "tableFrom": "Users", + "tableTo": "Medias", + "columnsFrom": ["headerId"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "cascade" + }, + "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": { + "Users_uri_unique": { + "name": "Users_uri_unique", + "nullsNotDistinct": false, + "columns": ["uri"] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": {}, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} diff --git a/drizzle/migrations/meta/_journal.json b/drizzle/migrations/meta/_journal.json index de7c98cf..b4ba6c21 100644 --- a/drizzle/migrations/meta/_journal.json +++ b/drizzle/migrations/meta/_journal.json @@ -351,6 +351,13 @@ "when": 1744979203068, "tag": "0049_graceful_iron_man", "breakpoints": true + }, + { + "idx": 50, + "version": "7", + "when": 1746368175263, + "tag": "0050_thick_lester", + "breakpoints": true } ] } diff --git a/drizzle/schema.ts b/drizzle/schema.ts index f9695ada..fa6875ad 100644 --- a/drizzle/schema.ts +++ b/drizzle/schema.ts @@ -446,6 +446,9 @@ export const Notes = pgTable("Notes", { visibility: text("visibility") .$type>() .notNull(), + reblogCount: integer("reblog_count").default(0).notNull(), + likeCount: integer("like_count").default(0).notNull(), + replyCount: integer("reply_count").default(0).notNull(), replyId: uuid("replyId").references((): AnyPgColumn => Notes.id, { onDelete: "cascade", onUpdate: "cascade", @@ -583,6 +586,9 @@ export const Users = pgTable( onDelete: "set null", onUpdate: "cascade", }), + followerCount: integer("follower_count").default(0).notNull(), + followingCount: integer("following_count").default(0).notNull(), + statusCount: integer("status_count").default(0).notNull(), createdAt: createdAt(), updatedAt: updatedAt(), isBot: boolean("is_bot").default(false).notNull(),