mirror of
https://github.com/myronblair/novacpx
synced 2026-06-30 17:50:41 -05:00
fix: global exception handler (prevents 502), transaction rollback on account create, CORS for reverse proxy
- set_exception_handler in api/index.php prevents uncaught exceptions from crashing PHP-FPM - AccountManager::create() wrapped in DB transaction with rollback + Linux user cleanup on failure - CORS origin regex updated to allow requests from port 443 (NPM reverse proxy) - index.html written via sudo tee instead of file_put_contents (www-data permission fix) - chpasswd now called with sudo prefix Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LP9Q4kfCAYAjJnsbHBrViZ
This commit is contained in:
+10
-2
@@ -9,14 +9,22 @@ define('NOVACPX_API', __DIR__);
|
|||||||
define('NOVACPX_LIB', NOVACPX_ROOT . '/lib');
|
define('NOVACPX_LIB', NOVACPX_ROOT . '/lib');
|
||||||
|
|
||||||
header('Content-Type: application/json');
|
header('Content-Type: application/json');
|
||||||
|
|
||||||
|
// Global exception handler — prevents uncaught exceptions from crashing PHP-FPM (502)
|
||||||
|
set_exception_handler(function (Throwable $e) {
|
||||||
|
http_response_code(500);
|
||||||
|
echo json_encode(['success' => false, 'message' => $e->getMessage(), 'errors' => []]);
|
||||||
|
exit;
|
||||||
|
});
|
||||||
|
|
||||||
$_ver = file_get_contents(NOVACPX_ROOT . '/VERSION')
|
$_ver = file_get_contents(NOVACPX_ROOT . '/VERSION')
|
||||||
?: file_get_contents('/opt/novacpx-src/VERSION')
|
?: file_get_contents('/opt/novacpx-src/VERSION')
|
||||||
?: '1.0.0';
|
?: '1.0.0';
|
||||||
header('X-NovaCPX-Version: ' . trim($_ver));
|
header('X-NovaCPX-Version: ' . trim($_ver));
|
||||||
|
|
||||||
// CORS for same-origin panel requests (ports 8880/8881/8882/8883)
|
// CORS for same-origin panel requests (ports 8880/8881/8882/8883 and HTTPS via reverse proxy on 443)
|
||||||
$origin = $_SERVER['HTTP_ORIGIN'] ?? '';
|
$origin = $_SERVER['HTTP_ORIGIN'] ?? '';
|
||||||
if (preg_match('#^https?://[^/]+:(888[0-3])$#', $origin)) {
|
if (preg_match('#^https?://[^/]+(:(888[0-3]))?$#', $origin)) {
|
||||||
header("Access-Control-Allow-Origin: $origin");
|
header("Access-Control-Allow-Origin: $origin");
|
||||||
header('Access-Control-Allow-Credentials: true');
|
header('Access-Control-Allow-Credentials: true');
|
||||||
header('Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS');
|
header('Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS');
|
||||||
|
|||||||
@@ -25,25 +25,26 @@ class AccountManager {
|
|||||||
$docRoot = "{$homeDir}/public_html";
|
$docRoot = "{$homeDir}/public_html";
|
||||||
$password = $data['password'] ?? bin2hex(random_bytes(8));
|
$password = $data['password'] ?? bin2hex(random_bytes(8));
|
||||||
|
|
||||||
// Create Linux user
|
// Create Linux user and home directory first
|
||||||
self::shell("useradd -m -d {$homeDir} -s /sbin/nologin -G www-data " . escapeshellarg($username));
|
self::shell("useradd -m -d {$homeDir} -s /sbin/nologin -G www-data " . escapeshellarg($username));
|
||||||
self::shell("echo " . escapeshellarg("{$username}:{$password}") . " | chpasswd");
|
self::shell("echo " . escapeshellarg("{$username}:{$password}") . " | sudo chpasswd");
|
||||||
self::shell("sudo mkdir -p {$docRoot} {$homeDir}/logs {$homeDir}/tmp");
|
self::shell("sudo mkdir -p {$docRoot} {$homeDir}/logs {$homeDir}/tmp");
|
||||||
self::shell("sudo chown -R {$username}:www-data {$homeDir}");
|
self::shell("sudo chown -R {$username}:www-data {$homeDir}");
|
||||||
self::shell("sudo chmod 750 {$homeDir}"); self::shell("sudo chmod 775 {$docRoot}");
|
self::shell("sudo chmod 750 {$homeDir}");
|
||||||
|
self::shell("sudo chmod 775 {$docRoot}");
|
||||||
|
|
||||||
// Default index page
|
// Default index page (write as root via sudo tee)
|
||||||
file_put_contents("{$docRoot}/index.html",
|
$html = "<html><body style='font-family:sans-serif;text-align:center;padding:4rem'><h1>Welcome to {$domain}</h1><p>Hosted by NovaCPX</p></body></html>";
|
||||||
"<html><body style='font-family:sans-serif;text-align:center;padding:4rem'><h1>Welcome to {$domain}</h1><p>Hosted by NovaCPX</p></body></html>"
|
self::shell("sudo tee " . escapeshellarg("{$docRoot}/index.html") . " > /dev/null << 'HTMLEOF'\n{$html}\nHTMLEOF");
|
||||||
);
|
|
||||||
|
|
||||||
// Save account to DB
|
// Wrap all DB writes in a transaction so partial failures leave no orphans
|
||||||
|
$db->beginTransaction();
|
||||||
|
try {
|
||||||
$acctId = (int)$db->insert(
|
$acctId = (int)$db->insert(
|
||||||
"INSERT INTO accounts (user_id, username, domain, home_dir, package_id, php_version, web_server) VALUES (?,?,?,?,?,?,?)",
|
"INSERT INTO accounts (user_id, username, domain, home_dir, package_id, php_version, web_server) VALUES (?,?,?,?,?,?,?)",
|
||||||
[$userId, $username, $domain, $homeDir, $pkgId ?: null, $phpVer, $webSrv]
|
[$userId, $username, $domain, $homeDir, $pkgId ?: null, $phpVer, $webSrv]
|
||||||
);
|
);
|
||||||
|
|
||||||
// Save domain
|
|
||||||
$db->insert(
|
$db->insert(
|
||||||
"INSERT INTO domains (account_id, domain, type, document_root) VALUES (?,?,?,?)",
|
"INSERT INTO domains (account_id, domain, type, document_root) VALUES (?,?,?,?)",
|
||||||
[$acctId, $domain, 'main', $docRoot]
|
[$acctId, $domain, 'main', $docRoot]
|
||||||
@@ -61,6 +62,14 @@ class AccountManager {
|
|||||||
// Create PHP-FPM pool
|
// Create PHP-FPM pool
|
||||||
PHPManager::createPool($username, $phpVer);
|
PHPManager::createPool($username, $phpVer);
|
||||||
|
|
||||||
|
$db->commit();
|
||||||
|
} catch (Throwable $e) {
|
||||||
|
$db->rollBack();
|
||||||
|
// Clean up Linux user if DB failed
|
||||||
|
self::shell("userdel -r " . escapeshellarg($username) . " 2>/dev/null || true");
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
|
|
||||||
novacpx_log('info', "Account created: $username ($domain)");
|
novacpx_log('info', "Account created: $username ($domain)");
|
||||||
return ['account_id' => $acctId, 'username' => $username, 'domain' => $domain, 'home_dir' => $homeDir];
|
return ['account_id' => $acctId, 'username' => $username, 'domain' => $domain, 'home_dir' => $homeDir];
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user