generator client { provider = "prisma-client-js" } datasource db { provider = "postgresql" url = env("DATABASE_URL") } model User { id String @id @default(uuid()) email String @unique passwordHash String? @map("password_hash") firstName String? @map("first_name") lastName String? @map("last_name") company String? phone String? role UserRole @default(USER) mfaSecret String? @map("mfa_secret") emailVerified Boolean @default(false) @map("email_verified") failedLoginAttempts Int @default(0) @map("failed_login_attempts") lockedUntil DateTime? @map("locked_until") lastLoginAt DateTime? @map("last_login_at") createdAt DateTime @default(now()) @map("created_at") updatedAt DateTime @updatedAt @map("updated_at") auditLogs AuditLog[] idMapping IdMapping? idempotencyKeys IdempotencyKey[] invoicesMirror InvoiceMirror[] subscriptionsMirror SubscriptionMirror[] @@map("users") } model IdMapping { userId String @id @map("user_id") whmcsClientId Int @unique @map("whmcs_client_id") sfAccountId String? @map("sf_account_id") createdAt DateTime @default(now()) @map("created_at") updatedAt DateTime @updatedAt @map("updated_at") user User @relation(fields: [userId], references: [id], onDelete: Cascade) @@map("id_mappings") } model InvoiceMirror { invoiceId Int @id @map("invoice_id") userId String @map("user_id") number String status String amountCents Int @map("amount_cents") currency String @db.Char(3) dueDate DateTime? @map("due_date") @db.Date issuedAt DateTime? @map("issued_at") paidAt DateTime? @map("paid_at") updatedAt DateTime @updatedAt @map("updated_at") user User @relation(fields: [userId], references: [id], onDelete: Cascade) @@index([userId, status]) @@index([userId, dueDate]) @@map("invoices_mirror") } model SubscriptionMirror { serviceId Int @id @map("service_id") userId String @map("user_id") productName String @map("product_name") domain String? cycle String status String nextDue DateTime? @map("next_due") amountCents Int @map("amount_cents") currency String @db.Char(3) registeredAt DateTime @map("registered_at") updatedAt DateTime @updatedAt @map("updated_at") user User @relation(fields: [userId], references: [id], onDelete: Cascade) @@index([userId, status]) @@index([userId, nextDue]) @@map("subscriptions_mirror") } model IdempotencyKey { key String @id userId String @map("user_id") createdAt DateTime @default(now()) @map("created_at") user User @relation(fields: [userId], references: [id], onDelete: Cascade) @@index([userId]) @@index([createdAt]) @@map("idempotency_keys") } model Job { id String @id @default(uuid()) name String data Json status JobStatus @default(PENDING) attempts Int @default(0) maxRetries Int @default(3) @map("max_retries") createdAt DateTime @default(now()) @map("created_at") updatedAt DateTime @updatedAt @map("updated_at") processedAt DateTime? @map("processed_at") failedAt DateTime? @map("failed_at") error String? @@index([status, createdAt]) @@map("jobs") } model AuditLog { id String @id @default(uuid()) userId String? @map("user_id") action AuditAction resource String? details Json? ipAddress String? @map("ip_address") userAgent String? @map("user_agent") success Boolean @default(true) error String? createdAt DateTime @default(now()) @map("created_at") user User? @relation(fields: [userId], references: [id]) @@index([userId, action]) @@index([action, createdAt]) @@index([createdAt]) @@map("audit_logs") } enum UserRole { USER ADMIN } enum JobStatus { PENDING PROCESSING COMPLETED FAILED RETRYING } enum AuditAction { LOGIN_SUCCESS LOGIN_FAILED LOGOUT SIGNUP PASSWORD_RESET PASSWORD_CHANGE ACCOUNT_LOCKED ACCOUNT_UNLOCKED PROFILE_UPDATE MFA_ENABLED MFA_DISABLED API_ACCESS SYSTEM_MAINTENANCE } // Per-SIM daily usage snapshot used to build full-month charts model SimUsageDaily { id Int @id @default(autoincrement()) account String date DateTime @db.Date usageMb Float createdAt DateTime @default(now()) @map("created_at") updatedAt DateTime @updatedAt @map("updated_at") @@unique([account, date]) @@index([account, date]) @@map("sim_usage_daily") }