Files
nixpanel-server/server/prisma/schema.prisma
T
NixReaper 8ff00c544c feat: v0.4.0 — Apache service stack, shell access, log viewer, changelog
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
2026-04-11 05:17:49 -05:00

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")
}