Add full API endpoint suite, lib managers, webmail (Roundcube :8883), and NovaCPX icon/branding assets

- 14 API endpoints: accounts, packages, domains, dns, email, databases, ftp, ssl, cron, php, files, stats, webmail, server_setup
- 8 lib managers: AccountManager, VhostManager, DNSManager, EmailManager, DatabaseManager, PHPManager, FTPManager, SSLManager
- Roundcube webmail on dedicated port 8883 (sequenced after 8880/8881/8882)
- Custom NovaCPX SVG icon sprite (30+ unique icons), logo, mark, favicon
- PORT_WEBMAIL=8883 wired into Core.php, install.sh, UFW, Fail2Ban, credentials file

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-07 05:50:50 +00:00
parent 716d292e77
commit e3b166803a
28 changed files with 2576 additions and 1 deletions
+110
View File
@@ -0,0 +1,110 @@
<?php
/**
* Server Setup endpoint — hostname, contact info, nameservers, branding
*/
$db = DB::getInstance();
$body = json_decode(file_get_contents('php://input'), true) ?? [];
Auth::getInstance()->requireRole(['admin']);
function getSetting(string $key, $db): ?string {
return $db->fetchOne("SELECT value FROM settings WHERE `key` = ?", [$key])['value'] ?? null;
}
function setSetting(string $key, string $value, $db): void {
$db->execute(
"INSERT INTO settings (`key`, value, updated_at) VALUES (?,?,NOW()) ON DUPLICATE KEY UPDATE value=?, updated_at=NOW()",
[$key, $value, $value]
);
}
match ($action) {
'get' => (function() use ($db) {
$keys = ['hostname','contact_name','contact_email','contact_phone','company_name',
'nameserver1','nameserver2','server_ip','ipv6_enabled','panel_theme',
'panel_title','smtp_host','smtp_port','smtp_user','admin_email',
'two_fa_required','registration_enabled','max_accounts','timezone'];
$data = [];
foreach ($keys as $k) {
$data[$k] = getSetting($k, $db) ?? '';
}
// Live hostname
$data['system_hostname'] = trim(shell_exec('hostname -f') ?: '');
Response::success($data);
})(),
'save' => (function() use ($body, $db) {
$allowed = ['contact_name','contact_email','contact_phone','company_name',
'nameserver1','nameserver2','panel_theme','panel_title',
'smtp_host','smtp_port','smtp_user','smtp_pass','admin_email',
'two_fa_required','registration_enabled','max_accounts','timezone'];
foreach ($allowed as $k) {
if (isset($body[$k])) setSetting($k, (string)$body[$k], $db);
}
audit('server_setup.save', 'settings updated');
Response::success(null, 'Settings saved');
})(),
'set-hostname' => (function() use ($body, $db) {
$host = trim($body['hostname'] ?? '');
if (!$host || !preg_match('/^[a-z0-9][a-z0-9\-\.]+$/', $host)) Response::error("Invalid hostname");
shell_exec("hostnamectl set-hostname " . escapeshellarg($host));
// Update /etc/hosts
$hosts = file_get_contents('/etc/hosts');
$ip = trim(shell_exec("hostname -I | awk '{print $1}'") ?: '127.0.0.1');
$hosts = preg_replace('/^127\.0\.1\.1.*/m', "127.0.1.1\t$host", $hosts);
if (!str_contains($hosts, '127.0.1.1')) $hosts .= "\n127.0.1.1\t$host\n";
file_put_contents('/etc/hosts', $hosts);
setSetting('hostname', $host, $db);
audit('server_setup.hostname', $host);
Response::success(['hostname' => $host], 'Hostname updated');
})(),
'nameservers' => (function() use ($body, $db) {
$ns1 = trim($body['ns1'] ?? '');
$ns2 = trim($body['ns2'] ?? '');
if (!$ns1) Response::error("ns1 required");
setSetting('nameserver1', $ns1, $db);
if ($ns2) setSetting('nameserver2', $ns2, $db);
// Update BIND options
$named = file_get_contents('/etc/bind/named.conf.options') ?: '';
if (str_contains($named, 'forwarders')) {
$named = preg_replace('/forwarders\s*\{[^}]*\}/', "forwarders { 8.8.8.8; 1.1.1.1; }", $named);
file_put_contents('/etc/bind/named.conf.options', $named);
shell_exec('systemctl reload bind9 2>/dev/null || systemctl reload named 2>/dev/null');
}
audit('server_setup.nameservers', "$ns1 / $ns2");
Response::success(null, 'Nameservers updated');
})(),
'smtp-test' => (function() use ($body, $db) {
$to = $body['to'] ?? getSetting('admin_email', $db);
if (!$to) Response::error("email address required");
$host = getSetting('smtp_host', $db) ?: 'localhost';
$port = (int)(getSetting('smtp_port', $db) ?: 587);
$user = getSetting('smtp_user', $db) ?: '';
$pass = getSetting('smtp_pass', $db) ?: '';
// Simple test using PHP mail() for localhost
$sent = mail($to, 'NovaCPX SMTP Test', 'SMTP is working correctly from NovaCPX.');
Response::success(['sent' => $sent], $sent ? 'Test email sent' : 'mail() returned false — check SMTP config');
})(),
'system-info' => (function() {
$os = trim(shell_exec("lsb_release -d 2>/dev/null | cut -d: -f2 | xargs") ?: php_uname('s'));
$kernel = trim(shell_exec("uname -r") ?: '');
$uptime = trim(shell_exec("uptime -p") ?: '');
$cpu = trim(shell_exec("grep 'model name' /proc/cpuinfo | head -1 | cut -d: -f2 | xargs") ?: '');
$cpuCores = (int)trim(shell_exec("nproc") ?: 1);
$memTotal = (int)trim(shell_exec("grep MemTotal /proc/meminfo | awk '{print $2}'") ?: 0);
Response::success([
'os' => $os,
'kernel' => $kernel,
'uptime' => $uptime,
'cpu' => $cpu,
'cores' => $cpuCores,
'ram_gb' => round($memTotal / 1048576, 1),
'hostname' => trim(shell_exec('hostname -f') ?: ''),
'ip' => trim(shell_exec("hostname -I | awk '{print $1}'") ?: ''),
]);
})(),
default => Response::error("Unknown server_setup action: $action", 404),
};