Service stack (backend): - webserver.ts rewritten: apache2/php-fpm routes replacing nginx; per-account Apache log endpoint; PHP-FPM reload per version; apache2ctl configtest/reload - accounts.ts: added PUT /:id/shell to toggle /bin/bash vs /usr/sbin/nologin, runs usermod -s and writes audit log New UI features (nixserver): - ShellAccess page: grant/revoke SSH access per account via AccountPicker - ApacheLogs page: live log viewer (access/error) per account; replaces stub - NginxLogs: re-exports ApacheLogs (NixPanel uses Apache, not NGINX) - WebServer page: shows apache2, php8.2-fpm, php8.3-fpm status with reload buttons; config test; Apache log viewer Bug fixes: - Accounts.tsx: Lucide icon size prop type widened (string|number); removed unused Users import; dropped duplicate NGINX log card - Removed soon badge from ShellAccess and ApacheLogs entries Project: - CHANGELOG.md created with full history from 0.2.x through 0.4.0 - All packages bumped from 0.3.2 to 0.4.0
430 lines
14 KiB
Plaintext
430 lines
14 KiB
Plaintext
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")
|
|
}
|