// This is your Prisma schema file, // learn more about it in the docs: https://pris.ly/d/prisma-schema generator client { provider = "prisma-client-js" } datasource db { provider = "postgresql" url = env("DATABASE_URL") } // ============================================================================ // ENUMS // ============================================================================ enum UserStatus { PENDING_VERIFICATION ACTIVE SUSPENDED } enum StaffRole { ADMIN SUPPORT } enum SubscriptionPlan { TRIAL STARTER PRO ENTERPRISE } enum SubscriptionTier { HUB_DASHBOARD ADVANCED } enum SubscriptionStatus { TRIAL ACTIVE CANCELED PAST_DUE } enum OrderStatus { PAYMENT_CONFIRMED AWAITING_SERVER SERVER_READY DNS_PENDING DNS_READY PROVISIONING FULFILLED EMAIL_CONFIGURED FAILED } enum JobStatus { PENDING CLAIMED RUNNING COMPLETED FAILED DEAD } enum LogLevel { DEBUG INFO WARN ERROR } // ============================================================================ // USER & STAFF MODELS // ============================================================================ model User { id String @id @default(cuid()) email String @unique passwordHash String @map("password_hash") name String? company String? status UserStatus @default(PENDING_VERIFICATION) emailVerified DateTime? @map("email_verified") createdAt DateTime @default(now()) @map("created_at") updatedAt DateTime @updatedAt @map("updated_at") subscriptions Subscription[] orders Order[] tokenUsage TokenUsage[] @@map("users") } model Staff { id String @id @default(cuid()) email String @unique passwordHash String @map("password_hash") name String role StaffRole @default(SUPPORT) createdAt DateTime @default(now()) @map("created_at") updatedAt DateTime @updatedAt @map("updated_at") @@map("staff") } // ============================================================================ // SUBSCRIPTION & BILLING // ============================================================================ model Subscription { id String @id @default(cuid()) userId String @map("user_id") plan SubscriptionPlan @default(TRIAL) tier SubscriptionTier @default(HUB_DASHBOARD) tokenLimit Int @default(10000) @map("token_limit") tokensUsed Int @default(0) @map("tokens_used") trialEndsAt DateTime? @map("trial_ends_at") stripeCustomerId String? @map("stripe_customer_id") stripeSubscriptionId String? @map("stripe_subscription_id") status SubscriptionStatus @default(TRIAL) createdAt DateTime @default(now()) @map("created_at") updatedAt DateTime @updatedAt @map("updated_at") user User @relation(fields: [userId], references: [id], onDelete: Cascade) @@map("subscriptions") } // ============================================================================ // ORDERS & PROVISIONING // ============================================================================ model Order { id String @id @default(cuid()) userId String @map("user_id") status OrderStatus @default(PAYMENT_CONFIRMED) tier SubscriptionTier domain String tools String[] configJson Json @map("config_json") // Server credentials (entered by staff) serverIp String? @map("server_ip") serverPasswordEncrypted String? @map("server_password_encrypted") sshPort Int @default(22) @map("ssh_port") // Generated after provisioning portainerUrl String? @map("portainer_url") dashboardUrl String? @map("dashboard_url") failureReason String? @map("failure_reason") // Timestamps createdAt DateTime @default(now()) @map("created_at") updatedAt DateTime @updatedAt @map("updated_at") serverReadyAt DateTime? @map("server_ready_at") provisioningStartedAt DateTime? @map("provisioning_started_at") completedAt DateTime? @map("completed_at") user User @relation(fields: [userId], references: [id], onDelete: Cascade) provisioningLogs ProvisioningLog[] jobs ProvisioningJob[] @@map("orders") } model ProvisioningLog { id String @id @default(cuid()) orderId String @map("order_id") level LogLevel @default(INFO) message String step String? timestamp DateTime @default(now()) order Order @relation(fields: [orderId], references: [id], onDelete: Cascade) @@index([orderId, timestamp]) @@map("provisioning_logs") } // ============================================================================ // JOB QUEUE // ============================================================================ model ProvisioningJob { id String @id @default(cuid()) orderId String @map("order_id") jobType String @map("job_type") status JobStatus @default(PENDING) priority Int @default(0) claimedAt DateTime? @map("claimed_at") claimedBy String? @map("claimed_by") containerName String? @map("container_name") attempt Int @default(1) maxAttempts Int @default(3) @map("max_attempts") nextRetryAt DateTime? @map("next_retry_at") configSnapshot Json @map("config_snapshot") runnerTokenHash String? @map("runner_token_hash") result Json? error String? createdAt DateTime @default(now()) @map("created_at") updatedAt DateTime @updatedAt @map("updated_at") completedAt DateTime? @map("completed_at") order Order @relation(fields: [orderId], references: [id], onDelete: Cascade) logs JobLog[] @@index([status, priority, createdAt]) @@index([orderId]) @@map("provisioning_jobs") } model JobLog { id String @id @default(cuid()) jobId String @map("job_id") level LogLevel @default(INFO) message String step String? progress Int? timestamp DateTime @default(now()) job ProvisioningJob @relation(fields: [jobId], references: [id], onDelete: Cascade) @@index([jobId, timestamp]) @@map("job_logs") } // ============================================================================ // TOKEN USAGE (AI Tracking) // ============================================================================ model TokenUsage { id String @id @default(cuid()) userId String @map("user_id") instanceId String? @map("instance_id") operation String // chat, analysis, setup tokensInput Int @map("tokens_input") tokensOutput Int @map("tokens_output") model String createdAt DateTime @default(now()) @map("created_at") user User @relation(fields: [userId], references: [id], onDelete: Cascade) @@index([userId, createdAt]) @@map("token_usage") } // ============================================================================ // RUNNER TOKENS // ============================================================================ model RunnerToken { id String @id @default(cuid()) tokenHash String @unique @map("token_hash") name String isActive Boolean @default(true) @map("is_active") lastUsed DateTime? @map("last_used") createdAt DateTime @default(now()) @map("created_at") @@map("runner_tokens") }