219 Commits

Author SHA1 Message Date
myron bcd3b65520 Run panel on dedicated novacpx-web Nginx service; fix auth/transaction methods
- deploy/nginx-panel.conf: standalone Nginx config for ports 8880-8883
- deploy/novacpx-web.service: systemd unit, survives apache2/nginx stop
- server_setup.php: fix Auth::requireRole() -> require('admin')
- DB.php: add beginTransaction()/commit()/rollback() methods
2026-06-09 16:00:32 +00:00
myron c01a03645d Fix server_stats column names to match PHP code (cpu_usage/ram_usage/disk_usage/load_avg) 2026-06-09 15:00:10 +00:00
myron 1ebd146eb5 Fix sessions table: add impersonator_id column 2026-06-09 14:56:33 +00:00
myron fbc445dad2 Migrate panel DB from MySQL to SQLite
Panel no longer depends on the user-managed MariaDB service.
SQLite at /var/lib/novacpx/panel.db runs independently so the
control panel stays up even when MariaDB is stopped.

- DB.php: switch to sqlite: DSN, add SQL translator (ON DUPLICATE KEY,
  DATE_ADD/DATE_SUB INTERVAL, NOW(), UNIX_TIMESTAMP(), IFNULL)
- Core.php: replace DB_HOST/NAME/USER/PASS with DB_PATH constant
- schema.sql: full SQLite syntax, add TOTP columns to users table
- _branding.php: use sqlite: PDO, datetime('now') for session check
- install.sh: apt install sqlite3, create SQLite DB instead of MySQL DB
- tools/migrate-to-sqlite.sh: one-shot migration script for existing installs
2026-06-09 14:52:02 +00:00
myron 9bd78a81ea Fix uninstall not resetting to setup screen
- proxy.php: always set proxy_mode=disabled and clear remote_host/backend_ip
  on any uninstall, not just when nginx binary is removed
- admin.js: show setup cards when mode==='disabled' regardless of whether
  nginx binary still exists on the remote VM
- Status card shows 'Disabled' instead of 'Stopped' when mode is disabled

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-09 13:16:50 +00:00
myron 667f3b3a3c Fix auto-updater running git against web root instead of source repo
NOVACPX_ROOT (/srv/novacpx/public) is a deployed file copy, not a git
repo — hence 'fatal: not a git repository'. The actual git clone lives
at /opt/novacpx-src (installed by the installer).

check-update and apply-update now use /opt/novacpx-src for all git
operations. apply-update also deploys the pulled files back to the web
root with cp -a (public/, api/, lib/, bin/) and re-sets ownership.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-09 13:12:20 +00:00
myron 7e89ab6709 Fix proxy modals never saving — all were passing callbacks as footerHtml
Nova.modal(title, body, footerHtml) expects an HTML string for the third
parameter. proxyAddHost, proxyEditHost, proxySwitchLocal, and proxyUninstall
were all passing async functions instead, which got stringified as garbage
text with no actual buttons rendered.

Each modal now gets proper Cancel + action button footer HTML, with the
save/action logic wired via addEventListener after modal creation.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-09 13:09:26 +00:00
myron a900c5d490 Fix setup-remote connection lost — EventSource can't do POST requests
proxyRunSetup() used EventSource which only sends GET. The setup-remote
endpoint requires POST, so the request hit the 404 default and the SSE
connection immediately errored with 'Connection lost'.

Replace EventSource with fetch+ReadableStream (same pattern already used
by proxySwitchLocal). Also remove the dead EventSource+close() pair that
was left in proxySwitchLocal from an earlier draft.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-09 12:58:34 +00:00
myron 120449a40a Fix remote nginx always showing Stopped — SSH warning poisoned isRunning()
remoteExec uses 2>&1 so SSH's own stderr merged with command stdout.
The 'Warning: Permanently added...' line prepended to 'active' made the
=== 'active' check in isRunning() always return false.

Add -o LogLevel=ERROR to suppress SSH informational/warning messages
while keeping actual remote command output and real SSH errors.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-09 12:55:33 +00:00
myron 5e75d4cae4 Fix nginx proxy start/stop: missing sudo, silent failures, no progress UI
- ProxyManager::sysctl() and reload() now use sudo for local commands —
  www-data cannot run systemctl directly, so start/stop/restart/reload
  were silently failing with permission denied
