import type { ProcessorConfigs } from "@models/genModels/processorSchemas.js"; import type { PendingMigration } from "@models/migrationV2Models/pendingMigrationModel.js"; import { sql } from "drizzle-orm"; import { boolean, index, jsonb, numeric, pgTable, text, timestamp, unique, } from "drizzle-orm/pg-core"; import type { OrgConfig } from "./orgConfig.js"; export type SvixConfig = { sandbox_app_id: string; live_app_id: string; }; export type StripeConfig = { test_api_key?: string; live_api_key?: string; test_webhook_secret?: string; live_webhook_secret?: string; sandbox_success_url?: string; success_url?: string; test_connect_webhook_secret?: string; live_connect_webhook_secret?: string; }; export interface VersionConfig { sandbox?: string; live?: string; // sandbox_webhooks: string; // live_webhooks: string; } export type StripeConnectConfig = { default_account_id?: string; account_id?: string; master_org_id?: string; }; export type OrgRedisConfig = { /** AES-256-CBC encrypted full Redis connection string via encryptData() */ connectionString: string; /** Plain domain/host only, used for pool URL-change detection */ url: string; /** Percentage of customers routed to the dedicated Redis (0-100) */ migrationPercent: number; /** The migrationPercent before the last change, used for staleness detection */ previousMigrationPercent: number; /** Epoch ms when migrationPercent was last changed */ migrationChangedAt: number; }; export const organizations = pgTable( "organizations", { id: text().primaryKey(), slug: text().notNull().unique(), // Better Auth name: text("name").notNull(), logo: text("logo"), createdAt: timestamp("createdAt", { withTimezone: true }).notNull(), metadata: text("metadata"), // Stripe default_currency: text("default_currency").default("usd"), stripe_connected: boolean("stripe_connected").default(false), stripe_config: jsonb("stripe_config").$type(), test_stripe_connect: jsonb("test_stripe_connect") .$type() .default({} as StripeConnectConfig), live_stripe_connect: jsonb("live_stripe_connect") .$type() .default({} as StripeConnectConfig), // stripe_connect: jsonb("stripe_connect") // .$type() // .default({} as StripeConnectConfig) // .notNull(), processor_configs: jsonb("processor_configs").$type(), test_pkey: text("test_pkey"), live_pkey: text("live_pkey"), svix_config: jsonb("svix_config") .$type() .default(sql`'{}'::jsonb`), created_at: numeric({ mode: "number" }), config: jsonb().default({}).notNull().$type(), created_by: text("created_by"), onboarded: boolean("onboarded").default(false), deployed: boolean("deployed").default(false), redis_config: jsonb("redis_config").$type(), }, (table) => [ index("idx_organizations_name_trgm") .using("gin", sql`${table.name} gin_trgm_ops`) .where(sql`${table.name} IS NOT NULL`), index("idx_organizations_slug_trgm") .using("gin", sql`${table.slug} gin_trgm_ops`) .where(sql`${table.slug} IS NOT NULL`), index("idx_organizations_created_at_id").on( sql`${table.createdAt} DESC`, sql`${table.id} DESC`, ), unique("organizations_test_pkey_key").on(table.test_pkey), unique("organizations_live_pkey_key").on(table.live_pkey), ], ); export type Organization = typeof organizations.$inferSelect & { master: Organization | null; pendingMigrations?: PendingMigration[]; }; // Multi tenancy flow <-> stripe connect... // Create org in Autumn, don't need stripe connect key, we create an Autumn connect account for them. // Connect own stripe to sandbox / prod // 1. OAuth to link their stripe account (?) -> need to use access token though // 2. Paste in their secret key // 3. Onboard onto Stripe connect (?)