219 Commits

Author SHA1 Message Date
github-actions[bot] e87e1a14b8 chore: bump version to 1.0.22 [skip ci] 2026-06-10 12:32:17 +00:00
myron 2fa1f10901 Security: fix 8 code-review findings
- 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>
2026-06-10 12:32:06 +00:00
github-actions[bot] 5037633f5f chore: bump version to 1.0.21 [skip ci] 2026-06-10 05:53:34 +00:00
myron 658e2f9057 Fix PHP-FPM pool not removed on account termination
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>
2026-06-10 05:53:25 +00:00
github-actions[bot] 7107d230c8 chore: bump version to 1.0.20 [skip ci] 2026-06-10 05:51:17 +00:00
myron e6550f0a90 Add full service sudoers rules to installer
Previous installer only granted www-data access to nginx/apache2/fail2ban.
Added NOPASSWD rules for all panel-managed services:
postfix, dovecot, rspamd, proftpd, vsftpd, pure-ftpd,
named/bind9/pdns/nsd, mysql, mariadb, php*-fpm.
Without these, service restart/stop/start buttons returned 502
(shell_exec hung waiting for sudo password → Apache timeout).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-10 05:51:08 +00:00
github-actions[bot] b0ac6e7aa2 chore: bump version to 1.0.19 [skip ci] 2026-06-10 05:45:42 +00:00
myron b9c37030b6 Fix terminate 500: require DatabaseManager before calling drop
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>
2026-06-10 05:45:33 +00:00
github-actions[bot] 892034d3a7 chore: bump version to 1.0.18 [skip ci] Nova1014 2026-06-10 03:17:50 +00:00
myron 57949214de Fix WordPress manager 500: define DB_HOST, lazy-load MySQL provDb
- 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>
2026-06-10 03:17:39 +00:00
github-actions[bot] 8f32c973cb chore: bump version to 1.0.17 [skip ci] 2026-06-10 03:06:23 +00:00
myron 4d7c35076b Fix 10 code review findings: security, correctness, and SQLite compat
- system.php: fix null dereference on fetchOne (TypeError on null['value'])
- system.php: validate update_channel to ['stable','beta'] to prevent shell injection
- system.php: escapeshellarg remoteBranch in git log/show calls (was RCE vector)
- system.php: fix backup path — rsync contents, not directory, so restore is symmetric
- system.php: syntax check only changed files (git diff) not all 300+ panel files
- system.php: copy VERSION to $webRoot/VERSION not $webRoot/../VERSION (wrong path)
- system.php: fix 3× ON DUPLICATE KEY UPDATE → SQLite ON CONFLICT syntax
- deploy-runner.sh: hoist DB_PATH/CHANNEL above while loop
- deploy-runner.sh: sanitize NEW_VERSION and commit hashes before SQL interpolation
- deploy-runner.sh: parse queued branch (4th field) from webhook queue entry
- webhook.php: remove dead $branch config variable
- webhook.php: include pushed branch in queue entry to eliminate TOCTOU race

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-10 03:06:14 +00:00
github-actions[bot] 64c9569104 chore: bump version to 1.0.16 [skip ci] 2026-06-09 23:10:31 +00:00
myron 7367fe658c Update documentation with all current features and services
- README: full feature matrix (hosting, DNS, email, databases, files, SSL, security, Docker,
  monitoring, update channels/versioning, reseller branding, settings, API)
- Admin guide: update channels section with stable/beta table; Settings section now documents
  all DB-backed fields including update channel
- User guide: email domain dropdown note

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-09 23:10:19 +00:00
github-actions[bot] 95cc4914cd chore: bump version to 1.0.15 [skip ci] 2026-06-09 22:55:54 +00:00
myron 14aa6e8b4d Fix column name: commit_hash → git_commit in novacpx_version INSERT
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-09 22:55:43 +00:00
github-actions[bot] 3b47fc27e7 chore: bump version to 1.0.14 [skip ci] 2026-06-09 22:44:58 +00:00
myron 9cabe8af5e Wire update channel (stable/beta) into settings, check, deploy, and version tracking
- 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>
2026-06-09 22:44:46 +00:00
github-actions[bot] d53eb309eb chore: bump version to 1.0.13 [skip ci] 2026-06-09 22:31:02 +00:00
myron 846683c7a2 Add SEO meta description, keywords, and noindex to all three panel pages
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-09 22:30:53 +00:00
github-actions[bot] 9dd75bfff8 chore: bump version to 1.0.12 [skip ci] 2026-06-09 22:25:00 +00:00
myron f9d423b15a Fix OS upgrade script: date format and backup dir permission
- date -u +%H:%M:%S UTC → ts() helper with date -u +"%H:%M:%S UTC"
  (UTC as a separate word was being treated as an extra date argument)