- Control endpoint now returns success:false when nginx stays stopped
  after a start/restart, or stays running after a stop
- proxyControl() JS shows a loading overlay while the action runs and
  uses error toast when the action reports failure

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-09 11:16:41 +00:00
myron db1f6b8bb8 Fix proxy settings modal never saving — wrong third arg to Nova.modal
Nova.modal(title, bodyHtml, footerHtml) expects footerHtml as an HTML
string, but an async callback was passed instead. The function got
stringified as garbage text in the footer with no save button, so
nothing ever saved regardless of mode chosen.

Replaced with proper footer HTML (Cancel + Save buttons) and wired the
save logic as an event listener on the save button.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-09 11:12:29 +00:00
myron 98f6a0700c Fix NovaCPX update action name mismatch and progress bar null error
- admin.js was calling 'apply-novacpx-update' but endpoint is 'apply-update'
- Add null guard in nova.js progress bar setInterval — _barEl can be set to
  null by the fade-out timer between interval ticks, causing a crash

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-09 11:03:04 +00:00
myron 18c5989c17 Fix constant reload loop caused by 401 redirect in nova.js
The redirect on 401 sent the browser back to /?redirect=... on the same
panel port, which re-served the panel page itself — causing an infinite
reload. The panel pages (admin/reseller/user) already have inline login
forms that activate when auth/me fails, so no redirect is needed.
Return a failure object on 401 so the existing login form shows instead.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-09 10:59:27 +00:00
myron a4bf01d78f Remove API rate limiting
Was blocking logins. Can be reintroduced later if needed.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-09 10:55:19 +00:00
myron e8d13678fb Fix rate limiting triggering on login page loads
Only apply the tight 10/min bucket to POST /auth (actual login attempts).
GET /auth (session checks on page load) now falls into the general 120/min
bucket, preventing the login page from rate-limiting itself during normal use.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-09 10:53:18 +00:00
myron 4409a94d78 Fix install.sh gaps and add missing schema tables
- Add sshpass to base packages (required by ProxyManager remote SSH)
- Add PORT_WEBMAIL to Apache ports.conf listen loop (was missing port 8883)
- Add systemctl nginx/apache2 to www-data sudoers (local proxy mode needs these)
- Fix cron to use real script paths: bin/collect-stats.php + bin/notify-checks.php
- Seed proxy_mode=disabled and proxy_apache_port=80 defaults after schema import
- Add api_rate_limits table (rate limiting middleware requires it)
- Add proxy_hosts table (ProxyManager requires it for host CRUD)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-09 10:40:37 +00:00
myron a2daaa1ea3 Fix SSH known_hosts permission error when running as www-data
Add -o UserKnownHostsFile=/dev/null to remoteExec so SSH doesn't
attempt to write to /var/www/.ssh/known_hosts (permission denied).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-09 10:35:18 +00:00
myron c07639667b Nginx proxy: local mode — Apache port migration, one-click enable/disable
- VhostManager: getApachePort() reads proxy_apache_port setting (default 80);
  writeApache() uses configured port; migrateApachePort() rewrites all vhosts
  and ports.conf; restoreApachePort() reverses the migration
- ProxyManager::switchToLocalMode() — generator: installs nginx if needed,
  migrates Apache to 8090, configs nginx catch-all, starts nginx, syncs proxy
  hosts; rolls back Apache on nginx config failure
- ProxyManager::disableLocalMode() — stops nginx, restores Apache to 80/443
- proxy.php: POST /api/proxy/switch-local and /api/proxy/disable-local (SSE stream)
- admin.js: two-card "not configured" layout (Local Mode / Remote VM);
  proxySwitchLocal() modal with port picker + live progress stream;
  proxyDisableLocal() reverts with progress; 'Disable Local Mode' in service
  controls when mode=local

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-09 10:30:33 +00:00
myron dc77c65a3f Proxy docs: Proxmox-first design note, multi-environment setup guide
- ProxyManager: full header comment block — Proxmox intended env, non-Proxmox
  requirements (sshpass + PermitRootLogin), local mode Apache migration steps,
  cloud/remote server instructions, settings key reference
