All files / web/src/db/schema subscriptions.ts

100% Statements 56/56
100% Branches 1/1
100% Functions 0/0
100% Lines 56/56

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 572x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x  
import { createId } from '@paralleldrive/cuid2'
import { integer, sqliteTable, text } from 'drizzle-orm/sqlite-core'
import { users } from './users'
 
/**
 * Subscriptions table — one per user.
 *
 * Maps a user to their Stripe subscription and tracks plan/status.
 * Users without a row here are on the free tier.
 */
export const subscriptions = sqliteTable('subscriptions', {
  id: text('id')
    .primaryKey()
    .$defaultFn(() => createId()),
 
  /** User FK — unique, one subscription per user */
  userId: text('user_id')
    .notNull()
    .unique()
    .references(() => users.id),
 
  /** Stripe customer ID (cus_...) */
  stripeCustomerId: text('stripe_customer_id').notNull(),
 
  /** Stripe subscription ID (sub_...) */
  stripeSubscriptionId: text('stripe_subscription_id').unique(),
 
  /** Plan name — matches TierName (excluding 'guest') */
  plan: text('plan', { enum: ['free', 'family'] })
    .notNull()
    .default('free'),
 
  /** Subscription lifecycle status */
  status: text('status', {
    enum: ['active', 'past_due', 'canceled', 'trialing'],
  })
    .notNull()
    .default('active'),
 
  /** When the current billing period ends */
  currentPeriodEnd: integer('current_period_end', { mode: 'timestamp' }),
 
  /** Whether the subscription will cancel at period end */
  cancelAtPeriodEnd: integer('cancel_at_period_end', { mode: 'boolean' }).notNull().default(false),
 
  createdAt: integer('created_at', { mode: 'timestamp' })
    .notNull()
    .$defaultFn(() => new Date()),
 
  updatedAt: integer('updated_at', { mode: 'timestamp' })
    .notNull()
    .$defaultFn(() => new Date()),
})
 
export type Subscription = typeof subscriptions.$inferSelect
export type NewSubscription = typeof subscriptions.$inferInsert