- Backup dir changed from /var/novacpx/backups/ (root-owned, doesn't exist)
  to /tmp/novacpx-backup-TIMESTAMP/ (always writable by www-data)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-09 22:24:50 +00:00
github-actions[bot] 2f141973c1 chore: bump version to 1.0.11 [skip ci] 2026-06-09 22:23:55 +00:00
myron 09bd0820a5 Updates page: serve cached results instantly, nightly cron refreshes cache
- check-novacpx-update and check-os-update return cached data (12h TTL)
  immediately instead of running slow git fetch / apt-get update on page load
- Cache stored in settings table (update_cache_novacpx, update_cache_os)
- Updates page shows "Cached · last checked X ago" when serving cache
- "Refresh now" button forces a live re-check and updates cache
- bin/cache-update-check.php: standalone cron script that warms cache nightly
- Cron registered at 2am daily on panel server

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-09 22:23:45 +00:00
github-actions[bot] 0c32c8a018 chore: bump version to 1.0.10 [skip ci] 2026-06-09 22:18:49 +00:00
myron 877f157665 Docker page: show compose stacks in My Apps tab instead of raw containers
launchFromCatalog creates compose stacks, not docker_containers entries.
Replace My Containers tab with My Apps tab backed by docker/stacks endpoint.
Add Refresh, Start/Stop, Logs, Remove actions per stack row.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-09 22:18:39 +00:00
github-actions[bot] cdb92c5ef2 chore: bump version to 1.0.9 [skip ci] 2026-06-09 22:15:52 +00:00
myron 1ac9728fd7 Fix Docker async launch, email SUBSTRING_INDEX (SQLite), postfix sudo writes
- Docker app launch now runs docker compose up -d in background (nohup &)
  so the API returns immediately instead of timing out during image pulls
- EmailManager syncPostfix: replace MySQL SUBSTRING_INDEX with SQLite SUBSTR/INSTR
- EmailManager syncPostfix: write postfix files via sudo tee (www-data permission fix)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-09 22:15:41 +00:00
github-actions[bot] b90ef41677 chore: bump version to 1.0.8 [skip ci] 2026-06-09 22:13:39 +00:00
myron 24a2434ccd Email create modal: split input into local-part + domain dropdown
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-09 22:13:29 +00:00
github-actions[bot] 071f32ce76 chore: bump version to 1.0.7 [skip ci] 2026-06-09 22:10:06 +00:00
myron dc8829e2c9 Fix DockerManager stack directory creation using sudo mkdir for www-data
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-09 22:09:55 +00:00
github-actions[bot] 24e0ce3633 chore: bump version to 1.0.6 [skip ci] 2026-06-09 22:04:29 +00:00
myron 89996cc0ea Fix _branding.php session lookup using correct column name (id not token)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-09 22:04:18 +00:00
github-actions[bot] 978ea6082c chore: bump version to 1.0.5 [skip ci] 2026-06-09 18:58:00 +00:00
myron b90f753890 Add version badge to reseller and user panel sidebars
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-09 18:57:48 +00:00
github-actions[bot] 8a11458220 chore: bump version to 1.0.4 [skip ci] 2026-06-09 18:42:41 +00:00
myron ddd81d73e7 Fix MySQL create: sanitize db names, fix empty db_user default, catch RuntimeException
Dots/dashes in names were failing validateName; now stripped to underscores.
Empty db_user field sent as "" (not null) so ?? fallback never fired; fixed
to check for empty string explicitly.  Wrap createMySQL/Postgres in try/catch
so validation errors return 400 JSON instead of 500.  Also pass db_type from
JS (was being sent as db_type not type).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-09 18:42:24 +00:00
github-actions[bot] 8716299201 chore: bump version to 1.0.3 [skip ci] 2026-06-09 18:32:26 +00:00
myron c22e1fd067 Fix multiple user panel 500 errors
- domains: VhostManager::create() called with array instead of 4 params
- PHPManager: VhostManager not required; pool writes use sudo tee (permission);
  updateConfig creates pool if missing instead of throwing
- DatabaseManager: MySQL ops used SQLite panel PDO; add dedicated mysqlPdo()
  using MariaDB socket auth
- BackupManager: column name is size_mb not size; diskUsage returns float
- DB.php: add LAST_INSERT_ID() → last_insert_rowid() translation
- user.js: SSL issue/submit used Nova.api (JSON) but endpoint streams SSE;
  add _sslStream() helper matching admin panel behavior
- schema/migration: add enc_password column to email_accounts

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-09 18:32:10 +00:00
github-actions[bot] 563386b8b8 chore: bump version to 1.0.2 [skip ci] 2026-06-09 18:22:22 +00:00
myron 8179326526 Fix NOVACPX_ROOT undefined on panel pages (black screen)
Constants were only defined in api/index.php, so direct requests to
admin/reseller/user index.php got a fatal error before rendering any HTML.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-09 18:22:05 +00:00
myron 601155aa6b Add GitHub Actions workflow for automatic version bumping
Increments PATCH on push to main (1.0.1 → 1.0.2), appends -beta.N on
beta branch pushes.  Uses [skip ci] to prevent infinite loop.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-09 18:15:30 +00:00
myron 5aaeaa9a35 Fix deploy-runner: SQLite migration tracking, re-create webhook symlink after rsync
Switches migration tracking from MySQL to SQLite (panel.db), reads DB path
from config.ini with fallback to /var/lib/novacpx/panel.db.  Re-creates the
webhook symlink after each rsync deploy so it survives --delete.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-09 18:09:02 +00:00
myron 1c2c11251c Streaming terminals for PHP extensions, SSL certificates; UFW logging state fix
- php.php: install-extension and remove-extension now stream via SSE (real-time progress, proper error detection, sudo)
- ssl.php: issue action now streams certbot output via SSE
- admin.js: phpExtInstall/Remove use streaming terminal modal
- admin.js: adminIssueBulkSSL uses streaming modal with per-domain progress
- admin.js: adminRenewCert now confirms before renewing
- admin.js: adminIssueSingleSSL helper for per-domain streaming SSL
- admin.js: firewall page pre-selects current UFW logging level from API response
- admin.js: fwSetLogging reloads firewall page on success
- firewall.php: ufw_status() now parses and returns logging level

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-09 18:04:43 +00:00
myron 99cdc6f3dc chore: exclude .github/workflows from PAT push [skip ci] 2026-06-09 18:00:03 +00:00
myron 4d016b4156 Add notification email templates: DB migration, API CRUD, admin UI
- db/migrations/009_email_templates.sql: email_templates table with 8 default templates
- db/schema.sql: email_templates table added
- system.php: email-templates/get/save/delete/test actions with placeholder rendering
- admin.js: notifications page enhanced with template list, edit modal, CRUD, send test
- Templates support placeholders: {{name}}, {{domain}}, {{username}}, {{password}}, etc.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-09 17:59:54 +00:00
myron b295f8ca8e chore: remove workflow from push (PAT lacks workflow scope) [skip ci] 2026-06-09 16:24:07 +00:00
myron 2af9e34fb0 Add service versions panel, version auto-tracking, Fail2Ban sidebar, streaming service switch
- .github/workflows/version-bump.yml: auto-increment patch version on push to main/beta
- admin/index.php: show version under logo from VERSION file
- system.php: service-versions endpoint (catalog of 22 services with version/description/status)
- admin.js: updates page shows Installed Services table with current/latest/status/description
- admin.js: loadServiceVersions() lazy-loaded after page render via setTimeout
- admin/index.php: separate Fail2Ban sidebar entry (was merged into Firewall label)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-09 16:23:51 +00:00
myron 7aa33defa2 Fix SQLite backtick translation, add service-switch SSE streaming, Fail2Ban management page
- DB.php: fix backtick-quoted column names in ON DUPLICATE KEY UPDATE VALUES() regex
- DB.php: add global backtick→double-quote identifier strip
- system.php: add service-switch SSE streaming endpoint for web/mail/ftp/dns server changes
- system.php: simplify save-option to DB save only (no inline shell)
- firewall.php: add f2b-config-get, f2b-config-save, f2b-log, f2b-jail, f2b-ban, f2b-unban, f2b-ignoreip-* actions
- admin.js: Fail2Ban dedicated management page with jail table, global settings, whitelist, log viewer
- admin.js: soSave() now uses streaming terminal overlay instead of blocking spinner
- admin/index.php: split Firewall (UFW) and Fail2Ban into separate sidebar entries

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-09 16:18:28 +00:00