- admin.js: Setup Guide modal rewritten — 4 options (Proxmox LXC, other
  hypervisors, cloud/remote, local); settings table reference; how-it-works
  section covering health check, auto config push, and uninstall

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-09 10:26:34 +00:00
myron ed552cd5a6 Proxy: setup progress stream, self-healing, uninstall, health check cron
- ProxyManager::runSetupOnRemote() — generator yields step-by-step
  progress; drives SSE stream from /api/proxy/setup-remote POST
- ProxyManager::uninstall(bool) — removes configs from remote or local;
  optionally apt-get removes nginx and sets mode=disabled
- ProxyManager::healthCheck() — called every 5 min from collect-stats.php;
  restarts nginx on remote if found stopped
- proxy.php: POST /api/proxy/setup-remote (SSE stream), DELETE /api/proxy/uninstall
- admin.js: proxyRunSetup() streams output to a live log modal;
  proxyUninstall() with configs-only vs full removal choice;
  'Run Setup on Remote VM' / 'Uninstall' buttons in page header

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-09 10:23:02 +00:00
myron 6b95571548 Nginx proxy: remote VM support via SSH
- ProxyManager: all ops (start/stop/reload, config push) work over SSH
  when proxy_mode=remote; sysctl/reload/writeHostConfig/deleteHost all
  route to remoteExec/remotePush helpers
- proxy.php: add GET/POST /api/proxy/settings and POST /api/proxy/test-remote
- admin.js: Settings modal with mode selector + remote fields + Test Connection;
  page header always shows Settings button; status card shows mode + remote host;
  'not installed' state directs to Configure Remote Proxy VM

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-09 10:14:18 +00:00
myron 89c9bfdc49 Security hardening: token-at-rest, rate limiting, XSS, transactions
- auth: impersonate stores empty data instead of raw cookie; unimpersonate
  issues a fresh session rather than replaying a stored token
- api/index.php: restore rate limiting (10 req/min auth, 120 general)
- nova.js: 401 redirects to login instead of silently returning error;
  escHtml now escapes single quotes to prevent onclick XSS
- accounts: wrap ownership-change 4-write path in beginTransaction/commit;
  restore audit body on account.update
- reseller/user login cards: use $_pname instead of hardcoded 'NovaCPX'

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-09 07:51:21 +00:00
myron d29b8b9d65 Add VERSION to gitignore to prevent deploy conflicts
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-09 03:36:34 +00:00
myron 8a21179e48 Auto-deploy setup: polling cron + JARVIS webhook relay
- VM polls GitHub every minute via novacpx-poll-deploy cron
- GitHub webhook registered at JARVIS for push notifications
- Deploy webhook.php at /deploy/webhook.php for future relay use
- Queue file permissions fixed (666) for www-data/root shared access

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-09 03:31:31 +00:00
myron 537d52dafa Role isolation, impersonation, account ownership, loading spinners, Docker fixes
- Enforce portal role isolation: admin/reseller/user can only auth on their own port
- Admin/reseller impersonation: Login As with cookie handoff + Return banner in user panel
- Account ownership: admin can reassign accounts to resellers, DNS NS follows
- accounts/update: ownership change cascades package + NS to new owner
- users.php endpoint: admin list/filter by role (reseller dropdown)
- Docker launch fix: uDockerUpdateParams defined before call
- Nova.loading() spinners: login, SSL, PHP switch/save, backup create, docker launch/actions
- Logo consistency: gradient CPX text on all login pages, novacpx_logo_html() in all sidebars
- BackupManager: fix DB class name, table name, column name
- DNSManager: fix settings keys (ns1_hostname/ns2_hostname)
- docker.php: resolve account_id from user uid for all actions
- Auth: impersonate sets cookie + stores return_token for seamless round-trip

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-09 02:56:45 +00:00
myron f75f124725 Fix login and panel visibility for user and reseller portals
- initUser/initReseller: show main-layout and hide auth-check on
  successful auth (was never shown, causing blank page after login)
- Fix login form IDs in user/index.php and reseller/index.php:
  changed l-user/l-pass/login-err to li-user/li-pass/li-err to
  match what doLogin() reads; add onsubmit handler so form works
  immediately without waiting for JS to replace it
- Wire logout button in both boot sequences
- Fix data.accounts → data in reseller.js (same paginate() bug as admin)
- Reset myron user password to Joker1974!!!

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-08 22:50:28 +00:00
myron eccbfbfeea Fix user portal blank after login
- initUser() now hides auth-check and shows main-layout on success
- Remove conflicting inline script from user/index.php that referenced
  #app (non-existent) instead of #main-layout, causing null JS error
  that prevented the panel from ever rendering after successful auth
