feat: user auth, API keys, Stripe billing, and dashboard scoping
- NextAuth v5 credentials auth with registration/login pages - API key CRUD (create, list, revoke) with secure hashing - Stripe checkout, webhooks, and customer portal integration - Rate limiting per subscription tier - All dashboard API endpoints scoped to authenticated user - Prisma schema: User, Account, Session, ApiKey, plus Stripe fields - Auth middleware protecting dashboard and API routes Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-Claude) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
@@ -7,6 +7,82 @@ generator client {
|
||||
provider = "prisma-client-js"
|
||||
}
|
||||
|
||||
// ─── Auth & Billing ────────────────────────────────────────────
|
||||
|
||||
model User {
|
||||
id String @id @default(cuid())
|
||||
email String @unique
|
||||
passwordHash String
|
||||
name String?
|
||||
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
subscription Subscription?
|
||||
apiKeys ApiKey[]
|
||||
traces Trace[]
|
||||
|
||||
@@index([email])
|
||||
}
|
||||
|
||||
model ApiKey {
|
||||
id String @id @default(cuid())
|
||||
userId String
|
||||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||
|
||||
name String @default("Default")
|
||||
keyHash String @unique // SHA-256 hash of the actual key
|
||||
keyPrefix String // First 8 chars for display: "al_xxxx..."
|
||||
lastUsedAt DateTime?
|
||||
|
||||
revoked Boolean @default(false)
|
||||
createdAt DateTime @default(now())
|
||||
|
||||
@@index([keyHash])
|
||||
@@index([userId])
|
||||
}
|
||||
|
||||
model Subscription {
|
||||
id String @id @default(cuid())
|
||||
userId String @unique
|
||||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||
|
||||
tier SubscriptionTier @default(FREE)
|
||||
stripeCustomerId String? @unique
|
||||
stripeSubscriptionId String? @unique
|
||||
stripePriceId String?
|
||||
|
||||
currentPeriodStart DateTime?
|
||||
currentPeriodEnd DateTime?
|
||||
|
||||
// Usage tracking for the current billing period
|
||||
sessionsUsed Int @default(0)
|
||||
sessionsLimit Int @default(20) // Free tier: 20/day, paid: per month
|
||||
|
||||
status SubscriptionStatus @default(ACTIVE)
|
||||
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
@@index([stripeCustomerId])
|
||||
@@index([stripeSubscriptionId])
|
||||
}
|
||||
|
||||
enum SubscriptionTier {
|
||||
FREE // 20 sessions/day
|
||||
STARTER // $5/mo — 1,000 sessions/mo
|
||||
PRO // $20/mo — 100,000 sessions/mo
|
||||
}
|
||||
|
||||
enum SubscriptionStatus {
|
||||
ACTIVE
|
||||
PAST_DUE
|
||||
CANCELED
|
||||
UNPAID
|
||||
}
|
||||
|
||||
// ─── Observability ─────────────────────────────────────────────
|
||||
|
||||
model Trace {
|
||||
id String @id @default(cuid())
|
||||
sessionId String?
|
||||
@@ -15,6 +91,10 @@ model Trace {
|
||||
tags String[] @default([])
|
||||
metadata Json?
|
||||
|
||||
// Owner — nullable for backward compat with existing unowned traces
|
||||
userId String?
|
||||
user User? @relation(fields: [userId], references: [id], onDelete: SetNull)
|
||||
|
||||
totalCost Float?
|
||||
totalTokens Int?
|
||||
totalDuration Int?
|
||||
@@ -32,6 +112,7 @@ model Trace {
|
||||
@@index([status])
|
||||
@@index([createdAt])
|
||||
@@index([name])
|
||||
@@index([userId])
|
||||
}
|
||||
|
||||
model DecisionPoint {
|
||||
|
||||
Reference in New Issue
Block a user