- 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>
#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>
#26 Mobile responsive:
- Hamburger button (SVG) in topbar for all three panels (admin/user/reseller)
- Sidebar overlay div for click-outside-to-close on mobile
- nova.js: DOMContentLoaded toggle handler with overlay and auto-close on nav click
- nova.css: sidebar-overlay, page-header, panel/panel-header, table, btn-success/warning/danger/secondary/xs,
badge-muted; mobile media query shows toggle, fixes stats-grid/modal/panel-header layout
#27 Custom error pages:
- /errors/404.php and /errors/500.php with NovaCPX dark theme matching panel design
- Apache ErrorDocument 400/401/403/404/500/503 for ports 8880/8881/8882 with Alias /errors
#28 API rate limiting:
- api_rate_limits table (migration 004) with per-IP per-bucket counters
- api/index.php: 10 req/min for auth endpoint, 120 req/min for all others
- Returns X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset headers
- Returns 429 Too Many Requests when exceeded; rate limit failure is non-fatal
#29 Session Manager:
- sessions.php endpoint: list/revoke/revoke-user/revoke-all
- Admin panel Sessions page: table of active sessions with user, role, IP, browser, timestamps
- Revoke single session, revoke all for user, revoke all sessions (self-evicts)
Each panel now has its own dedicated port and is fully self-contained:
- Port 8880: User panel (end-user hosting dashboard)
- Port 8881: Reseller panel (account/package management)
- Port 8882: Admin panel (datacenter/server manager)
Changes:
- install.sh: PORT_USER/PORT_RESELLER/PORT_ADMIN constants; three separate
nginx/Apache vhosts; UFW opens all three ports; Fail2Ban jail per port;
credentials file shows all three URLs
- config.ini: stores port_user/port_reseller/port_admin
- Core.php: defines PORT_USER/RESELLER/ADMIN, detects CURRENT_PORTAL from
SERVER_PORT so the API knows which tier is being accessed
- Auth.php: portalUrl() maps role → correct port for cross-portal redirects
- auth.php endpoint: returns portal_url on login so JS redirects to right port
- index.php login: uses portal_url from API response (no hardcoded paths)
- admin/index.php: inline login form (port 8882 is self-contained, no redirect)
- user/index.php: inline login form (port 8880 self-contained)
- reseller/index.php: new full reseller panel with inline login (port 8881);
sidebar with accounts, packages, DNS, branding, bandwidth report sections
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>