import type { AdapterAccount } from "@auth/core/adapters"; import type { MantineColor } from "@mantine/core"; import type { InferSelectModel } from "drizzle-orm"; import { relations } from "drizzle-orm"; import { index, int, integer, primaryKey, sqliteTable, text, } from "drizzle-orm/sqlite-core"; import type { BackgroundImageAttachment, BackgroundImageRepeat, BackgroundImageSize, IntegrationKind, IntegrationSecretKind, SectionKind, WidgetKind, } from "@homarr/definitions"; export const users = sqliteTable("user", { id: text("id").notNull().primaryKey(), name: text("name"), email: text("email"), emailVerified: integer("emailVerified", { mode: "timestamp_ms" }), image: text("image"), password: text("password"), salt: text("salt"), }); export const accounts = sqliteTable( "account", { userId: text("userId") .notNull() .references(() => users.id, { onDelete: "cascade" }), type: text("type").$type().notNull(), provider: text("provider").notNull(), providerAccountId: text("providerAccountId").notNull(), refresh_token: text("refresh_token"), access_token: text("access_token"), expires_at: integer("expires_at"), token_type: text("token_type"), scope: text("scope"), id_token: text("id_token"), session_state: text("session_state"), }, (account) => ({ compoundKey: primaryKey({ columns: [account.provider, account.providerAccountId], }), userIdIdx: index("userId_idx").on(account.userId), }), ); export const sessions = sqliteTable( "session", { sessionToken: text("sessionToken").notNull().primaryKey(), userId: text("userId") .notNull() .references(() => users.id, { onDelete: "cascade" }), expires: integer("expires", { mode: "timestamp_ms" }).notNull(), }, (session) => ({ userIdIdx: index("user_id_idx").on(session.userId), }), ); export const verificationTokens = sqliteTable( "verificationToken", { identifier: text("identifier").notNull(), token: text("token").notNull(), expires: integer("expires", { mode: "timestamp_ms" }).notNull(), }, (vt) => ({ compoundKey: primaryKey({ columns: [vt.identifier, vt.token] }), }), ); export const integrations = sqliteTable( "integration", { id: text("id").notNull().primaryKey(), name: text("name").notNull(), url: text("url").notNull(), kind: text("kind").$type().notNull(), }, (i) => ({ kindIdx: index("integration__kind_idx").on(i.kind), }), ); export const integrationSecrets = sqliteTable( "integrationSecret", { kind: text("kind").$type().notNull(), value: text("value").$type<`${string}.${string}`>().notNull(), updatedAt: integer("updated_at", { mode: "timestamp" }).notNull(), integrationId: text("integration_id") .notNull() .references(() => integrations.id, { onDelete: "cascade" }), }, (is) => ({ compoundKey: primaryKey({ columns: [is.integrationId, is.kind], }), kindIdx: index("integration_secret__kind_idx").on(is.kind), updatedAtIdx: index("integration_secret__updated_at_idx").on(is.updatedAt), }), ); export const boards = sqliteTable("board", { id: text("id").notNull().primaryKey(), name: text("name").notNull(), isPublic: int("is_public", { mode: "boolean" }).default(false).notNull(), pageTitle: text("page_title"), metaTitle: text("meta_title"), logoImageUrl: text("logo_image_url"), faviconImageUrl: text("favicon_image_url"), backgroundImageUrl: text("background_image_url"), backgroundImageAttachment: text("background_image_attachment") .$type() .default("fixed") .notNull(), backgroundImageRepeat: text("background_image_repeat") .$type() .default("no-repeat") .notNull(), backgroundImageSize: text("background_image_size") .$type() .default("cover") .notNull(), primaryColor: text("primary_color") .$type() .default("red") .notNull(), secondaryColor: text("secondary_color") .$type() .default("orange") .notNull(), primaryShade: int("primary_shade").default(6).notNull(), appOpacity: int("app_opacity").default(100).notNull(), customCss: text("custom_css"), showRightSidebar: int("show_right_sidebar", { mode: "boolean", }) .default(false) .notNull(), showLeftSidebar: int("show_left_sidebar", { mode: "boolean", }) .default(false) .notNull(), columnCount: int("column_count").default(10).notNull(), }); export const sections = sqliteTable("section", { id: text("id").notNull().primaryKey(), boardId: text("board_id") .notNull() .references(() => boards.id, { onDelete: "cascade" }), kind: text("kind").$type().notNull(), position: int("position").notNull(), name: text("name"), }); export const items = sqliteTable("item", { id: text("id").notNull().primaryKey(), sectionId: text("section_id") .notNull() .references(() => sections.id, { onDelete: "cascade" }), kind: text("kind").$type().notNull(), xOffset: int("x_offset").notNull(), yOffset: int("y_offset").notNull(), width: int("width").notNull(), height: int("height").notNull(), options: text("options").default('{"json": {}}').notNull(), // empty superjson object }); export const integrationItems = sqliteTable( "integration_item", { itemId: text("item_id") .notNull() .references(() => items.id, { onDelete: "cascade" }), integrationId: text("integration_id") .notNull() .references(() => integrations.id, { onDelete: "cascade" }), }, (table) => ({ compoundKey: primaryKey({ columns: [table.itemId, table.integrationId], }), }), ); export const accountRelations = relations(accounts, ({ one }) => ({ user: one(users, { fields: [accounts.userId], references: [users.id], }), })); export const userRelations = relations(users, ({ many }) => ({ accounts: many(accounts), })); export const integrationRelations = relations(integrations, ({ many }) => ({ secrets: many(integrationSecrets), items: many(integrationItems), })); export const integrationSecretRelations = relations( integrationSecrets, ({ one }) => ({ integration: one(integrations, { fields: [integrationSecrets.integrationId], references: [integrations.id], }), }), ); export const boardRelations = relations(boards, ({ many }) => ({ sections: many(sections), })); export const sectionRelations = relations(sections, ({ many, one }) => ({ items: many(items), board: one(boards, { fields: [sections.boardId], references: [boards.id], }), })); export const itemRelations = relations(items, ({ one, many }) => ({ section: one(sections, { fields: [items.sectionId], references: [sections.id], }), integrations: many(integrationItems), })); export const integrationItemRelations = relations( integrationItems, ({ one }) => ({ integration: one(integrations, { fields: [integrationItems.integrationId], references: [integrations.id], }), item: one(items, { fields: [integrationItems.itemId], references: [items.id], }), }), ); export type User = InferSelectModel; export type Account = InferSelectModel; export type Session = InferSelectModel; export type VerificationToken = InferSelectModel; export type Integration = InferSelectModel; export type IntegrationSecret = InferSelectModel;