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');
|
||||
|
||||
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')
|
||||
?: file_get_contents('/opt/novacpx-src/VERSION')
|
||||
?: '1.0.0';
|
||||
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'] ?? '';
|
||||
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-Credentials: true');
|
||||
header('Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS');
|
||||
|
||||
@@ -25,25 +25,26 @@ class AccountManager {
|
||||
$docRoot = "{$homeDir}/public_html";
|
||||
$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("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 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
|
||||
file_put_contents("{$docRoot}/index.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>"
|
||||
);
|
||||
// Default index page (write as root via sudo tee)
|
||||
$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(
|
||||
"INSERT INTO accounts (user_id, username, domain, home_dir, package_id, php_version, web_server) VALUES (?,?,?,?,?,?,?)",
|
||||
[$userId, $username, $domain, $homeDir, $pkgId ?: null, $phpVer, $webSrv]
|
||||
);
|
||||
|
||||
// Save domain
|
||||
$db->insert(
|
||||
"INSERT INTO domains (account_id, domain, type, document_root) VALUES (?,?,?,?)",
|
||||
[$acctId, $domain, 'main', $docRoot]
|
||||
@@ -61,6 +62,14 @@ class AccountManager {
|
||||
// Create PHP-FPM pool
|
||||
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)");
|
||||
return ['account_id' => $acctId, 'username' => $username, 'domain' => $domain, 'home_dir' => $homeDir];
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user