- Wire logout button in boot sequence (was only in the removed inline script)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-08 22:43:17 +00:00
myron 77f88ca5bf Fix suspend/unsuspend/terminate — wrong body field name
JS sends account_id but PHP was reading id; both now accept either.
Same fix applied to terminate.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-08 22:39:22 +00:00
myron fe2d3d457c Add account edit modal — package, PHP version, email
- New accounts/update endpoint: updates package_id, php_version, email,
  and notes; switches PHP-FPM pool when version changes
- Edit button on each account row opens pre-populated modal
- Modal shows email, package dropdown, PHP version selector; domain
  is read-only with tooltip explaining it can't change

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-08 20:39:54 +00:00
myron af9f1b8f43 Fix accounts list display, OS update terminal modal
- Fix accounts list always showing empty: Response::paginate() returns data
  as res.data (array), not res.data.accounts — fix all 9 call sites in admin.js
- Replace blocking apply-os-update with background job + terminal modal:
  start-os-update runs apt-get as nohup subprocess with sudo, writes to
  /tmp log file; os-update-status polls log and done-file; admin.js shows
  scrolling terminal modal that auto-closes when complete
- Fix OS update: was running apt-get without sudo (www-data lacks root)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-08 20:24:36 +00:00
myron d587ad4ebd Fix web server switch — panel always stays on Apache
- novacpx-webserver-switch: new helper script that manages ports 80/443
  only; panel ports 8880-8883 are never touched
- system.php: save-option web_server now calls the helper script instead
  of stopping all web servers (which killed the panel)
- admin.js: server options shows live Apache/Nginx status badges and notes
  that the panel always runs on Apache
2026-06-08 16:36:49 +00:00
myron 906720e215 Fix updates page and server options page
- system.php: sudo git for check/apply-novacpx-update (fixes www-data ownership)
- system.php: find instead of glob(**) for PHP syntax check
- system.php: php8.3 -l instead of php -l
- system.php: sudo rsync/chown for deploy
- system.php: steps[] tracking in apply-novacpx-update response
- system.php: config.ini sync on save-option web/ftp/dns_server change
- system.php: safety guard blocking removal of active DB engine
- admin.js: Nova.loading() in soSave() for server options page
- admin.js: fix soSave page reload (window._novaPages -> adminPage())
- admin.js: applyNovaCPXUpdate shows step-by-step modal on completion
2026-06-08 16:23:27 +00:00
myron 237c19c13d Guard db-engine remove from dropping panel DB; restore VERSION 2026-06-08 16:05:03 +00:00
myron 6217c3b9f9 Add activity bar to Nova.api() — every API call shows top-of-page progress stripe 2026-06-08 15:59:43 +00:00
myron ae875917cb Fix WordPressManager (wrong DB class) and DockerManager (sudo install) 2026-06-08 12:21:06 +00:00
myron ffb623dd16 Fix service controls, loading overlay, DB engine awareness
- system.php: add sudo to all systemctl/apt-get calls (www-data runs as non-root)
- system.php: flush command for postfix uses postqueue -f
- system.php: save-option writes web_server to config.ini so VhostManager picks it up
- databases.php: list endpoint supports admin (no account_id), defaults db type to active_db_engine setting
- nova.js: add Nova.loading() / Nova.loadingDone() spinner overlay
- admin.js: adminServiceAction shows loading overlay + optimistic badge update
- admin.js: phpInstallVersion, dbEngineAction, docker install, OS/NovaCPX update all show loading overlay
- WordPressManager.php: fix Database::getInstance() -> DB::getInstance()->pdo()
- DockerManager.php: fix install to write script file and sudo bash (no interactive terminal)
2026-06-08 12:20:55 +00:00
myron 99eb8ede67 Fix service status refresh, DNS zones, Docker page, SSL manager, input styling, updates
- Service status: data-svc-status/data-svc-dot attrs + refreshSvcStatus() updates in-place after restart/stop/start
- svc-check endpoint: lightweight is-active poll for single service
- Docker page: fix function signature (was docker(el), now returns HTML)
- DNS zones: fix records response (array not object), fix add-record content field, fix delete-zone accept zone_id
- DNS create-zone: allow admin to create zones without account_id
- SSL manager: add Generate CSR modal (openssl req), Upload Custom SSL modal, explain both options
- nova.css: add input:not([type]), date/search/tel/time types, .form-control to styling selector; fix date picker icon
- Updates: fix panel-up check (was fsockopen on HTTPS port, now curl -sk); add set_time_limit(180/300)
- apply-os-update: set_time_limit(300)
- DB engine manager: fix duplicate INSERT line

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-08 11:56:14 +00:00
myron 5ef458dfb0 Fix service switching, dynamic dashboard services, DB engine manager
- save-option: inline service switching (web/ftp/dns) instead of missing shell scripts
- stats: dynamic service list based on web_server/ftp_server/dns_server settings
- service action: allow all variants (nginx, pure-ftpd, pdns, nsd, etc.)
- mysqlManager: full rewrite with MySQL/MariaDB/PostgreSQL engine cards (install/remove/start/stop), active engine selector, all-databases table
- ftpServer page: dynamic — shows whichever FTP server is configured, not hardcoded proftpd
- db-engine-action: fixed duplicate INSERT line

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-08 11:41:49 +00:00
myron 5251494f7a feat: full PHP Manager — version install/remove, per-version extension management
php.php: install-version, remove-version, version-extensions, install-extension,
remove-extension, fpm-action endpoints. versions now returns fpm_active status
and panel_php (current runtime version).

