Files
novacpx/db/schema.sql
T
myron e802443d4a feat: NovaCPX v1.0.0 initial scaffold
Full hosting control panel with 3 tiers: Admin, Reseller, User.
- install.sh: unattended installer for Ubuntu 20/22/24 + Debian 11/12
- PHP multi-version (7.4/8.1/8.2/8.3), Apache2/nginx choice, MySQL, PostgreSQL
- BIND9 DNS, Postfix+Dovecot mail, ProFTPD, Certbot SSL, UFW, Fail2Ban
- 18-table DB schema with audit log and version tracking
- PHP REST API (auth, system/updates, server stats, service control)
- Admin panel: dark dashboard, service manager, git-based update system
- User panel: usage rings + feature card grid (distinct from cPanel)
- VERSION file: git-tracked; Admin > Updates panel shows/applies git commits

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-07 05:05:30 +00:00

373 lines
18 KiB
SQL

-- NovaCPX Database Schema v1.0.0
-- Engine: MySQL 8+ | Charset: utf8mb4_unicode_ci
SET NAMES utf8mb4;
SET foreign_key_checks = 0;
-- ── Version tracking ──────────────────────────────────────────────────────────
CREATE TABLE IF NOT EXISTS novacpx_version (
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
version VARCHAR(20) NOT NULL,
installed_at DATETIME DEFAULT CURRENT_TIMESTAMP,
notes TEXT,
git_commit VARCHAR(64),
INDEX idx_version (version)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
INSERT INTO novacpx_version (version, notes, git_commit)
VALUES ('1.0.0', 'Initial installation', 'HEAD');
-- ── Audit log (every action tracked) ─────────────────────────────────────────
CREATE TABLE IF NOT EXISTS audit_log (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
user_id INT UNSIGNED,
username VARCHAR(100),
action VARCHAR(100) NOT NULL,
resource VARCHAR(200),
detail JSON,
ip_address VARCHAR(45),
user_agent TEXT,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
INDEX idx_user (user_id),
INDEX idx_action (action),
INDEX idx_created (created_at)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- ── Users (admin, resellers, end-users) ───────────────────────────────────────
CREATE TABLE IF NOT EXISTS users (
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(100) NOT NULL UNIQUE,
password VARCHAR(255) NOT NULL,
email VARCHAR(255) NOT NULL UNIQUE,
role ENUM('admin','reseller','user') DEFAULT 'user',
status ENUM('active','suspended','pending') DEFAULT 'active',
reseller_id INT UNSIGNED DEFAULT NULL,
package_id INT UNSIGNED DEFAULT NULL,
theme VARCHAR(50) DEFAULT 'nova-dark',
language VARCHAR(10) DEFAULT 'en',
contact_name VARCHAR(200),
contact_phone VARCHAR(50),
last_login DATETIME,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME ON UPDATE CURRENT_TIMESTAMP,
FOREIGN KEY (reseller_id) REFERENCES users(id) ON DELETE SET NULL,
INDEX idx_role (role),
INDEX idx_status (status),
INDEX idx_reseller (reseller_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- ── Sessions ──────────────────────────────────────────────────────────────────
CREATE TABLE IF NOT EXISTS sessions (
id VARCHAR(128) PRIMARY KEY,
user_id INT UNSIGNED NOT NULL,
ip_address VARCHAR(45),
user_agent TEXT,
data JSON,
expires_at DATETIME NOT NULL,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
INDEX idx_expires (expires_at)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- ── Packages / Hosting Plans ──────────────────────────────────────────────────
CREATE TABLE IF NOT EXISTS packages (
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(100) NOT NULL,
owner_id INT UNSIGNED DEFAULT NULL, -- NULL = system, or reseller
disk_mb INT UNSIGNED DEFAULT 1024,
bandwidth_mb BIGINT UNSIGNED DEFAULT 10240,
max_domains SMALLINT UNSIGNED DEFAULT 1,
max_subdomains SMALLINT UNSIGNED DEFAULT 10,
max_addon_domains SMALLINT UNSIGNED DEFAULT 0,
max_parked_domains SMALLINT UNSIGNED DEFAULT 5,
max_email SMALLINT UNSIGNED DEFAULT 10,
max_ftp SMALLINT UNSIGNED DEFAULT 5,
max_databases SMALLINT UNSIGNED DEFAULT 5,
php_version VARCHAR(10) DEFAULT '8.3',
ssl_enabled TINYINT(1) DEFAULT 1,
is_default TINYINT(1) DEFAULT 0,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (owner_id) REFERENCES users(id) ON DELETE SET NULL,
INDEX idx_owner (owner_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
INSERT INTO packages (name, disk_mb, bandwidth_mb, max_domains, max_email, max_databases, is_default)
VALUES ('Default', 5120, 51200, 5, 25, 10, 1);
-- ── Hosting Accounts ──────────────────────────────────────────────────────────
CREATE TABLE IF NOT EXISTS accounts (
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
user_id INT UNSIGNED NOT NULL UNIQUE,
username VARCHAR(32) NOT NULL UNIQUE,
domain VARCHAR(253) NOT NULL,
home_dir VARCHAR(500) NOT NULL,
package_id INT UNSIGNED,
disk_used_mb INT UNSIGNED DEFAULT 0,
bw_used_mb BIGINT UNSIGNED DEFAULT 0,
php_version VARCHAR(10) DEFAULT '8.3',
web_server ENUM('apache','nginx') DEFAULT 'apache',
status ENUM('active','suspended','terminated') DEFAULT 'active',
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
suspended_at DATETIME DEFAULT NULL,
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
FOREIGN KEY (package_id) REFERENCES packages(id) ON DELETE SET NULL,
INDEX idx_domain (domain),
INDEX idx_status (status)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- ── Domains ───────────────────────────────────────────────────────────────────
CREATE TABLE IF NOT EXISTS domains (
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
account_id INT UNSIGNED NOT NULL,
domain VARCHAR(253) NOT NULL,
type ENUM('main','addon','subdomain','parked','alias') DEFAULT 'main',
document_root VARCHAR(500),
php_version VARCHAR(10),
ssl_enabled TINYINT(1) DEFAULT 0,
ssl_cert TEXT,
ssl_key TEXT,
ssl_chain TEXT,
ssl_expires DATE,
redirect_to VARCHAR(500) DEFAULT NULL,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (account_id) REFERENCES accounts(id) ON DELETE CASCADE,
UNIQUE KEY uq_domain (domain),
INDEX idx_account (account_id),
INDEX idx_type (type)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- ── DNS Zones & Records ───────────────────────────────────────────────────────
CREATE TABLE IF NOT EXISTS dns_zones (
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
account_id INT UNSIGNED NOT NULL,
domain VARCHAR(253) NOT NULL UNIQUE,
serial BIGINT UNSIGNED DEFAULT 1,
primary_ns VARCHAR(253) DEFAULT 'ns1.localhost',
secondary_ns VARCHAR(253) DEFAULT 'ns2.localhost',
admin_email VARCHAR(255) DEFAULT 'hostmaster@localhost',
ttl INT UNSIGNED DEFAULT 3600,
refresh INT UNSIGNED DEFAULT 86400,
retry INT UNSIGNED DEFAULT 7200,
expire INT UNSIGNED DEFAULT 2419200,
minimum INT UNSIGNED DEFAULT 86400,
status ENUM('active','disabled') DEFAULT 'active',
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME ON UPDATE CURRENT_TIMESTAMP,
FOREIGN KEY (account_id) REFERENCES accounts(id) ON DELETE CASCADE,
INDEX idx_account (account_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS dns_records (
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
zone_id INT UNSIGNED NOT NULL,
name VARCHAR(253) NOT NULL,
type ENUM('A','AAAA','CNAME','MX','TXT','SRV','NS','PTR','CAA','DKIM','SPF','DMARC') NOT NULL,
content TEXT NOT NULL,
ttl INT UNSIGNED DEFAULT 3600,
priority SMALLINT UNSIGNED DEFAULT NULL,
proxied TINYINT(1) DEFAULT 0,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (zone_id) REFERENCES dns_zones(id) ON DELETE CASCADE,
INDEX idx_zone (zone_id),
INDEX idx_type (type),
INDEX idx_name (name(100))
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- ── Email Accounts & Forwarders ───────────────────────────────────────────────
CREATE TABLE IF NOT EXISTS email_accounts (
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
account_id INT UNSIGNED NOT NULL,
email VARCHAR(320) NOT NULL UNIQUE,
password VARCHAR(255) NOT NULL,
quota_mb INT UNSIGNED DEFAULT 500,
used_mb INT UNSIGNED DEFAULT 0,
status ENUM('active','suspended') DEFAULT 'active',
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (account_id) REFERENCES accounts(id) ON DELETE CASCADE,
INDEX idx_account (account_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS email_forwarders (
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
account_id INT UNSIGNED NOT NULL,
source VARCHAR(320) NOT NULL,
destination VARCHAR(320) NOT NULL,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (account_id) REFERENCES accounts(id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE IF NOT EXISTS email_autoresponders (
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
account_id INT UNSIGNED NOT NULL,
email VARCHAR(320) NOT NULL,
subject VARCHAR(255),
body TEXT,
is_active TINYINT(1) DEFAULT 1,
start_at DATETIME,
stop_at DATETIME,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (account_id) REFERENCES accounts(id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- ── Databases ─────────────────────────────────────────────────────────────────
CREATE TABLE IF NOT EXISTS databases (
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
account_id INT UNSIGNED NOT NULL,
db_name VARCHAR(100) NOT NULL,
db_user VARCHAR(100) NOT NULL,
db_pass VARCHAR(255) NOT NULL,
db_type ENUM('mysql','postgresql') DEFAULT 'mysql',
size_mb FLOAT DEFAULT 0,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (account_id) REFERENCES accounts(id) ON DELETE CASCADE,
INDEX idx_account (account_id),
INDEX idx_type (db_type)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- ── FTP Accounts ─────────────────────────────────────────────────────────────
CREATE TABLE IF NOT EXISTS ftp_accounts (
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
account_id INT UNSIGNED NOT NULL,
username VARCHAR(100) NOT NULL UNIQUE,
password VARCHAR(255) NOT NULL,
home_dir VARCHAR(500) NOT NULL,
quota_mb INT UNSIGNED DEFAULT 0,
status ENUM('active','suspended') DEFAULT 'active',
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (account_id) REFERENCES accounts(id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- ── SSL Certificates ──────────────────────────────────────────────────────────
CREATE TABLE IF NOT EXISTS ssl_certs (
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
account_id INT UNSIGNED NOT NULL,
domain VARCHAR(253) NOT NULL,
type ENUM('lets_encrypt','self_signed','custom') DEFAULT 'lets_encrypt',
cert TEXT,
private_key TEXT,
chain TEXT,
issued_at DATETIME,
expires_at DATETIME,
auto_renew TINYINT(1) DEFAULT 1,
status ENUM('active','expired','pending','failed') DEFAULT 'pending',
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (account_id) REFERENCES accounts(id) ON DELETE CASCADE,
INDEX idx_domain (domain),
INDEX idx_expires (expires_at)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- ── Cron Jobs ─────────────────────────────────────────────────────────────────
CREATE TABLE IF NOT EXISTS cron_jobs (
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
account_id INT UNSIGNED NOT NULL,
command TEXT NOT NULL,
minute VARCHAR(20) DEFAULT '*',
hour VARCHAR(20) DEFAULT '*',
day VARCHAR(20) DEFAULT '*',
month VARCHAR(20) DEFAULT '*',
weekday VARCHAR(20) DEFAULT '*',
is_active TINYINT(1) DEFAULT 1,
last_run DATETIME,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (account_id) REFERENCES accounts(id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- ── PHP Configuration ─────────────────────────────────────────────────────────
CREATE TABLE IF NOT EXISTS php_configs (
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
account_id INT UNSIGNED NOT NULL UNIQUE,
php_version VARCHAR(10) DEFAULT '8.3',
memory_limit VARCHAR(20) DEFAULT '256M',
max_execution_time INT DEFAULT 30,
upload_max_filesize VARCHAR(20) DEFAULT '64M',
post_max_size VARCHAR(20) DEFAULT '64M',
max_input_vars INT DEFAULT 1000,
display_errors TINYINT(1) DEFAULT 0,
error_reporting VARCHAR(50) DEFAULT 'E_ALL & ~E_NOTICE',
extensions JSON,
updated_at DATETIME ON UPDATE CURRENT_TIMESTAMP,
FOREIGN KEY (account_id) REFERENCES accounts(id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- ── Backups ───────────────────────────────────────────────────────────────────
CREATE TABLE IF NOT EXISTS backups (
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
account_id INT UNSIGNED NOT NULL,
filename VARCHAR(500) NOT NULL,
size_mb FLOAT DEFAULT 0,
type ENUM('full','partial','db_only','files_only') DEFAULT 'full',
status ENUM('pending','running','complete','failed') DEFAULT 'pending',
storage ENUM('local','s3','ftp','sftp') DEFAULT 'local',
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
completed_at DATETIME,
FOREIGN KEY (account_id) REFERENCES accounts(id) ON DELETE CASCADE,
INDEX idx_account (account_id),
INDEX idx_status (status)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- ── Server Stats / Monitoring ─────────────────────────────────────────────────
CREATE TABLE IF NOT EXISTS server_stats (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
cpu_pct FLOAT,
ram_pct FLOAT,
disk_pct FLOAT,
load_1m FLOAT,
load_5m FLOAT,
load_15m FLOAT,
net_in_kb BIGINT UNSIGNED DEFAULT 0,
net_out_kb BIGINT UNSIGNED DEFAULT 0,
recorded_at DATETIME DEFAULT CURRENT_TIMESTAMP,
INDEX idx_recorded (recorded_at)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- ── Notifications ─────────────────────────────────────────────────────────────
CREATE TABLE IF NOT EXISTS notifications (
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
user_id INT UNSIGNED,
title VARCHAR(255) NOT NULL,
message TEXT NOT NULL,
type ENUM('info','success','warning','error') DEFAULT 'info',
is_read TINYINT(1) DEFAULT 0,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
INDEX idx_user (user_id),
INDEX idx_unread (is_read)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- ── API Tokens ────────────────────────────────────────────────────────────────
CREATE TABLE IF NOT EXISTS api_tokens (
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
user_id INT UNSIGNED NOT NULL,
name VARCHAR(100) NOT NULL,
token VARCHAR(128) NOT NULL UNIQUE,
permissions JSON,
last_used DATETIME,
expires_at DATETIME,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
INDEX idx_token (token)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- ── Settings ──────────────────────────────────────────────────────────────────
CREATE TABLE IF NOT EXISTS settings (
`key` VARCHAR(100) PRIMARY KEY,
`value` TEXT,
updated_at DATETIME ON UPDATE CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
INSERT INTO settings (`key`, `value`) VALUES
('panel_name', 'NovaCPX'),
('panel_version', '1.0.0'),
('default_nameserver1', 'ns1.localhost'),
('default_nameserver2', 'ns2.localhost'),
('default_php', '8.3'),
('mail_enabled', '1'),
('ftp_enabled', '1'),
('dns_enabled', '1'),
('backup_dir', '/var/novacpx/backups'),
('update_channel', 'stable'),
('git_remote', 'https://github.com/myronblair/novacpx.git')
ON DUPLICATE KEY UPDATE `value` = VALUES(`value`);
SET foreign_key_checks = 1;