generator client { provider = "prisma-client-js" } datasource db { provider = "mysql" url = env("DATABASE_URL") } // ─── Admin / WHM Users ─────────────────────────────────────────────────────── model AdminUser { id Int @id @default(autoincrement()) username String @unique @db.VarChar(64) passwordHash String @db.VarChar(255) email String @unique @db.VarChar(255) twoFactorSecret String? @db.VarChar(64) twoFactorEnabled Boolean @default(false) lastLogin DateTime? lastLoginIp String? @db.VarChar(45) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt refreshTokens RefreshToken[] auditLogs AuditLog[] @@map("admin_users") } // ─── Resellers ─────────────────────────────────────────────────────────────── model Reseller { id Int @id @default(autoincrement()) username String @unique @db.VarChar(64) passwordHash String @db.VarChar(255) email String @unique @db.VarChar(255) contactName String? @db.VarChar(255) company String? @db.VarChar(255) maxAccounts Int @default(0) diskLimitMb BigInt @default(0) bandwidthLimitMb BigInt @default(0) diskUsedMb BigInt @default(0) bandwidthUsedMb BigInt @default(0) status String @default("active") @db.VarChar(20) brandName String? @db.VarChar(255) brandLogo String? @db.VarChar(500) notes String? @db.Text createdAt DateTime @default(now()) updatedAt DateTime @updatedAt accounts Account[] refreshTokens RefreshToken[] @@map("resellers") } // ─── Hosting Packages ──────────────────────────────────────────────────────── model Package { id Int @id @default(autoincrement()) name String @unique @db.VarChar(100) diskMb BigInt @default(1024) bandwidthMb BigInt @default(10240) maxDatabases Int @default(5) maxEmailAccounts Int @default(10) maxSubdomains Int @default(10) maxAddonDomains Int @default(5) maxParkedDomains Int @default(5) maxFtpAccounts Int @default(3) maxCronJobs Int @default(5) phpVersion String @default("8.3") @db.VarChar(10) sslEnabled Boolean @default(true) backupEnabled Boolean @default(true) dedicatedIp Boolean @default(false) notes String? @db.Text createdAt DateTime @default(now()) updatedAt DateTime @updatedAt accounts Account[] @@map("packages") } // ─── Hosting Accounts (cPanel users) ───────────────────────────────────────── model Account { id Int @id @default(autoincrement()) username String @unique @db.VarChar(64) passwordHash String @db.VarChar(255) email String @db.VarChar(255) domain String @unique @db.VarChar(255) homedir String @db.VarChar(500) shell String @default("/bin/false") @db.VarChar(100) status String @default("active") @db.VarChar(20) diskUsedMb BigInt @default(0) bandwidthUsedMb BigInt @default(0) ipAddress String? @db.VarChar(45) nameservers String? @db.VarChar(500) notes String? @db.Text suspendedAt DateTime? suspendReason String? @db.VarChar(500) terminatedAt DateTime? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt resellerId Int? reseller Reseller? @relation(fields: [resellerId], references: [id]) packageId Int? package Package? @relation(fields: [packageId], references: [id]) ipId Int? ip IpAddress? @relation(fields: [ipId], references: [id]) dnsZones DnsZone[] emailAccounts EmailAccount[] emailForwarders EmailForwarder[] ftpAccounts FtpAccount[] databases Database[] subdomains Subdomain[] addonDomains AddonDomain[] parkedDomains ParkedDomain[] sslCertificates SslCertificate[] cronJobs CronJob[] refreshTokens RefreshToken[] @@map("accounts") } // ─── IP Addresses ───────────────────────────────────────────────────────────── model IpAddress { id Int @id @default(autoincrement()) address String @unique @db.VarChar(45) netmask String? @db.VarChar(45) gateway String? @db.VarChar(45) type String @default("shared") @db.VarChar(20) status String @default("available") @db.VarChar(20) notes String? @db.Text createdAt DateTime @default(now()) updatedAt DateTime @updatedAt accounts Account[] @@map("ip_addresses") } // ─── DNS ────────────────────────────────────────────────────────────────────── model DnsZone { id Int @id @default(autoincrement()) domain String @unique @db.VarChar(255) zoneFile String? @db.VarChar(500) serial BigInt @default(1) status String @default("active") @db.VarChar(20) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt accountId Int account Account @relation(fields: [accountId], references: [id], onDelete: Cascade) records DnsRecord[] @@map("dns_zones") } model DnsRecord { id Int @id @default(autoincrement()) type String @db.VarChar(10) name String @db.VarChar(255) value String @db.VarChar(500) ttl Int @default(3600) priority Int? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt zoneId Int zone DnsZone @relation(fields: [zoneId], references: [id], onDelete: Cascade) @@map("dns_records") } // ─── Email ──────────────────────────────────────────────────────────────────── model EmailAccount { id Int @id @default(autoincrement()) username String @db.VarChar(64) domain String @db.VarChar(255) address String @unique @db.VarChar(320) passwordHash String @db.VarChar(255) quotaMb Int @default(500) usedMb Int @default(0) status String @default("active") @db.VarChar(20) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt accountId Int account Account @relation(fields: [accountId], references: [id], onDelete: Cascade) @@map("email_accounts") } model EmailForwarder { id Int @id @default(autoincrement()) source String @db.VarChar(320) destination String @db.VarChar(320) createdAt DateTime @default(now()) accountId Int account Account @relation(fields: [accountId], references: [id], onDelete: Cascade) @@map("email_forwarders") } // ─── FTP ────────────────────────────────────────────────────────────────────── model FtpAccount { id Int @id @default(autoincrement()) username String @db.VarChar(64) passwordHash String @db.VarChar(255) homedir String @db.VarChar(500) quotaMb Int @default(0) status String @default("active") @db.VarChar(20) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt accountId Int account Account @relation(fields: [accountId], references: [id], onDelete: Cascade) @@map("ftp_accounts") } // ─── Databases ──────────────────────────────────────────────────────────────── model Database { id Int @id @default(autoincrement()) name String @unique @db.VarChar(64) charset String @default("utf8mb4") @db.VarChar(32) collation String @default("utf8mb4_unicode_ci") @db.VarChar(64) sizeMb Float @default(0) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt accountId Int account Account @relation(fields: [accountId], references: [id], onDelete: Cascade) dbUsers DatabaseUser[] @@map("databases") } model DatabaseUser { id Int @id @default(autoincrement()) username String @db.VarChar(64) passwordHash String @db.VarChar(255) host String @default("localhost") @db.VarChar(255) privileges String @default("ALL") @db.VarChar(255) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt databaseId Int database Database @relation(fields: [databaseId], references: [id], onDelete: Cascade) @@map("database_users") } // ─── Subdomains / Addon / Parked Domains ───────────────────────────────────── model Subdomain { id Int @id @default(autoincrement()) subdomain String @db.VarChar(255) domain String @db.VarChar(255) fqdn String @unique @db.VarChar(510) documentRoot String @db.VarChar(500) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt accountId Int account Account @relation(fields: [accountId], references: [id], onDelete: Cascade) @@map("subdomains") } model AddonDomain { id Int @id @default(autoincrement()) domain String @unique @db.VarChar(255) subdomain String @db.VarChar(255) documentRoot String @db.VarChar(500) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt accountId Int account Account @relation(fields: [accountId], references: [id], onDelete: Cascade) @@map("addon_domains") } model ParkedDomain { id Int @id @default(autoincrement()) domain String @unique @db.VarChar(255) createdAt DateTime @default(now()) accountId Int account Account @relation(fields: [accountId], references: [id], onDelete: Cascade) @@map("parked_domains") } // ─── SSL ────────────────────────────────────────────────────────────────────── model SslCertificate { id Int @id @default(autoincrement()) domain String @db.VarChar(255) type String @default("letsencrypt") @db.VarChar(30) certPath String? @db.VarChar(500) keyPath String? @db.VarChar(500) chainPath String? @db.VarChar(500) issuedAt DateTime? expiresAt DateTime? autoRenew Boolean @default(true) status String @default("pending") @db.VarChar(20) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt accountId Int account Account @relation(fields: [accountId], references: [id], onDelete: Cascade) @@map("ssl_certificates") } // ─── Cron Jobs ──────────────────────────────────────────────────────────────── model CronJob { id Int @id @default(autoincrement()) schedule String @db.VarChar(100) command String @db.VarChar(1000) comment String? @db.VarChar(255) enabled Boolean @default(true) lastRun DateTime? nextRun DateTime? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt accountId Int account Account @relation(fields: [accountId], references: [id], onDelete: Cascade) @@map("cron_jobs") } // ─── Security ───────────────────────────────────────────────────────────────── model SecurityEvent { id Int @id @default(autoincrement()) type String @db.VarChar(50) severity String @default("info") @db.VarChar(20) message String @db.Text sourceIp String? @db.VarChar(45) resolved Boolean @default(false) resolvedAt DateTime? createdAt DateTime @default(now()) @@map("security_events") } model BlockedIp { id Int @id @default(autoincrement()) ip String @unique @db.VarChar(45) reason String? @db.VarChar(500) expiresAt DateTime? createdAt DateTime @default(now()) @@map("blocked_ips") } model WhitelistedIp { id Int @id @default(autoincrement()) ip String @unique @db.VarChar(45) note String? @db.VarChar(255) createdAt DateTime @default(now()) @@map("whitelisted_ips") } // ─── Audit Log ──────────────────────────────────────────────────────────────── model AuditLog { id Int @id @default(autoincrement()) action String @db.VarChar(100) target String? @db.VarChar(255) details String? @db.Text sourceIp String? @db.VarChar(45) createdAt DateTime @default(now()) adminId Int? admin AdminUser? @relation(fields: [adminId], references: [id]) @@map("audit_log") } // ─── Auth ───────────────────────────────────────────────────────────────────── model RefreshToken { id Int @id @default(autoincrement()) token String @unique @db.VarChar(500) role String @db.VarChar(20) expiresAt DateTime revokedAt DateTime? createdAt DateTime @default(now()) adminId Int? admin AdminUser? @relation(fields: [adminId], references: [id]) resellerId Int? reseller Reseller? @relation(fields: [resellerId], references: [id]) accountId Int? account Account? @relation(fields: [accountId], references: [id]) @@map("refresh_tokens") } // ─── Settings ───────────────────────────────────────────────────────────────── model Setting { key String @id @db.VarChar(100) value String @db.Text updatedAt DateTime @updatedAt @@map("settings") } // ─── Terminal Sessions ──────────────────────────────────────────────────────── model TerminalSession { id Int @id @default(autoincrement()) adminId Int sourceIp String? @db.VarChar(45) startedAt DateTime @default(now()) endedAt DateTime? @@map("terminal_sessions") }