admin.js phpManager(): grid of installed/not-installed versions with Install/
Remove/Restart FPM buttons; Extensions panel slides in below with filterable
list, dropdown of common extensions + custom input, per-extension Remove buttons.
Panel PHP info card shows which version NovaCPX runs on.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-08 11:35:12 +00:00
myron 31bd590b26 fix: web server manager buttons wrap instead of overflow into next card
Use btn-xs + flex-wrap so Restart/Start/Stop fit within the stat-card
on narrow layouts without bleeding into adjacent cards.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-08 11:30:53 +00:00
myron 89037d7544 fix: 2FA, sessions, cloudflare, wordpress, nginx-proxy pages showing stub
Stubs inside the IIFE (twofa/sessions/cloudflare/wordpress/nginxProxy)
always shadowed the real global implementations. Pages dict captured
the stubs so clicking those nav items just showed "Loading...".

Fix: stubs now delegate to renamed global functions (*Page suffix).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-08 11:27:12 +00:00
myron c80513d17b fix: server_stats column names + remove hardcoded spamassassin unknown
system.php: INSERT used wrong column names (cpu_pct/ram_pct/disk_pct/
load_1m) — table has cpu_usage/ram_usage/disk_usage/load_avg matching
migration 007. Fatal PDOException was crashing stats API → all services
showing as unknown.

