import type { AdapterAccountType } from "@auth/core/adapters"; import { boolean, index, integer, jsonb, pgEnum, pgTable, primaryKey, text, timestamp, uniqueIndex, uuid, varchar, } from "drizzle-orm/pg-core"; export const roleEnum = pgEnum("household_member_role", ["owner", "member"]); // Auth.js-compatible users table. "name" and "image" are populated from OIDC claims. export const users = pgTable("users", { id: uuid("id").primaryKey().defaultRandom(), name: text("name"), email: varchar("email", { length: 255 }).notNull().unique(), emailVerified: timestamp("email_verified", { withTimezone: true }), image: text("image"), themePalette: text("theme_palette").notNull().default("clay"), themeMode: text("theme_mode").notNull().default("system"), themeFontPair: text("theme_font_pair").notNull().default("serif-sans"), themeDensity: text("theme_density").notNull().default("regular"), themeDashLayout: text("theme_dash_layout").notNull().default("classic"), themeCalView: text("theme_cal_view").notNull().default("month"), themeNavStyle: text("theme_nav_style").notNull().default("rail-desktop"), completionVisibilityHours: integer("completion_visibility_hours").notNull().default(24), notifPush: boolean("notif_push").notNull().default(true), notifInApp: boolean("notif_inapp").notNull().default(true), notifNtfy: boolean("notif_ntfy").notNull().default(false), createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(), }); // Auth.js adapter tables export const accounts = pgTable( "accounts", { userId: uuid("user_id") .notNull() .references(() => users.id, { onDelete: "cascade" }), type: text("type").$type().notNull(), provider: text("provider").notNull(), providerAccountId: text("provider_account_id").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"), }, (t) => [primaryKey({ columns: [t.provider, t.providerAccountId] })], ); export const sessions = pgTable("sessions", { sessionToken: text("session_token").primaryKey(), userId: uuid("user_id") .notNull() .references(() => users.id, { onDelete: "cascade" }), expires: timestamp("expires", { withTimezone: true }).notNull(), }); export const verificationTokens = pgTable( "verification_tokens", { identifier: text("identifier").notNull(), token: text("token").notNull(), expires: timestamp("expires", { withTimezone: true }).notNull(), }, (t) => [primaryKey({ columns: [t.identifier, t.token] })], ); export const households = pgTable("households", { id: uuid("id").primaryKey().defaultRandom(), name: varchar("name", { length: 255 }).notNull(), createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(), }); export const householdMembers = pgTable( "household_members", { householdId: uuid("household_id") .notNull() .references(() => households.id, { onDelete: "cascade" }), userId: uuid("user_id") .notNull() .references(() => users.id, { onDelete: "cascade" }), role: roleEnum("role").notNull().default("member"), }, (t) => [primaryKey({ columns: [t.householdId, t.userId] })], ); export const activityLog = pgTable( "activity_log", { id: uuid("id").primaryKey().defaultRandom(), householdId: uuid("household_id") .notNull() .references(() => households.id, { onDelete: "cascade" }), entityType: text("entity_type").notNull(), entityId: uuid("entity_id").notNull(), actorId: uuid("actor_id").references(() => users.id, { onDelete: "set null" }), action: text("action").notNull(), payload: jsonb("payload").$type | null>(), createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(), }, (t) => [index("activity_log_household_created_idx").on(t.householdId, t.createdAt)], ); export const shareLinks = pgTable( "share_links", { id: uuid("id").primaryKey().defaultRandom(), householdId: uuid("household_id") .notNull() .references(() => households.id, { onDelete: "cascade" }), entityType: text("entity_type").notNull(), entityId: uuid("entity_id").notNull(), token: text("token").notNull().unique(), capabilities: jsonb("capabilities").$type<{ read: boolean; write: boolean }>().notNull(), createdBy: uuid("created_by") .notNull() .references(() => users.id, { onDelete: "cascade" }), expiresAt: timestamp("expires_at", { withTimezone: true }), revokedAt: timestamp("revoked_at", { withTimezone: true }), createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(), }, (t) => [ index("share_links_household_idx").on(t.householdId), index("share_links_entity_idx").on(t.entityType, t.entityId), ], ); export const dashboards = pgTable( "dashboards", { id: uuid("id").primaryKey().defaultRandom(), userId: uuid("user_id") .notNull() .references(() => users.id, { onDelete: "cascade" }), name: text("name").notNull(), slug: text("slug").notNull(), isDefault: boolean("is_default").notNull().default(false), position: integer("position").notNull().default(0), layout: jsonb("layout").notNull().default({ version: 1, widgets: [] }), createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(), updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow(), }, (t) => [uniqueIndex("dashboards_user_slug_uq").on(t.userId, t.slug)], ); export const reminders = pgTable( "reminders", { id: uuid("id").primaryKey().defaultRandom(), householdId: uuid("household_id") .notNull() .references(() => households.id, { onDelete: "cascade" }), entityType: text("entity_type").notNull(), entityId: uuid("entity_id").notNull(), fireAt: timestamp("fire_at", { withTimezone: true }).notNull(), channel: text("channel").notNull().default("auto"), title: text("title"), body: text("body"), firedAt: timestamp("fired_at", { withTimezone: true }), createdBy: uuid("created_by").references(() => users.id, { onDelete: "set null" }), createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(), }, (t) => [ uniqueIndex("reminders_entity_unique").on(t.entityType, t.entityId), index("reminders_household_fire_at_idx").on(t.householdId, t.fireAt), ], ); export const pushSubscriptions = pgTable( "push_subscriptions", { id: uuid("id").primaryKey().defaultRandom(), userId: uuid("user_id") .notNull() .references(() => users.id, { onDelete: "cascade" }), endpoint: text("endpoint").notNull().unique(), p256dh: text("p256dh").notNull(), auth: text("auth").notNull(), userAgent: text("user_agent"), createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(), }, (t) => [index("push_subscriptions_user_idx").on(t.userId)], ); export const notifications = pgTable( "notifications", { id: uuid("id").primaryKey().defaultRandom(), userId: uuid("user_id") .notNull() .references(() => users.id, { onDelete: "cascade" }), title: text("title").notNull(), body: text("body").notNull(), url: text("url"), readAt: timestamp("read_at", { withTimezone: true }), createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(), }, (t) => [index("notifications_user_read_idx").on(t.userId, t.readAt)], );