import { integer, real, sqliteTable, text } from "drizzle-orm/sqlite-core"; export const categories = sqliteTable("categories", { id: integer("id").primaryKey({ autoIncrement: true }), name: text("name").notNull().unique(), icon: text("icon").notNull().default("package"), createdAt: integer("created_at", { mode: "timestamp" }) .notNull() .$defaultFn(() => new Date()), }); export const items = sqliteTable("items", { id: integer("id").primaryKey({ autoIncrement: true }), name: text("name").notNull(), weightGrams: real("weight_grams"), priceCents: integer("price_cents"), categoryId: integer("category_id") .notNull() .references(() => categories.id), notes: text("notes"), productUrl: text("product_url"), imageFilename: text("image_filename"), imageSourceUrl: text("image_source_url"), quantity: integer("quantity").notNull().default(1), createdAt: integer("created_at", { mode: "timestamp" }) .notNull() .$defaultFn(() => new Date()), updatedAt: integer("updated_at", { mode: "timestamp" }) .notNull() .$defaultFn(() => new Date()), }); export const threads = sqliteTable("threads", { id: integer("id").primaryKey({ autoIncrement: true }), name: text("name").notNull(), status: text("status").notNull().default("active"), resolvedCandidateId: integer("resolved_candidate_id"), categoryId: integer("category_id") .notNull() .references(() => categories.id), createdAt: integer("created_at", { mode: "timestamp" }) .notNull() .$defaultFn(() => new Date()), updatedAt: integer("updated_at", { mode: "timestamp" }) .notNull() .$defaultFn(() => new Date()), }); export const threadCandidates = sqliteTable("thread_candidates", { id: integer("id").primaryKey({ autoIncrement: true }), threadId: integer("thread_id") .notNull() .references(() => threads.id, { onDelete: "cascade" }), name: text("name").notNull(), weightGrams: real("weight_grams"), priceCents: integer("price_cents"), categoryId: integer("category_id") .notNull() .references(() => categories.id), notes: text("notes"), productUrl: text("product_url"), imageFilename: text("image_filename"), imageSourceUrl: text("image_source_url"), status: text("status").notNull().default("researching"), pros: text("pros"), cons: text("cons"), sortOrder: real("sort_order").notNull().default(0), createdAt: integer("created_at", { mode: "timestamp" }) .notNull() .$defaultFn(() => new Date()), updatedAt: integer("updated_at", { mode: "timestamp" }) .notNull() .$defaultFn(() => new Date()), }); export const setups = sqliteTable("setups", { id: integer("id").primaryKey({ autoIncrement: true }), name: text("name").notNull(), createdAt: integer("created_at", { mode: "timestamp" }) .notNull() .$defaultFn(() => new Date()), updatedAt: integer("updated_at", { mode: "timestamp" }) .notNull() .$defaultFn(() => new Date()), }); export const setupItems = sqliteTable("setup_items", { id: integer("id").primaryKey({ autoIncrement: true }), setupId: integer("setup_id") .notNull() .references(() => setups.id, { onDelete: "cascade" }), itemId: integer("item_id") .notNull() .references(() => items.id, { onDelete: "cascade" }), classification: text("classification").notNull().default("base"), }); export const settings = sqliteTable("settings", { key: text("key").primaryKey(), value: text("value").notNull(), }); export const users = sqliteTable("users", { id: integer("id").primaryKey({ autoIncrement: true }), username: text("username").notNull().unique(), passwordHash: text("password_hash").notNull(), createdAt: integer("created_at", { mode: "timestamp" }) .notNull() .$defaultFn(() => new Date()), }); export const sessions = sqliteTable("sessions", { id: text("id").primaryKey(), userId: integer("user_id") .notNull() .references(() => users.id, { onDelete: "cascade" }), expiresAt: integer("expires_at", { mode: "timestamp" }).notNull(), }); export const apiKeys = sqliteTable("api_keys", { id: integer("id").primaryKey({ autoIncrement: true }), name: text("name").notNull(), keyHash: text("key_hash").notNull(), keyPrefix: text("key_prefix").notNull(), createdAt: integer("created_at", { mode: "timestamp" }) .notNull() .$defaultFn(() => new Date()), }); export const globalItems = sqliteTable("global_items", { id: integer("id").primaryKey({ autoIncrement: true }), brand: text("brand").notNull(), model: text("model").notNull(), category: text("category"), weightGrams: real("weight_grams"), priceCents: integer("price_cents"), imageUrl: text("image_url"), description: text("description"), createdAt: integer("created_at", { mode: "timestamp" }) .notNull() .$defaultFn(() => new Date()), }); export const itemGlobalLinks = sqliteTable("item_global_links", { id: integer("id").primaryKey({ autoIncrement: true }), itemId: integer("item_id") .notNull() .references(() => items.id, { onDelete: "cascade" }) .unique(), globalItemId: integer("global_item_id") .notNull() .references(() => globalItems.id, { onDelete: "cascade" }), }); export const oauthClients = sqliteTable("oauth_clients", { id: integer("id").primaryKey({ autoIncrement: true }), clientId: text("client_id").notNull().unique(), clientName: text("client_name"), redirectUris: text("redirect_uris").notNull(), // JSON array createdAt: integer("created_at", { mode: "timestamp" }) .notNull() .$defaultFn(() => new Date()), }); export const oauthCodes = sqliteTable("oauth_codes", { id: integer("id").primaryKey({ autoIncrement: true }), code: text("code").notNull().unique(), clientId: text("client_id").notNull(), codeChallenge: text("code_challenge").notNull(), codeChallengeMethod: text("code_challenge_method").notNull().default("S256"), redirectUri: text("redirect_uri").notNull(), expiresAt: integer("expires_at", { mode: "timestamp" }).notNull(), used: integer("used").notNull().default(0), }); export const oauthTokens = sqliteTable("oauth_tokens", { id: integer("id").primaryKey({ autoIncrement: true }), accessTokenHash: text("access_token_hash").notNull().unique(), refreshTokenHash: text("refresh_token_hash").notNull().unique(), clientId: text("client_id").notNull(), expiresAt: integer("expires_at", { mode: "timestamp" }).notNull(), // access token expiry refreshExpiresAt: integer("refresh_expires_at", { mode: "timestamp", }).notNull(), // refresh token expiry createdAt: integer("created_at", { mode: "timestamp" }) .notNull() .$defaultFn(() => new Date()), });