- accounts.php: remove outer beginTransaction() — AccountManager already wraps in its own transaction; nested transactions fail in SQLite with 'already an active transaction'
- accounts.php: on AccountManager failure, manually delete the inserted user row instead
- admin/reseller/user index.php: fix favicon href from /assets/img/favicon.svg to nova-favicon.svg
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LP9Q4kfCAYAjJnsbHBrViZ
- admin.js: 1292 lines of features were on server but not in repo — recovered and committed
- admin.js: impersonation redirect now uses location.origin instead of hardcoded :8880 port
- accounts.php: pre-validate email uniqueness and username before INSERT to prevent SQLSTATE 23000
- accounts.php: wrap user INSERT + AccountManager::create() in single transaction for full rollback
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LP9Q4kfCAYAjJnsbHBrViZ
When HTTP_HOST has no port (NPM on 443), return URL without appending panel port.
Direct access (HTTP_HOST includes :8882 etc.) still redirects to correct port.
Prevents browser being sent to :8882 directly after login via novacpx.orbishosting.com.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LP9Q4kfCAYAjJnsbHBrViZ
- 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
- useradd/userdel/usermod/chpasswd for hosting account management
- mkdir/chown/chmod for home directory provisioning
- nginx sites-available and sites-enabled write permissions
- certbot, opendkim-genkey, rndc, named-checkzone for SSL and DKIM
- chown root:www-data on nginx vhost dirs so VhostManager can write configs directly
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LP9Q4kfCAYAjJnsbHBrViZ
- admin.js was calling auth/register (action does not exist) — changed
to users/create
- Reseller list was fetching from accounts/list which is for hosting
accounts; fixed to users/list?role=reseller
- Replaced shared adminSuspend/adminChangePass (account-scoped) with
dedicated adminResellerSuspend/Unsuspend/Passwd/Delete functions that
operate on the users table
- Added users endpoint actions: create, suspend, unsuspend,
change-password, delete — all admin-only, operating on user rows
rather than hosting account rows
- Reseller delete disowns their accounts (sets reseller_id=NULL) rather
than cascading delete
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
deploy-runner.sh was rsyncing panel/public/ but VERSION lives at repo
root — web root /srv/novacpx/public/VERSION was perpetually stale.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- API: stack-action/stack-remove now verify ownership for non-admin users
- API: add stack-reinstall action (pull latest images → down → up)
- User panel: add Reinstall button per stack; fix bug where remove-stack was called instead of stack-remove
- Admin panel: add Reinstall button per stack + dockerStackReinstall() handler
- User panel: Remove All My Apps now only removes the calling user's own containers/stacks
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Admin Docker page: add App Catalog tab (60 apps, account-picker modal)
- Admin Docker page: add dockerAdminLaunchApp() for launching apps on behalf of any account
- User panel: add 'Remove All My Apps' button — stops/removes only that user's own containers and stacks
- API: add uninstall-account action (user-scoped; admin can specify account_id, users limited to own account)
- Admin panel: no global Docker uninstall (would affect all users on the server)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- install.sh: replace /usr/sbin/ufw * with scoped subcommands
- install.sh: remove /usr/bin/curl * and /usr/bin/env * NOPASSWD (trivial root escalation)
- PHPManager: switchVersion() uses sudo rm -f instead of unlink() for old pool
- PHPManager: updateConfig() SQLite syntax (ON CONFLICT / datetime('now'))
- WordPressManager: cloneStaging() escapeshellarg() on all shell-interpolated paths
- WordPressManager: delete() removes DB record before filesystem to avoid phantom records
- WordPressManager: ensureWpCli() validates download size and enforces 30s timeout
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Two bugs that together left stale pool files behind after termination,
crashing php-fpm on next startup (exit-code 78, user not found):
1. removePool() used file_exists() to guard the rm — fails silently when
www-data can't read /etc/php/*/fpm/pool.d/; now always attempts sudo rm -f
2. reloadFPM() called systemctl without sudo — silently failed as www-data,
leaving the old pool loaded even when the file was successfully removed
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
AccountManager::terminate() called DatabaseManager::drop() without
requiring the class first — fatal class not found error on every
account termination.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Core.php: add DB_HOST constant (was undefined, causing fatal error on any
WordPress manager page load in PHP 8)
- WordPressManager: make provDb lazy (only connects to MySQL when actually
needed for install/clone/delete — not on list/info which only use SQLite)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Settings page now loads current values from DB and saves via save-option API
- check-novacpx-update reads update_channel setting, checks origin/main or origin/beta
- apply-novacpx-update pulls from channel branch, fixes backup dir (/tmp), fixes SQLite migration syntax, records new version in novacpx_version table + settings.panel_version
- deploy-runner.sh reads update_channel from DB, pulls correct branch, records version after deploy
- webhook.php accepts pushes to both main and beta branches
- Updates page shows channel badge and latest remote version
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>