generator client { provider = "prisma-client-js" } datasource db { provider = "postgresql" url = env("DATABASE_URL") } enum UserStatus { ACTIVE SUSPENDED DELETED } enum GameServerStatus { PENDING PROVISIONING STOPPED STARTING RUNNING STOPPING ERROR DELETED } enum GameNodeStatus { ONLINE OFFLINE MAINTENANCE DRAINING } enum JobStatus { PENDING RUNNING COMPLETED FAILED CANCELLED } model User { id String @id @default(uuid()) email String @unique username String @unique displayName String? status UserStatus @default(ACTIVE) emailVerifiedAt DateTime? @db.Timestamptz(3) failedLoginAttempts Int @default(0) lockedUntil DateTime? @db.Timestamptz(3) createdAt DateTime @default(now()) @db.Timestamptz(3) updatedAt DateTime @updatedAt @db.Timestamptz(3) credentials UserCredential[] sessions UserSession[] platformRoles UserPlatformRole[] gameServers GameServer[] auditEvents AuditEvent[] emailVerificationTokens EmailVerificationToken[] passwordResetTokens PasswordResetToken[] totpCredential TotpCredential? @@map("users") } model UserCredential { id String @id @default(uuid()) userId String passwordHash String createdAt DateTime @default(now()) @db.Timestamptz(3) updatedAt DateTime @updatedAt @db.Timestamptz(3) user User @relation(fields: [userId], references: [id], onDelete: Cascade) @@index([userId]) @@map("user_credentials") } model UserSession { id String @id @default(uuid()) userId String tokenHash String @unique expiresAt DateTime @db.Timestamptz(3) revokedAt DateTime? @db.Timestamptz(3) ipAddress String? userAgent String? createdAt DateTime @default(now()) @db.Timestamptz(3) updatedAt DateTime @updatedAt @db.Timestamptz(3) user User @relation(fields: [userId], references: [id], onDelete: Cascade) @@index([userId]) @@index([expiresAt]) @@map("user_sessions") } model EmailVerificationToken { id String @id @default(uuid()) userId String tokenHash String @unique expiresAt DateTime @db.Timestamptz(3) usedAt DateTime? @db.Timestamptz(3) createdAt DateTime @default(now()) @db.Timestamptz(3) user User @relation(fields: [userId], references: [id], onDelete: Cascade) @@index([userId]) @@index([expiresAt]) @@map("email_verification_tokens") } model PasswordResetToken { id String @id @default(uuid()) userId String tokenHash String @unique expiresAt DateTime @db.Timestamptz(3) usedAt DateTime? @db.Timestamptz(3) createdAt DateTime @default(now()) @db.Timestamptz(3) user User @relation(fields: [userId], references: [id], onDelete: Cascade) @@index([userId]) @@index([expiresAt]) @@map("password_reset_tokens") } model TotpCredential { id String @id @default(uuid()) userId String @unique secret String enabledAt DateTime? @db.Timestamptz(3) createdAt DateTime @default(now()) @db.Timestamptz(3) updatedAt DateTime @updatedAt @db.Timestamptz(3) user User @relation(fields: [userId], references: [id], onDelete: Cascade) recoveryCodes RecoveryCode[] @@map("totp_credentials") } model RecoveryCode { id String @id @default(uuid()) totpCredentialId String codeHash String usedAt DateTime? @db.Timestamptz(3) createdAt DateTime @default(now()) @db.Timestamptz(3) totpCredential TotpCredential @relation(fields: [totpCredentialId], references: [id], onDelete: Cascade) @@index([totpCredentialId]) @@map("recovery_codes") } model PlatformRole { id String @id @default(uuid()) name String @unique description String? createdAt DateTime @default(now()) @db.Timestamptz(3) updatedAt DateTime @updatedAt @db.Timestamptz(3) userRoles UserPlatformRole[] @@map("platform_roles") } model UserPlatformRole { id String @id @default(uuid()) userId String roleId String createdAt DateTime @default(now()) @db.Timestamptz(3) updatedAt DateTime @updatedAt @db.Timestamptz(3) user User @relation(fields: [userId], references: [id], onDelete: Cascade) role PlatformRole @relation(fields: [roleId], references: [id], onDelete: Cascade) @@unique([userId, roleId]) @@index([userId]) @@index([roleId]) @@map("user_platform_roles") } model GameServer { id String @id @default(uuid()) userId String planId String? nodeId String? name String status GameServerStatus @default(PENDING) version Int @default(1) createdAt DateTime @default(now()) @db.Timestamptz(3) updatedAt DateTime @updatedAt @db.Timestamptz(3) user User @relation(fields: [userId], references: [id]) plan Plan? @relation(fields: [planId], references: [id]) node GameNode? @relation(fields: [nodeId], references: [id]) stateTransitions GameServerStateTransition[] @@index([userId]) @@index([nodeId]) @@index([status]) @@map("game_servers") } model GameServerStateTransition { id String @id @default(uuid()) gameServerId String fromStatus GameServerStatus? toStatus GameServerStatus reason String? metadata Json? createdAt DateTime @default(now()) @db.Timestamptz(3) gameServer GameServer @relation(fields: [gameServerId], references: [id], onDelete: Cascade) @@index([gameServerId]) @@index([createdAt]) @@map("game_server_state_transitions") } model GameNode { id String @id @default(uuid()) name String @unique hostname String status GameNodeStatus @default(OFFLINE) maxServers Int @default(0) activeServers Int @default(0) metadata Json? createdAt DateTime @default(now()) @db.Timestamptz(3) updatedAt DateTime @updatedAt @db.Timestamptz(3) gameServers GameServer[] heartbeats GameNodeHeartbeat[] @@map("game_nodes") } model GameNodeHeartbeat { id String @id @default(uuid()) nodeId String payload Json? createdAt DateTime @default(now()) @db.Timestamptz(3) node GameNode @relation(fields: [nodeId], references: [id], onDelete: Cascade) @@index([nodeId]) @@index([createdAt]) @@map("game_node_heartbeats") } model Plan { id String @id @default(uuid()) name String @unique slug String @unique description String? isFree Boolean @default(false) isActive Boolean @default(true) maxRamMb Int maxCpuCores Float maxStorageMb Int metadata Json? createdAt DateTime @default(now()) @db.Timestamptz(3) updatedAt DateTime @updatedAt @db.Timestamptz(3) gameServers GameServer[] @@map("plans") } model FeatureFlag { id String @id @default(uuid()) key String @unique description String? enabled Boolean @default(false) metadata Json? createdAt DateTime @default(now()) @db.Timestamptz(3) updatedAt DateTime @updatedAt @db.Timestamptz(3) @@map("feature_flags") } model SystemSetting { id String @id @default(uuid()) key String @unique value Json createdAt DateTime @default(now()) @db.Timestamptz(3) updatedAt DateTime @updatedAt @db.Timestamptz(3) @@map("system_settings") } model AuditEvent { id String @id @default(uuid()) userId String? action String entityType String? entityId String? metadata Json? ipAddress String? createdAt DateTime @default(now()) @db.Timestamptz(3) user User? @relation(fields: [userId], references: [id], onDelete: SetNull) @@index([userId]) @@index([action]) @@index([entityType, entityId]) @@index([createdAt]) @@map("audit_events") } model JobRecord { id String @id @default(uuid()) queue String jobName String status JobStatus @default(PENDING) payload Json? result Json? error String? attempts Int @default(0) startedAt DateTime? @db.Timestamptz(3) completedAt DateTime? @db.Timestamptz(3) createdAt DateTime @default(now()) @db.Timestamptz(3) updatedAt DateTime @updatedAt @db.Timestamptz(3) @@index([queue, status]) @@index([createdAt]) @@map("job_records") }