refactor(database): ♻️ Clean up database schema

This commit is contained in:
Jesse Wierzbinski 2024-12-18 21:52:53 +01:00
parent c334cd9cc8
commit 6f97f9f8f1
No known key found for this signature in database
4 changed files with 2339 additions and 154 deletions

View file

@ -0,0 +1,11 @@
ALTER TABLE "VersiaObject" DISABLE ROW LEVEL SECURITY;--> statement-breakpoint
DROP TABLE "VersiaObject" CASCADE;--> statement-breakpoint
ALTER TABLE "Likes" RENAME COLUMN "createdAt" TO "created_at";--> statement-breakpoint
ALTER TABLE "Notes" RENAME COLUMN "createdAt" TO "created_at";--> statement-breakpoint
ALTER TABLE "Notes" RENAME COLUMN "updatedAt" TO "updated_at";--> statement-breakpoint
ALTER TABLE "Notifications" RENAME COLUMN "createdAt" TO "created_at";--> statement-breakpoint
ALTER TABLE "Reaction" RENAME COLUMN "update_at" TO "updated_at";--> statement-breakpoint
ALTER TABLE "OpenIdAccounts" DROP CONSTRAINT "OpenIdAccounts_userId_Users_id_fk";
--> statement-breakpoint
ALTER TABLE "OpenIdAccounts" ADD CONSTRAINT "OpenIdAccounts_userId_Users_id_fk" FOREIGN KEY ("userId") REFERENCES "public"."Users"("id") ON DELETE cascade ON UPDATE cascade;--> statement-breakpoint
ALTER TABLE "Users" ADD CONSTRAINT "Users_uri_unique" UNIQUE("uri");

File diff suppressed because it is too large Load diff

View file

@ -274,6 +274,13 @@
"when": 1734548407684, "when": 1734548407684,
"tag": "0038_friendly_supernaut", "tag": "0038_friendly_supernaut",
"breakpoints": true "breakpoints": true
},
{
"idx": 39,
"version": "7",
"when": 1734555117380,
"tag": "0039_special_serpent_society",
"breakpoints": true
} }
] ]
} }

View file

