/** * Support tables: referrals, changelogs, onboarding, api_keys, * notifications, operational events, audit logs. * Source: `db/migrations/0001_init.sql`. */ import { pgTable, uuid, text, varchar, boolean, timestamp, jsonb, integer, numeric, time, index, uniqueIndex, } from "drizzle-orm/pg-core"; import { brands, adminUsers } from "./brands"; // ── Referrals ────────────────────────────────────────────────────────────── export const referralCodes = pgTable( "referral_codes", { id: uuid("id").primaryKey().defaultRandom(), brandId: uuid("brand_id").references(() => brands.id, { onDelete: "cascade", }), referrerUserId: uuid("referrer_user_id").notNull(), referralCode: varchar("referral_code", { length: 50 }).notNull().unique(), referrerEmail: varchar("referrer_email", { length: 255 }).notNull(), createdAt: timestamp("created_at", { withTimezone: true }) .notNull() .defaultNow(), expiresAt: timestamp("expires_at", { withTimezone: true }), maxUses: integer("max_uses").default(1), currentUses: integer("current_uses").default(0), isActive: boolean("is_active").default(true), rewardType: varchar("reward_type", { length: 50 }).default("percentage"), rewardValue: numeric("reward_value", { precision: 10, scale: 2 }).default("20.00"), metadata: jsonb("metadata").default({}), }, ); export const referralRedemptions = pgTable( "referral_redemptions", { id: uuid("id").primaryKey().defaultRandom(), referralCodeId: uuid("referral_code_id").references( () => referralCodes.id, { onDelete: "cascade" }, ), brandId: uuid("brand_id").references(() => brands.id, { onDelete: "cascade", }), referredUserId: uuid("referred_user_id").notNull(), referredEmail: varchar("referred_email", { length: 255 }).notNull(), redeemedAt: timestamp("redeemed_at", { withTimezone: true }) .notNull() .defaultNow(), rewardType: varchar("reward_type", { length: 50 }), rewardValue: numeric("reward_value", { precision: 10, scale: 2 }), isCreditApplied: boolean("is_credit_applied").default(false), signupPlan: varchar("signup_plan", { length: 50 }), signupValue: numeric("signup_value", { precision: 10, scale: 2 }), metadata: jsonb("metadata").default({}), }, ); // ── Changelogs ───────────────────────────────────────────────────────────── export const changelogs = pgTable( "changelogs", { id: uuid("id").primaryKey().defaultRandom(), brandId: uuid("brand_id").references(() => brands.id, { onDelete: "cascade", }), version: varchar("version", { length: 50 }).notNull(), title: varchar("title", { length: 255 }).notNull(), description: text("description"), content: jsonb("content").notNull().default([]), releasedAt: timestamp("released_at", { withTimezone: true }) .notNull() .defaultNow(), isPublished: boolean("is_published").default(false), featureType: varchar("feature_type", { length: 50 }).default("general"), metadata: jsonb("metadata").default({}), }, (t) => ({ brandVersionIdx: uniqueIndex("changelogs_brand_version_idx").on( t.brandId, t.version, ), }), ); export const changelogReads = pgTable( "changelog_reads", { id: uuid("id").primaryKey().defaultRandom(), userId: uuid("user_id").notNull(), changelogId: uuid("changelog_id").references(() => changelogs.id, { onDelete: "cascade", }), readAt: timestamp("read_at", { withTimezone: true }) .notNull() .defaultNow(), dismissed: boolean("dismissed").default(false), }, (t) => ({ userChangelogIdx: uniqueIndex("changelog_reads_user_changelog_idx").on( t.userId, t.changelogId, ), }), ); // ── Onboarding ───────────────────────────────────────────────────────────── export const onboardingProgress = pgTable( "onboarding_progress", { id: uuid("id").primaryKey().defaultRandom(), brandId: uuid("brand_id").references(() => brands.id, { onDelete: "cascade", }), userId: uuid("user_id").notNull(), currentStep: varchar("current_step", { length: 50 }).notNull(), completedSteps: jsonb("completed_steps").default([]), skippedSteps: jsonb("skipped_steps").default([]), data: jsonb("data").default({}), startedAt: timestamp("started_at", { withTimezone: true }) .notNull() .defaultNow(), completedAt: timestamp("completed_at", { withTimezone: true }), isCompleted: boolean("is_completed").default(false), metadata: jsonb("metadata").default({}), }, (t) => ({ brandUserIdx: uniqueIndex("onboarding_progress_brand_user_idx").on( t.brandId, t.userId, ), }), ); // ── API Keys ──────────────────────────────────────────────────────────────── export const apiKeys = pgTable( "api_keys", { id: uuid("id").primaryKey().defaultRandom(), brandId: uuid("brand_id").references(() => brands.id, { onDelete: "cascade", }), name: varchar("name", { length: 255 }).notNull(), keyHash: varchar("key_hash", { length: 255 }).notNull(), keyPrefix: varchar("key_prefix", { length: 20 }), permissions: jsonb("permissions").default(["read"]), rateLimit: integer("rate_limit").default(1000), lastUsedAt: timestamp("last_used_at", { withTimezone: true }), expiresAt: timestamp("expires_at", { withTimezone: true }), isActive: boolean("is_active").default(true), createdAt: timestamp("created_at", { withTimezone: true }) .notNull() .defaultNow(), createdBy: uuid("created_by").notNull(), }, (t) => ({ brandIdx: index("api_keys_brand_idx").on(t.brandId), }), ); // ── Notification Preferences ─────────────────────────────────────────────── export const notificationPreferences = pgTable( "notification_preferences", { id: uuid("id").primaryKey().defaultRandom(), userId: uuid("user_id").notNull(), brandId: uuid("brand_id").references(() => brands.id, { onDelete: "cascade", }), emailOrders: boolean("email_orders").default(true), emailMarketing: boolean("email_marketing").default(false), emailReports: boolean("email_reports").default(true), emailBilling: boolean("email_billing").default(true), smsOrders: boolean("sms_orders").default(false), smsMarketing: boolean("sms_marketing").default(false), pushOrders: boolean("push_orders").default(true), pushMarketing: boolean("push_marketing").default(false), quietHoursStart: time("quiet_hours_start"), quietHoursEnd: time("quiet_hours_end"), timezone: varchar("timezone", { length: 50 }).default("America/New_York"), metadata: jsonb("metadata").default({}), updatedAt: timestamp("updated_at", { withTimezone: true }) .notNull() .defaultNow(), }, (t) => ({ userBrandIdx: uniqueIndex("notification_preferences_user_brand_idx").on( t.userId, t.brandId, ), }), ); // ── Operational Events ───────────────────────────────────────────────────── export const operationalEvents = pgTable( "operational_events", { id: uuid("id").primaryKey().defaultRandom(), brandId: uuid("brand_id") .notNull() .references(() => brands.id, { onDelete: "cascade" }), eventType: text("event_type").notNull(), entityType: text("entity_type"), entityId: uuid("entity_id"), actorType: text("actor_type"), actorId: uuid("actor_id"), source: text("source").notNull().default("system"), payload: jsonb("payload").notNull().default({}), createdAt: timestamp("created_at", { withTimezone: true }) .notNull() .defaultNow(), }, (t) => ({ brandIdx: index("operational_events_brand_idx").on(t.brandId), typeIdx: index("operational_events_type_idx").on(t.brandId, t.eventType), }), ); // ── Audit Logs ───────────────────────────────────────────────────────────── export const auditLogs = pgTable( "audit_logs", { id: uuid("id").primaryKey().defaultRandom(), userId: uuid("user_id"), brandId: uuid("brand_id").references(() => brands.id, { onDelete: "set null", }), action: text("action").notNull(), entityType: text("entity_type"), entityId: uuid("entity_id"), details: jsonb("details"), ipAddress: text("ip_address"), createdAt: timestamp("created_at", { withTimezone: true }) .notNull() .defaultNow(), }, (t) => ({ brandIdx: index("audit_logs_brand_idx").on(t.brandId), userIdx: index("audit_logs_user_idx").on(t.userId), createdIdx: index("audit_logs_created_idx").on(t.createdAt), }), ); export const adminActionLogs = pgTable( "admin_action_logs", { id: uuid("id").primaryKey().defaultRandom(), adminUserId: uuid("admin_user_id") .notNull() .references(() => adminUsers.id), brandId: uuid("brand_id").references(() => brands.id, { onDelete: "set null", }), action: text("action").notNull(), targetType: text("target_type"), targetId: uuid("target_id"), metadata: jsonb("metadata"), ipAddress: text("ip_address"), createdAt: timestamp("created_at", { withTimezone: true }) .notNull() .defaultNow(), }, (t) => ({ adminIdx: index("admin_action_logs_admin_idx").on(t.adminUserId), createdIdx: index("admin_action_logs_created_idx").on(t.createdAt), }), ); export type ReferralCode = typeof referralCodes.$inferSelect; export type ReferralRedemption = typeof referralRedemptions.$inferSelect; export type Changelog = typeof changelogs.$inferSelect; export type ChangelogRead = typeof changelogReads.$inferSelect; export type OnboardingProgress = typeof onboardingProgress.$inferSelect; export type ApiKey = typeof apiKeys.$inferSelect; export type NotificationPreference = typeof notificationPreferences.$inferSelect; export type OperationalEvent = typeof operationalEvents.$inferSelect; export type AuditLog = typeof auditLogs.$inferSelect; export type AdminActionLog = typeof adminActionLogs.$inferSelect;