admin.js mailServer(): remove spamassassin row (not installed, was
permanently hardcoded to 'unknown').

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-08 11:23:20 +00:00
myron 0bdaa048df fix: remove stray brace in admin.js that broke IIFE scope
Extra } after auditLog helpers was closing the outer async IIFE early,
causing phpManager() and all subsequent functions to parse outside the
IIFE — resulting in "Unexpected token async" crash and a blank panel.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-08 11:05:51 +00:00
myron 1675de36eb docs(#23): install guide, admin guide, reseller guide, user guide, API reference
Five markdown documents covering the full panel:
- docs/README.md: index with links to all guides
- docs/install.md: requirements, one-liner install, file layout, config.ini, auto-deploy, upgrade
- docs/admin-guide.md: all admin panel sections (accounts, DNS, mail, security, Docker, notifications, WHMCS)
- docs/reseller-guide.md: account management, white-label branding, Docker quotas
- docs/user-guide.md: files, email, databases, FTP, DNS, SSL, cron, Docker, settings
- docs/api-reference.md: all 25+ endpoints with request/response shapes, auth, rate limits, role access

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-08 10:57:03 +00:00
myron bfa2cfc3f8 fix: CyberMail from field format + deploy runner syncs api/ and lib/
- Notifier.php + test-notify: use plain email address in 'from' field
  (CyberMail rejects "Name <email>" format)
- deploy-runner.sh: rsync panel/api/ and panel/lib/ to web root after
  panel/public/ sync; also syncs panel/bin/ to /opt/novacpx/bin/

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-08 04:40:16 +00:00
myron 2ab74b7569 feat(#25): email notifications via CyberMail
- Notifier.php: CyberMail API sender with 4 trigger types (account
  created, suspended, disk quota warning, SSL expiry)
- Reads cybermail_api_key / notify_from_* / notify_admin_email from
  settings table
- accounts.php: fires Notifier on create (welcome + admin alert) and
  suspend (user + admin alert)
- system.php: notify-settings GET, save-notify-settings POST,
  test-notify POST (with API key masking)
- bin/notify-checks.php: daily cron for disk ≥85% and SSL ≤14 days
  (flag-based dedup in settings table)
- admin panel: Notifications page with form + trigger reference table;
  sidebar link added

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-08 04:12:47 +00:00
myron 33c36ffc65 Add #18 reseller white-label branding + #24 audit log UI with filters
#18: reseller_branding table (migration 008). branding.php endpoint: get/save/
     upload-logo/delete-logo/resellers. _branding.php server-side helper injects
     CSS vars (--primary, --accent), custom CSS, favicon, and panel name into
     <head> of reseller + user portals at page-load time (no flash of unbranded
     content). NOVACPX_BRANDING JS global carries panel_name/support_email/
     support_url/hide_powered_by for runtime use. Reseller panel gets a new
     "White Label" sidebar page with logo upload, color pickers with live preview,
     support contact fields, powered-by toggle, and custom CSS textarea.

#24: audit-log backend now accepts user/action/date_from/date_to filter params.
     auditLog() JS rebuilt: filter bar at top, paginated table, expandable detail
     rows (click row to show JSON detail), total entry count, page buttons.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-08 03:51:45 +00:00
myron dbc5a01de9 Fix #4-#8: mail virtual domains, DNS verified, reseller isolation, missing DB tables
#4: Postfix virtual mailbox config (virtual_mailbox_domains/maps, vmail user, maildir
    at /var/mail/vhosts/%d/%n). Dovecot SQL backend pointed at novacpx.email_accounts
    with SHA512-CRYPT passdb and per-domain Maildir userdb.

#5: BIND9 confirmed working — dig @localhost resolves testdomain1.com correctly.

#6: Certbot 2.9.0 confirmed installed; domains.document_root wired; infrastructure
    ready for live domain issuance (testdomain1.com not publicly resolvable so
    dry-run expected to fail).

#7: Fixed all broken user-panel API queries — missing tables (databases, ftp_accounts,
    ssl_certs, cron_jobs, php_configs, notifications) created; `databases` reserved-word
    backtick-quoted across DatabaseManager+endpoints; domains.php is_primary→type=main,
    doc_root→document_root column fixes; DNSManager::createZone call signature fixed;
    stats/account auto-resolves account_id for user role.

#8: assert_account_access() helper added to api/index.php; reseller ownership check
    wired into email, ftp, databases, domains, dns, ssl endpoints.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-08 03:31:30 +00:00
myron d49095f4e8 fix: stats.php RAM% calculation key typo (MemTotal: not MemTotal_kB)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-08 03:01:39 +00:00
myron c0c9865653 feat: server monitoring charts, package limits, WHMCS bridge, server options (#19-22)
#19 Server monitoring charts:
- server_stats table (migration 007) + collect-stats.php cron script
- serverStatus() page rebuilt with Chart.js line charts (CPU/RAM/disk)
- Chart.js lazy-loaded from CDN; history shown for last 24h

#20 Cron job manager: already complete in prior session

#21 Package limits enforcement:
- email.php: checks max_email before creating email account
- databases.php: checks max_databases before creating database
- ftp.php: checks max_ftp before creating FTP account
- stats.php: fixed column names (max_email/max_ftp/max_databases vs old aliases)

#22b WHMCS billing bridge:
- whmcs.php endpoint: create/suspend/unsuspend/terminate/changepackage/info
- Auth via X-WHMCS-Key header; enabled/key stored in settings table

#22a/c/d/e Server options admin page:
- Web/mail/FTP/DNS server selection with settings persistence
- Server switch triggers /opt/novacpx/bin/switch-*.sh scripts (if present)
- NS health checker: live dig lookup of all zones vs configured NS1/NS2
- system.php: server-options + save-option actions
- dns.php: ns-health action

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