@ -5,7 +5,6 @@ import { relations, sql } from "drizzle-orm";
import { import {
type AnyPgColumn, type AnyPgColumn,
boolean, boolean,
foreignKey,
index, index,
integer, integer,
jsonb, jsonb,
@ -16,8 +15,27 @@ import {
uuid, uuid,
} from "drizzle-orm/pg-core"; } from "drizzle-orm/pg-core";
// biome-ignore lint/nursery/useExplicitType: Type is too complex
const createdAt = () =>
timestamp("created_at", { precision: 3, mode: "string" })
.defaultNow()
.notNull();
// biome-ignore lint/nursery/useExplicitType: Type is too complex
const updatedAt = () =>
timestamp("updated_at", { precision: 3, mode: "string" })
.defaultNow()
.notNull();
// biome-ignore lint/nursery/useExplicitType: Type is too complex
const uri = () => text("uri").unique();
// biome-ignore lint/nursery/useExplicitType: Type is too complex
const id = () =>
uuid("id").default(sql`uuid_generate_v7()`).primaryKey().notNull();
export const Challenges = pgTable("Challenges", { export const Challenges = pgTable("Challenges", {
id: uuid("id").default(sql`uuid_generate_v7()`).primaryKey().notNull(), id: id(),
challenge: jsonb("challenge").notNull().$type<Challenge>(), challenge: jsonb("challenge").notNull().$type<Challenge>(),
expiresAt: timestamp("expires_at", { expiresAt: timestamp("expires_at", {
precision: 3, precision: 3,
@ -28,13 +46,11 @@ export const Challenges = pgTable("Challenges", {
sql`NOW() + INTERVAL '5 minutes'`, sql`NOW() + INTERVAL '5 minutes'`,
) )
.notNull(), .notNull(),
createdAt: timestamp("created_at", { precision: 3, mode: "string" }) createdAt: createdAt(),
.defaultNow()
.notNull(),
}); });
export const Emojis = pgTable("Emojis", { export const Emojis = pgTable("Emojis", {
id: uuid("id").default(sql`uuid_generate_v7()`).primaryKey().notNull(), id: id(),
shortcode: text("shortcode").notNull(), shortcode: text("shortcode").notNull(),
url: text("url").notNull(), url: text("url").notNull(),
visibleInPicker: boolean("visible_in_picker").notNull(), visibleInPicker: boolean("visible_in_picker").notNull(),
@ -52,8 +68,8 @@ export const Emojis = pgTable("Emojis", {
}); });
export const Reactions = pgTable("Reaction", { export const Reactions = pgTable("Reaction", {
id: uuid("id").default(sql`uuid_generate_v7()`).primaryKey().notNull(), id: id(),
uri: text("uri").unique(), uri: uri(),
// Emoji ID is nullable, in which case it is a text emoji, and the emojiText field is used // Emoji ID is nullable, in which case it is a text emoji, and the emojiText field is used
emojiId: uuid("emojiId").references(() => Emojis.id, { emojiId: uuid("emojiId").references(() => Emojis.id, {
onDelete: "cascade", onDelete: "cascade",
@ -72,15 +88,8 @@ export const Reactions = pgTable("Reaction", {
onDelete: "cascade", onDelete: "cascade",
onUpdate: "cascade", onUpdate: "cascade",
}), }),
createdAt: timestamp("created_at", { precision: 3, mode: "string" }) createdAt: createdAt(),
.defaultNow() updatedAt: updatedAt(),
.notNull(),
updatedAt: timestamp("update_at", {
precision: 3,
mode: "string",
})
.defaultNow()
.notNull(),
}); });
export const ReactionRelations = relations(Reactions, ({ one }) => ({ export const ReactionRelations = relations(Reactions, ({ one }) => ({
@ -99,7 +108,7 @@ export const ReactionRelations = relations(Reactions, ({ one }) => ({
})); }));
export const Filters = pgTable("Filters", { export const Filters = pgTable("Filters", {
id: uuid("id").default(sql`uuid_generate_v7()`).primaryKey().notNull(), id: id(),
userId: uuid("userId") userId: uuid("userId")
.notNull() .notNull()
.references(() => Users.id, { .references(() => Users.id, {
@ -115,13 +124,11 @@ export const Filters = pgTable("Filters", {
title: text("title").notNull(), title: text("title").notNull(),
filterAction: text("filter_action").notNull().$type<"warn" | "hide">(), filterAction: text("filter_action").notNull().$type<"warn" | "hide">(),
expireAt: timestamp("expires_at", { precision: 3, mode: "string" }), expireAt: timestamp("expires_at", { precision: 3, mode: "string" }),
createdAt: timestamp("created_at", { precision: 3, mode: "string" }) createdAt: createdAt(),
.defaultNow()
.notNull(),
}); });
export const FilterKeywords = pgTable("FilterKeywords", { export const FilterKeywords = pgTable("FilterKeywords", {
id: uuid("id").default(sql`uuid_generate_v7()`).primaryKey().notNull(), id: id(),
filterId: uuid("filterId") filterId: uuid("filterId")
.notNull() .notNull()
.references(() => Filters.id, { .references(() => Filters.id, {
@ -144,7 +151,7 @@ export const FilterKeywordsRelations = relations(FilterKeywords, ({ one }) => ({
})); }));
export const Markers = pgTable("Markers", { export const Markers = pgTable("Markers", {
id: uuid("id").default(sql`uuid_generate_v7()`).primaryKey().notNull(), id: id(),
noteId: uuid("noteId").references(() => Notes.id, { noteId: uuid("noteId").references(() => Notes.id, {
onDelete: "cascade", onDelete: "cascade",
onUpdate: "cascade", onUpdate: "cascade",
@ -160,14 +167,12 @@ export const Markers = pgTable("Markers", {
}) })
.notNull(), .notNull(),
timeline: text("timeline").notNull().$type<"home" | "notifications">(), timeline: text("timeline").notNull().$type<"home" | "notifications">(),
createdAt: timestamp("created_at", { precision: 3, mode: "string" }) createdAt: createdAt(),
.defaultNow()
.notNull(),
}); });
export const Likes = pgTable("Likes", { export const Likes = pgTable("Likes", {
id: uuid("id").default(sql`uuid_generate_v7()`).primaryKey().notNull(), id: id(),
uri: text("uri").unique(), uri: uri(),
likerId: uuid("likerId") likerId: uuid("likerId")
.notNull() .notNull()
.references(() => Users.id, { .references(() => Users.id, {
@ -180,41 +185,11 @@ export const Likes = pgTable("Likes", {
onDelete: "cascade", onDelete: "cascade",
onUpdate: "cascade", onUpdate: "cascade",
}), }),
createdAt: timestamp("createdAt", { precision: 3, mode: "string" }) createdAt: createdAt(),
.defaultNow()
.notNull(),
}); });
export const VersiaObjects = pgTable(
"VersiaObject",
{
id: uuid("id").default(sql`uuid_generate_v7()`).primaryKey().notNull(),
remoteId: text("remote_id").notNull(),
type: text("type").notNull(),
uri: text("uri").notNull(),
createdAt: timestamp("created_at", { precision: 3, mode: "string" })
.defaultNow()
.notNull(),
authorId: uuid("authorId"),
extraData: jsonb("extra_data").notNull(),
extensions: jsonb("extensions").notNull(),
},
(table) => {
return {
remoteIdKey: uniqueIndex().on(table.remoteId),
uriKey: uniqueIndex().on(table.uri),
versiaObjectAuthorIdFkey: foreignKey({
columns: [table.authorId],
foreignColumns: [table.id],
})
.onUpdate("cascade")
.onDelete("cascade"),
};
},
);
export const Relationships = pgTable("Relationships", { export const Relationships = pgTable("Relationships", {
id: uuid("id").default(sql`uuid_generate_v7()`).primaryKey().notNull(), id: id(),
ownerId: uuid("ownerId") ownerId: uuid("ownerId")
.notNull() .notNull()
.references(() => Users.id, { .references(() => Users.id, {
@ -238,21 +213,14 @@ export const Relationships = pgTable("Relationships", {
endorsed: boolean("endorsed").notNull(), endorsed: boolean("endorsed").notNull(),
languages: text("languages").array(), languages: text("languages").array(),
note: text("note").notNull(), note: text("note").notNull(),
createdAt: timestamp("created_at", { precision: 3, mode: "string" }) createdAt: createdAt(),
.defaultNow() updatedAt: updatedAt(),
.notNull(),
updatedAt: timestamp("updated_at", {
precision: 3,
mode: "string",
})
.defaultNow()
.notNull(),
}); });
export const Applications = pgTable( export const Applications = pgTable(
"Applications", "Applications",
{ {
id: uuid("id").default(sql`uuid_generate_v7()`).primaryKey().notNull(), id: id(),
name: text("name").notNull(), name: text("name").notNull(),
website: text("website"), website: text("website"),
vapidKey: text("vapid_key"), vapidKey: text("vapid_key"),
@ -261,11 +229,7 @@ export const Applications = pgTable(
scopes: text("scopes").notNull(), scopes: text("scopes").notNull(),
redirectUri: text("redirect_uri").notNull(), redirectUri: text("redirect_uri").notNull(),
}, },
(table) => { (table) => [uniqueIndex().on(table.clientId)],
return {
clientIdKey: uniqueIndex().on(table.clientId),
};
},
); );
export const ApplicationsRelations = relations(Applications, ({ many }) => ({ export const ApplicationsRelations = relations(Applications, ({ many }) => ({
@ -274,15 +238,13 @@ export const ApplicationsRelations = relations(Applications, ({ many }) => ({
})); }));
export const Tokens = pgTable("Tokens", { export const Tokens = pgTable("Tokens", {
id: uuid("id").default(sql`uuid_generate_v7()`).primaryKey().notNull(), id: id(),
tokenType: text("token_type").notNull(), tokenType: text("token_type").notNull(),
scope: text("scope").notNull(), scope: text("scope").notNull(),
accessToken: text("access_token").notNull(), accessToken: text("access_token").notNull(),
code: text("code"), code: text("code"),
expiresAt: timestamp("expires_at", { precision: 3, mode: "string" }), expiresAt: timestamp("expires_at", { precision: 3, mode: "string" }),
createdAt: timestamp("created_at", { precision: 3, mode: "string" }) createdAt: createdAt(),
.defaultNow()
.notNull(),
clientId: text("client_id").notNull().default(""), clientId: text("client_id").notNull().default(""),
redirectUri: text("redirect_uri").notNull().default(""), redirectUri: text("redirect_uri").notNull().default(""),
idToken: text("id_token"), idToken: text("id_token"),
@ -299,7 +261,7 @@ export const Tokens = pgTable("Tokens", {
}); });
export const Attachments = pgTable("Attachments", { export const Attachments = pgTable("Attachments", {
id: uuid("id").default(sql`uuid_generate_v7()`).primaryKey().notNull(), id: id(),
url: text("url").notNull(), url: text("url").notNull(),
remoteUrl: text("remote_url"), remoteUrl: text("remote_url"),
thumbnailUrl: text("thumbnail_url"), thumbnailUrl: text("thumbnail_url"),
@ -319,11 +281,9 @@ export const Attachments = pgTable("Attachments", {
}); });
export const Notifications = pgTable("Notifications", { export const Notifications = pgTable("Notifications", {
id: uuid("id").default(sql`uuid_generate_v7()`).primaryKey().notNull(), id: id(),
type: text("type").notNull(), type: text("type").notNull(),
createdAt: timestamp("createdAt", { precision: 3, mode: "string" }) createdAt: createdAt(),
.defaultNow()
.notNull(),
notifiedId: uuid("notifiedId") notifiedId: uuid("notifiedId")
.notNull() .notNull()
.references(() => Users.id, { .references(() => Users.id, {
@ -344,23 +304,16 @@ export const Notifications = pgTable("Notifications", {
}); });
export const Notes = pgTable("Notes", { export const Notes = pgTable("Notes", {
id: uuid("id").default(sql`uuid_generate_v7()`).primaryKey().notNull(), id: id(),
uri: text("uri").unique(), uri: uri(),
authorId: uuid("authorId") authorId: uuid("authorId")
.notNull() .notNull()
.references(() => Users.id, { .references(() => Users.id, {
onDelete: "cascade", onDelete: "cascade",
onUpdate: "cascade", onUpdate: "cascade",
}), }),
createdAt: timestamp("createdAt", { precision: 3, mode: "string" }) createdAt: createdAt(),
.defaultNow() updatedAt: updatedAt(),
.notNull(),
updatedAt: timestamp("updatedAt", {
precision: 3,
mode: "string",
})
.defaultNow()
.notNull(),
reblogId: uuid("reblogId").references((): AnyPgColumn => Notes.id, { reblogId: uuid("reblogId").references((): AnyPgColumn => Notes.id, {
onDelete: "cascade", onDelete: "cascade",
onUpdate: "cascade", onUpdate: "cascade",
@ -386,7 +339,7 @@ export const Notes = pgTable("Notes", {
}); });
export const Instances = pgTable("Instances", { export const Instances = pgTable("Instances", {
id: uuid("id").default(sql`uuid_generate_v7()`).primaryKey().notNull(), id: id(),
baseUrl: text("base_url").notNull(), baseUrl: text("base_url").notNull(),
name: text("name").notNull(), name: text("name").notNull(),
version: text("version").notNull(), version: text("version").notNull(),
@ -404,9 +357,9 @@ export const Instances = pgTable("Instances", {
}); });
export const OpenIdAccounts = pgTable("OpenIdAccounts", { export const OpenIdAccounts = pgTable("OpenIdAccounts", {
id: uuid("id").default(sql`uuid_generate_v7()`).primaryKey().notNull(), id: id(),
userId: uuid("userId").references(() => Users.id, { userId: uuid("userId").references(() => Users.id, {
onDelete: "set null", onDelete: "cascade",
onUpdate: "cascade", onUpdate: "cascade",
}), }),
serverId: text("server_id").notNull(), serverId: text("server_id").notNull(),
@ -416,8 +369,8 @@ export const OpenIdAccounts = pgTable("OpenIdAccounts", {
export const Users = pgTable( export const Users = pgTable(
"Users", "Users",
{ {
id: uuid("id").default(sql`uuid_generate_v7()`).primaryKey().notNull(), id: id(),
uri: text("uri"), uri: uri(),
username: text("username").notNull(), username: text("username").notNull(),
displayName: text("display_name").notNull(), displayName: text("display_name").notNull(),
password: text("password"), password: text("password"),
@ -453,15 +406,8 @@ export const Users = pgTable(
>(), >(),
avatar: text("avatar").notNull(), avatar: text("avatar").notNull(),
header: text("header").notNull(), header: text("header").notNull(),
createdAt: timestamp("created_at", { precision: 3, mode: "string" }) createdAt: createdAt(),
.defaultNow() updatedAt: updatedAt(),
.notNull(),
updatedAt: timestamp("updated_at", {
precision: 3,
mode: "string",
})
.defaultNow()
.notNull(),
isBot: boolean("is_bot").default(false).notNull(), isBot: boolean("is_bot").default(false).notNull(),
isLocked: boolean("is_locked").default(false).notNull(), isLocked: boolean("is_locked").default(false).notNull(),
isDiscoverable: boolean("is_discoverable").default(false).notNull(), isDiscoverable: boolean("is_discoverable").default(false).notNull(),
@ -476,17 +422,15 @@ export const Users = pgTable(
.default(false) .default(false)
.notNull(), .notNull(),
}, },
(table) => { (table) => [
return { uniqueIndex().on(table.uri),
uriKey: uniqueIndex().on(table.uri), index().on(table.username),
usernameKey: index().on(table.username), uniqueIndex().on(table.email),
emailKey: uniqueIndex().on(table.email), ],
};
},
); );
export const OpenIdLoginFlows = pgTable("OpenIdLoginFlows", { export const OpenIdLoginFlows = pgTable("OpenIdLoginFlows", {
id: uuid("id").default(sql`uuid_generate_v7()`).primaryKey().notNull(), id: id(),
codeVerifier: text("code_verifier").notNull(), codeVerifier: text("code_verifier").notNull(),
applicationId: uuid("applicationId").references(() => Applications.id, { applicationId: uuid("applicationId").references(() => Applications.id, {
onDelete: "cascade", onDelete: "cascade",
@ -506,11 +450,9 @@ export const OpenIdLoginFlowsRelations = relations(
); );
export const Flags = pgTable("Flags", { export const Flags = pgTable("Flags", {
id: uuid("id").default(sql`uuid_generate_v7()`).primaryKey().notNull(), id: id(),
flagType: text("flag_type").default("other").notNull(), flagType: text("flag_type").default("other").notNull(),
createdAt: timestamp("created_at", { precision: 3, mode: "string" }) createdAt: createdAt(),
.defaultNow()
.notNull(),
noteId: uuid("noteId").references(() => Notes.id, { noteId: uuid("noteId").references(() => Notes.id, {
onDelete: "cascade", onDelete: "cascade",
onUpdate: "cascade", onUpdate: "cascade",
@ -522,7 +464,7 @@ export const Flags = pgTable("Flags", {
}); });
export const ModNotes = pgTable("ModNotes", { export const ModNotes = pgTable("ModNotes", {
id: uuid("id").default(sql`uuid_generate_v7()`).primaryKey().notNull(), id: id(),
nodeId: uuid("noteId").references(() => Notes.id, { nodeId: uuid("noteId").references(() => Notes.id, {
onDelete: "cascade", onDelete: "cascade",
onUpdate: "cascade", onUpdate: "cascade",
@ -538,13 +480,11 @@ export const ModNotes = pgTable("ModNotes", {
onUpdate: "cascade", onUpdate: "cascade",
}), }),
note: text("note").notNull(), note: text("note").notNull(),
createdAt: timestamp("created_at", { precision: 3, mode: "string" }) createdAt: createdAt(),
.defaultNow()
.notNull(),
}); });
export const ModTags = pgTable("ModTags", { export const ModTags = pgTable("ModTags", {
id: uuid("id").default(sql`uuid_generate_v7()`).primaryKey().notNull(), id: id(),
noteId: uuid("noteId").references(() => Notes.id, { noteId: uuid("noteId").references(() => Notes.id, {
onDelete: "cascade", onDelete: "cascade",
onUpdate: "cascade", onUpdate: "cascade",
@ -560,9 +500,7 @@ export const ModTags = pgTable("ModTags", {
onUpdate: "cascade", onUpdate: "cascade",
}), }),
tag: text("tag").notNull(), tag: text("tag").notNull(),
createdAt: timestamp("created_at", { precision: 3, mode: "string" }) createdAt: createdAt(),
.defaultNow()
.notNull(),
}); });
/** /**
@ -669,7 +607,7 @@ export const ADMIN_ROLES = [
]; ];
export const Roles = pgTable("Roles", { export const Roles = pgTable("Roles", {
id: uuid("id").default(sql`uuid_generate_v7()`).primaryKey().notNull(), id: id(),
name: text("name").notNull(), name: text("name").notNull(),
permissions: text("permissions") permissions: text("permissions")
.array() .array()
@ -727,12 +665,10 @@ export const EmojiToUser = pgTable(
onUpdate: "cascade", onUpdate: "cascade",
}), }),
}, },
(table) => { (table) => [
return { uniqueIndex().on(table.emojiId, table.userId),
abUnique: uniqueIndex().on(table.emojiId, table.userId), index().on(table.userId),
bIdx: index().on(table.userId), ],
};
},
); );
export const EmojiToUserRelations = relations(EmojiToUser, ({ one }) => ({ export const EmojiToUserRelations = relations(EmojiToUser, ({ one }) => ({
@ -762,12 +698,10 @@ export const EmojiToNote = pgTable(
onUpdate: "cascade", onUpdate: "cascade",
}), }),
}, },
(table) => { (table) => [
return { uniqueIndex().on(table.emojiId, table.noteId),
abUnique: uniqueIndex().on(table.emojiId, table.noteId), index().on(table.noteId),
bIdx: index().on(table.noteId), ],
};
},
); );
export const NoteToMentions = pgTable( export const NoteToMentions = pgTable(
@ -786,12 +720,10 @@ export const NoteToMentions = pgTable(
onUpdate: "cascade", onUpdate: "cascade",
}), }),
}, },
(table) => { (table) => [
return { uniqueIndex().on(table.noteId, table.userId),
abUnique: uniqueIndex().on(table.noteId, table.userId), index().on(table.userId),
bIdx: index().on(table.userId), ],
};
},
); );
export const UserToPinnedNotes = pgTable( export const UserToPinnedNotes = pgTable(
@ -810,12 +742,10 @@ export const UserToPinnedNotes = pgTable(
onUpdate: "cascade", onUpdate: "cascade",
}), }),
}, },
(table) => { (table) => [
return { uniqueIndex().on(table.userId, table.noteId),
abUnique: uniqueIndex().on(table.userId, table.noteId), index().on(table.noteId),
bIdx: index().on(table.noteId), ],
};
},
); );
export const AttachmentsRelations = relations(Attachments, ({ one }) => ({ export const AttachmentsRelations = relations(Attachments, ({ one }) => ({