505 lines
13 KiB
Plaintext
505 lines
13 KiB
Plaintext
generator client {
|
|
provider = "prisma-client-js"
|
|
}
|
|
|
|
datasource db {
|
|
provider = "postgresql"
|
|
url = env("DATABASE_URL")
|
|
}
|
|
|
|
enum UserStatus {
|
|
ACTIVE
|
|
SUSPENDED
|
|
DELETED
|
|
}
|
|
|
|
enum ServerEdition {
|
|
JAVA
|
|
BEDROCK
|
|
}
|
|
|
|
enum SoftwareFamily {
|
|
VANILLA
|
|
PAPER
|
|
PURPUR
|
|
FABRIC
|
|
FORGE
|
|
NEOFORGE
|
|
QUILT
|
|
BEDROCK_DEDICATED
|
|
}
|
|
|
|
enum GameServerStatus {
|
|
DRAFT
|
|
PROVISIONING
|
|
INSTALLING
|
|
STOPPED
|
|
STARTING
|
|
RUNNING
|
|
STOPPING
|
|
BACKING_UP
|
|
RESTORING
|
|
ERROR
|
|
DELETING
|
|
DELETED
|
|
}
|
|
|
|
enum GameNodeStatus {
|
|
ONLINE
|
|
OFFLINE
|
|
MAINTENANCE
|
|
DRAINING
|
|
UNREACHABLE
|
|
}
|
|
|
|
enum AllocationStatus {
|
|
ACTIVE
|
|
RELEASED
|
|
}
|
|
|
|
enum JobStatus {
|
|
PENDING
|
|
RUNNING
|
|
COMPLETED
|
|
FAILED
|
|
CANCELLED
|
|
}
|
|
|
|
enum BackupStatus {
|
|
PENDING
|
|
RUNNING
|
|
AVAILABLE
|
|
FAILED
|
|
DELETED
|
|
}
|
|
|
|
enum BackupType {
|
|
MANUAL
|
|
SCHEDULED
|
|
PRE_RESTORE
|
|
PRE_WORLD_REPLACE
|
|
}
|
|
|
|
enum BackupRestoreStatus {
|
|
PENDING
|
|
RUNNING
|
|
COMPLETED
|
|
FAILED
|
|
}
|
|
|
|
enum WorldUploadStatus {
|
|
PENDING
|
|
UPLOADING
|
|
VALIDATING
|
|
READY
|
|
COMPLETED
|
|
FAILED
|
|
}
|
|
|
|
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(DRAFT)
|
|
version Int @default(1)
|
|
edition ServerEdition @default(JAVA)
|
|
softwareFamily SoftwareFamily @default(VANILLA)
|
|
minecraftVersion String @default("1.21.1")
|
|
eulaAccepted Boolean @default(false)
|
|
hostPort Int?
|
|
containerId String?
|
|
dataPath String?
|
|
ramMb Int @default(1024)
|
|
activeWorldName String @default("world")
|
|
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[]
|
|
allocation GameServerAllocation?
|
|
backups ServerBackup[]
|
|
backupRestores BackupRestore[]
|
|
backupSchedule BackupSchedule?
|
|
worldUploads WorldUpload[]
|
|
|
|
@@index([userId])
|
|
@@index([nodeId])
|
|
@@index([status])
|
|
@@map("game_servers")
|
|
}
|
|
|
|
model GameServerAllocation {
|
|
id String @id @default(uuid())
|
|
serverId String @unique
|
|
nodeId String
|
|
hostPort Int
|
|
ramMbReserved Int
|
|
status AllocationStatus @default(ACTIVE)
|
|
createdAt DateTime @default(now()) @db.Timestamptz(3)
|
|
updatedAt DateTime @updatedAt @db.Timestamptz(3)
|
|
|
|
server GameServer @relation(fields: [serverId], references: [id], onDelete: Cascade)
|
|
node GameNode @relation(fields: [nodeId], references: [id])
|
|
|
|
@@unique([nodeId, hostPort])
|
|
@@index([nodeId])
|
|
@@map("game_server_allocations")
|
|
}
|
|
|
|
model GameServerStateTransition {
|
|
id String @id @default(uuid())
|
|
gameServerId String
|
|
fromStatus GameServerStatus?
|
|
toStatus GameServerStatus
|
|
reason String?
|
|
correlationId String?
|
|
actorId String?
|
|
errorCode String?
|
|
errorMessage 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(10)
|
|
activeServers Int @default(0)
|
|
lastHeartbeatAt DateTime? @db.Timestamptz(3)
|
|
agentVersion String?
|
|
enrollmentTokenHash String?
|
|
enrolledAt DateTime? @db.Timestamptz(3)
|
|
metadata Json?
|
|
createdAt DateTime @default(now()) @db.Timestamptz(3)
|
|
updatedAt DateTime @updatedAt @db.Timestamptz(3)
|
|
|
|
gameServers GameServer[]
|
|
heartbeats GameNodeHeartbeat[]
|
|
allocations GameServerAllocation[]
|
|
|
|
@@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")
|
|
}
|
|
|
|
model ServerBackup {
|
|
id String @id @default(uuid())
|
|
serverId String
|
|
type BackupType @default(MANUAL)
|
|
status BackupStatus @default(PENDING)
|
|
s3Key String?
|
|
sizeBytes BigInt?
|
|
sha256 String?
|
|
label String?
|
|
failureReason String?
|
|
createdAt DateTime @default(now()) @db.Timestamptz(3)
|
|
completedAt DateTime? @db.Timestamptz(3)
|
|
expiresAt DateTime? @db.Timestamptz(3)
|
|
|
|
server GameServer @relation(fields: [serverId], references: [id], onDelete: Cascade)
|
|
restores BackupRestore[] @relation("RestoredFromBackup")
|
|
safetyForRestores BackupRestore[] @relation("SafetyBackup")
|
|
|
|
@@index([serverId])
|
|
@@index([status])
|
|
@@index([createdAt])
|
|
@@map("server_backups")
|
|
}
|
|
|
|
model BackupRestore {
|
|
id String @id @default(uuid())
|
|
serverId String
|
|
backupId String
|
|
safetyBackupId String?
|
|
status BackupRestoreStatus @default(PENDING)
|
|
failureReason String?
|
|
createdAt DateTime @default(now()) @db.Timestamptz(3)
|
|
completedAt DateTime? @db.Timestamptz(3)
|
|
|
|
server GameServer @relation(fields: [serverId], references: [id], onDelete: Cascade)
|
|
backup ServerBackup @relation("RestoredFromBackup", fields: [backupId], references: [id])
|
|
safetyBackup ServerBackup? @relation("SafetyBackup", fields: [safetyBackupId], references: [id])
|
|
|
|
@@index([serverId])
|
|
@@index([backupId])
|
|
@@index([status])
|
|
@@map("backup_restores")
|
|
}
|
|
|
|
model BackupSchedule {
|
|
id String @id @default(uuid())
|
|
serverId String @unique
|
|
enabled Boolean @default(true)
|
|
intervalHours Int @default(24)
|
|
retentionCount Int @default(5)
|
|
nextRunAt DateTime? @db.Timestamptz(3)
|
|
lastRunAt DateTime? @db.Timestamptz(3)
|
|
createdAt DateTime @default(now()) @db.Timestamptz(3)
|
|
updatedAt DateTime @updatedAt @db.Timestamptz(3)
|
|
|
|
server GameServer @relation(fields: [serverId], references: [id], onDelete: Cascade)
|
|
|
|
@@map("backup_schedules")
|
|
}
|
|
|
|
model WorldUpload {
|
|
id String @id @default(uuid())
|
|
serverId String
|
|
status WorldUploadStatus @default(PENDING)
|
|
s3Key String
|
|
worldName String @default("world")
|
|
sha256 String?
|
|
sizeBytes BigInt?
|
|
failureReason String?
|
|
createdAt DateTime @default(now()) @db.Timestamptz(3)
|
|
completedAt DateTime? @db.Timestamptz(3)
|
|
|
|
server GameServer @relation(fields: [serverId], references: [id], onDelete: Cascade)
|
|
|
|
@@index([serverId])
|
|
@@index([status])
|
|
@@map("world_uploads")
|
|
}
|