generator client { provider = "prisma-client-js" } datasource db { provider = "sqlite" url = env("DATABASE_URL") } // ─── Admin / WHM Users ─────────────────────────────────────────────────────── model AdminUser { id Int @id @default(autoincrement()) username String @unique passwordHash String email String @unique twoFactorSecret String? twoFactorEnabled Boolean @default(false) lastLogin DateTime? lastLoginIp String? 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 passwordHash String email String @unique contactName String? company String? maxAccounts Int @default(0) diskLimitMb BigInt @default(0) bandwidthLimitMb BigInt @default(0) diskUsedMb BigInt @default(0) bandwidthUsedMb BigInt @default(0) status String @default("active") brandName String? brandLogo String? notes String? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt accounts Account[] refreshTokens RefreshToken[] auditLogs AuditLog[] @@map("resellers") } // ─── Hosting Packages ──────────────────────────────────────────────────────── model Package { id Int @id @default(autoincrement()) name String @unique 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") sslEnabled Boolean @default(true) backupEnabled Boolean @default(true) dedicatedIp Boolean @default(false) notes String? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt accounts Account[] @@map("packages") } // ─── Hosting Accounts ──────────────────────────────────────────────────────── model Account { id Int @id @default(autoincrement()) username String @unique passwordHash String email String domain String @unique homedir String shell String @default("/usr/sbin/nologin") status String @default("active") diskUsedMb BigInt @default(0) bandwidthUsedMb BigInt @default(0) ipAddress String? nameservers String? notes String? suspendedAt DateTime? suspendReason String? 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 netmask String? gateway String? type String @default("shared") status String @default("available") notes String? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt accounts Account[] @@map("ip_addresses") } // ─── DNS ────────────────────────────────────────────────────────────────────── model DnsZone { id Int @id @default(autoincrement()) domain String @unique zoneFile String? serial BigInt @default(1) status String @default("active") 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 name String value String 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 domain String address String @unique passwordHash String quotaMb Int @default(500) usedMb Int @default(0) status String @default("active") 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 destination String 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 passwordHash String homedir String quotaMb Int @default(0) status String @default("active") 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 charset String @default("utf8mb4") collation String @default("utf8mb4_unicode_ci") 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 passwordHash String host String @default("localhost") privileges String @default("ALL") 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 domain String fqdn String @unique documentRoot String 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 subdomain String documentRoot String 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 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 type String @default("letsencrypt") certPath String? keyPath String? chainPath String? issuedAt DateTime? expiresAt DateTime? autoRenew Boolean @default(true) status String @default("pending") 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 command String comment String? 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 severity String @default("info") message String sourceIp String? resolved Boolean @default(false) resolvedAt DateTime? createdAt DateTime @default(now()) @@map("security_events") } model BlockedIp { id Int @id @default(autoincrement()) ip String @unique reason String? expiresAt DateTime? createdAt DateTime @default(now()) @@map("blocked_ips") } model WhitelistedIp { id Int @id @default(autoincrement()) ip String @unique note String? createdAt DateTime @default(now()) @@map("whitelisted_ips") } // ─── Audit Log ──────────────────────────────────────────────────────────────── model AuditLog { id Int @id @default(autoincrement()) action String target String? details String? sourceIp String? createdAt DateTime @default(now()) adminId Int? admin AdminUser? @relation(fields: [adminId], references: [id]) resellerId Int? reseller Reseller? @relation(fields: [resellerId], references: [id]) @@map("audit_log") } // ─── Auth ───────────────────────────────────────────────────────────────────── model RefreshToken { id Int @id @default(autoincrement()) token String @unique role String 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 value String updatedAt DateTime @updatedAt @@map("settings") } // ─── Nav Modules ───────────────────────────────────────────────────────────── model NavModule { id Int @id @default(autoincrement()) name String @unique label String sectionId String sectionLabel String sectionIcon String @default("Settings") sectionTo String? sectionWeight Int @default(100) weight Int @default(100) route String enabled Boolean @default(true) builtIn Boolean @default(true) createdAt DateTime @default(now()) @@map("nav_modules") }