Major client-area overhaul, WHMCS 9 + VirtFusion v7 compatibility, and a
hardening pass on every destructive client.php endpoint.
Tested against WHMCS 9.0.3 + VirtFusion v7.0.0 Build 9.
Features
- "On This Page" jump-link group injected into the WHMCS Actions sidebar
via ClientAreaPrimarySidebar; auto-hides links for hidden panels.
- Monthly traffic chart (last 12 months) with rx/tx bars and centered
legend; replaces the dead canvas that read non-existent JSON paths.
- Live Stats panel: CPU, memory, disk I/O from remoteState; 30s refresh
while the panel is visible AND the page has focus.
- Filesystem usage rows in the Resources panel from qemu-guest-agent
fsinfo; pseudo-FS filtered out.
- Server Overview meta chips: data-center location with country flag,
OS template/agent name with kernel on hover, "Created N days ago".
- Hypervisor maintenance banner at the top of the page.
- Mask Sensitive screenshot mode: IPv4 keeps first two octets, IPv6
keeps first two hextets, hostnames keep first char per dot-label.
Inputs masked via text-security: disc; covers Server Name + Hostname
+ IP cells + rDNS panel rows.
- Per-IP copy buttons folded into the Server Overview cells (replaces
the deleted standalone Network panel).
- VNC viewer popup served from a same-origin authenticated route
(client.php?action=vncViewer) — POST + requireSameOrigin, rotates
the wss token on every open, X-Frame-Options DENY, strict CSP.
Bug Fixes
- UsageUpdate cron silently no-op'd: read server.usage.traffic.used
which doesn't exist. Bandwidth now from /servers/{id}/traffic;
disk usage from remoteState.agent.fsinfo.
- WHMCS 9 multi-service order short-circuit: AfterModuleCreate's
AcceptOrder fired after the first service and terminated the batch
loop, orphaning siblings. Defer until every VF service in the order
has a server_id.
- Orphaned services produced six generic 500s; new
requireProvisionedService() helper emits one clean 409 with an
actionable message. Wired into all 17 client.php cases.
- Server Overview Traffic showed "- / Unlimited"; now renders real
bytes and "Unmetered" (limit=0 is per-period uncapped, not feature-off).
- Rename endpoint moved to PUT /servers/{id}/modify/name in VF v7
(was 404'ing); response is HTTP 201 not 200/204.
- Rename was force-lowercasing the input; relaxed validation to
preserve case + freeze the input row mid-flight to prevent
double-submits.
- "Other" OS category icon override removed; uses VirtFusion's icon
instead of a hardcoded SVG.
- Save button squish on the rename row fixed via flex-wrap layout.
Security
- CSRF protection (requirePost + requireSameOrigin) added to every
destructive POST: rebuild, resetPassword, resetServerPassword,
powerAction, rename, selfServiceAddCredit, toggleVnc, vncViewer.
Previously only rdnsUpdate had it.
- Open-redirect defence in Module::fetchLoginTokens — refuses to
return a redirect URL whose host doesn't match the configured VF
panel hostname.
- Per-action rate limiting via new Module::requireRateLimit helper
(Cache-backed): rebuild 60s, resetPassword/resetServerPassword 30s,
powerAction 10s, vncViewer/toggleVnc/selfServiceAddCredit 5s.
- vncViewer route delivers strict Content-Security-Policy
(default-src none, script-src self + VF panel, connect-src wss VF
panel, frame-ancestors none).
- IPv6 examples in placeholder/comments switched to the IANA
documentation prefix 2001:db8::/32 (RFC 3849).
Removed
- Network panel (duplicated Server Overview IP rows).
- VNC enable/disable toggle (VF firewall flag is non-functional;
toggle was misleading).
- Network Speed row in Resources panel (always 0 from VF API).
Internal
- Module::fetchServerData now passes ?remoteState=true.
- ServerResource::process exposes osName/osPretty/osKernel/osDistro/
osIcon/location/locationIcon/hypervisorMaintenance/createdAt/
builtAt/live.* fields.
- Module::toggleVnc corrected to send {vnc:bool} (the actual API
param) instead of {enabled:bool} (silent no-op).
- Module::getVncConsole + toggleVnc return baseUrl alongside the
envelope so the viewer route can build the wss URL.
- Panel margins tightened mb-3 → mb-2 across all 11 panels.
The package field exposed by VirtFusion as `primaryStorageProfile` is a
storage *type code* (mirrors `server_packages.storage_type` in the VF
database), not a profile id. It's meant to filter to any pool whose
`storageType` matches — multiple pools across the fleet can carry the
same code, which is exactly how multi-hypervisor placement works for
mountpoint/datastore storage.
`capForStorage()` was checking `pool.id` against this code. Pool ids are
unique per hypervisor (e.g. for the same logical mountpoint on three
hypervisors, ids 23/28/30) and almost never match the type-code domain
(0=local default, 4=mountpoint, etc.). The mismatch silently returned 0
for every hypervisor, zeroing qty fleet-wide whenever the package's
type code didn't accidentally collide with some pool id.
Symptoms in the wild: every stock-controlled VPS product showed qty=0
in WHMCS even with abundant memory/CPU/IPv4 capacity. Disabling
`stockcontrol` on the product or removing `primaryStorageProfile` from
the package were the only known workarounds; both lose the actual stock
gating this module is meant to provide.
Fix:
- Match `pool.storageType` instead of `pool.id`.
- Walk all pools that match (a hypervisor may have multiple pools of
the same type) and use the one that fits the most VMs, instead of
short-circuiting on the first match. A disabled pool no longer kills
the whole hypervisor's capacity for that type — we just skip it and
keep looking for an enabled peer.
- Rename the parameter from `$profileId` to `$storageTypeId` so future
readers don't fall into the same naming trap. Updated the docblock
with a NOTE explaining the VirtFusion-side naming inconsistency.
Verified on a 3-hypervisor cluster with `storageType=4` (mountpoint)
packages: qty went from 0/0/0/0/0/0/0/0 to 66/32/15/7/3/1/32/15 across
the VPS-1 through VPS-32 + storage products without any other config
change.
Opt-in per product via WHMCS's native tblproducts.stockcontrol toggle.
When enabled, the module overwrites tblproducts.qty with the number of
VPSes the panel can still actually provision, derived from two
authoritative sources:
- GET /packages/{id} for the per-VPS resource footprint (memory,
cpuCores, primaryStorage, primaryStorageProfile, enabled)
- GET /compute/hypervisors/groups/{id}/resources for live
free/allocated data per hypervisor in the group
Algorithm sums min(memory, cpu, storage) across eligible hypervisors
(enabled AND commissioned AND !prohibit) for every group the product
can be placed in (default configoption1 plus every numeric value of a
Location configurable option), capped by the group-level IPv4 pool
taken as max() within a group to avoid double-counting. Storage
matching is strict against package.primaryStorageProfile; hypervisors
without the named pool contribute 0.
FAIL-SAFE INVARIANT: transient API failures return null from
Module::fetchPackage / Module::fetchGroupResources, and the orchestrator
leaves tblproducts.qty UNCHANGED in that case. Confirmed-missing
conditions (HTTP 404, package.enabled=false) return qty=0. Without this
tri-state contract the module would either zero out inventory during
API blips, or show inventory for packages that have been deleted.
Triggers:
- AfterModuleCreate: refresh + auto-accept pending order
- AfterModuleTerminate: refresh (capacity came back)
- AfterCronJob: every-2-hour safety net for out-of-band panel changes
- ClientAreaPageCart: opportunistic per-product refresh in order flow
- admin.php?action=stockRecalculate: on-demand full recalc
Shared 30s rate-limit (stockrefresh:event) coalesces provision bursts;
60s per-product limit (stockrefresh:{pid}) caps cart-page refreshes;
grpres:{id} 120s TTL caps upstream API reads per group regardless of
how often hooks fire.
Auto-accept: AfterModuleCreate calls WHMCS AcceptOrder with
autosetup=false when the parent order is still Pending. Idempotent;
already-accepted orders are skipped via strcasecmp status check.
New per-product config option stockSafetyBufferPct (configoption7,
default 10) reserves X% of each resource's max before computing fits.
Blank falls back to 10% so existing products get headroom without any
config change. Ignored for unlimited resources (max=0) and for IPv4
(no per-hypervisor max in the response).
TestConnection now probes /compute/hypervisors/groups to surface
missing compute:read scope at config time instead of as unexplained
nightly silence.
Two complementary improvements for operators debugging a misconfigured
addon — both motivated by a live production incident where "every IP
shows no zone" took several hypotheses (wrong serverId, wrong key,
stale cache) before landing on the real cause.
1. Diagnose-an-IP panel on the addon admin page (VirtFusionDns.php
_output()). Takes an IP in a text input and runs the full pipeline
inline: prints the current config snapshot, forces a fresh zone
list from PowerDNS (bypassing cache), shows the computed PTR name,
shows what IpUtil::findZoneAndPtrName selects, and fetches the
current PTR content. Every common failure mode — wrong key, wrong
serverId, forgotten zone, mis-aligned RFC 2317 label, stale cache
— produces a distinctive shape in that output.
2. More actionable error messages in PowerDns\Client::ping():
- On 401/403: now spells out the three real causes (API key
mismatch, api-allow-from excluding the WHMCS IP, whitespace in
the stored key) as a checklist, so the operator doesn't have to
guess which they're hitting.
- On 404: explicitly names serverId as the field to check and
reminds that "localhost" is the PowerDNS API server identifier,
NOT the nameserver's hostname (a surprisingly common misreading
of the field label).
The addon helper virtfusiondns_load_server_libs() now also pulls in
Resolver + PtrManager lazily since the diagnostic pane needs IpUtil's
pipeline-level output. They're optional — missing files don't break
the basic status page.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
VirtFusion's IPv6 allocation model routes a whole /64 to the VPS rather
than exposing discrete host addresses via the API. The previous module
silently filtered these entries — the client saw v4 IPs in the rDNS
panel but no v6 at all, with no indication why, and no way to set a
PTR for a specific address they were using inside the /64.
This commit surfaces subnets as first-class entries throughout:
- IpUtil::extractIps() now returns {addresses, subnets, skipped}. The
subnets bucket carries {subnet, cidr} pairs for any v6 allocation
with cidr != 128; /128 entries continue to be treated as discrete
addresses, and genuinely malformed entries still go to skipped.
- IpUtil::ipv6InSubnet($ip, $prefix, $cidrBits) — new helper that does
binary-prefix subnet containment via inet_pton + bit masking. Used
for v6 ownership verification (see below).
- PtrManager::listPtrs() emits subnet-only rows ahead of per-IP rows,
so the client UI can render the /64 as an informational anchor with
an entry point for the custom-host flow.
- client.php::rdnsUpdate adds a second ownership-check stage: if the
submitted IP is v6 AND doesn't match any discrete address, check
whether it falls inside one of the server's allocated subnets. This
preserves "only your own IPs" while unlocking the feature.
- Client-side (module.js / module.css) renders subnet rows with a
collapsible "Add host PTR" form (IP + hostname inputs) that posts
to the same rdnsUpdate endpoint. Subnet rows get a distinct cyan
accent so they visually differ from per-host rows.
The usual guards still apply to v6 custom-host writes: forward-DNS
(FCrDNS) verification, PTR regex, per-IP rate limit, same-origin /
POST-method gates. Nothing about the security envelope changes — only
what input is accepted as "you own this IP".
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
WHMCS's addon-module password-type fields are stored plaintext in
tbladdonmodules.value — unlike tblservers.password which IS encrypted
at rest. Config::get() was blindly calling decrypt() on the raw value
and then preferring its output over raw when the two differed.
Unfortunately, when decrypt() is fed a plaintext string, it doesn't
return empty or unchanged — it returns a short binary-garbage string
(observed: 4 bytes of \xEF\xBF\xBD unicode-replacement noise for a
32-char plaintext). That garbage then went into the X-API-Key header,
PowerDNS responded 401, and every rDNS read returned an empty zone list,
surfacing as "no zone" for every IP in the client UI.
Fix: only accept decrypt()'s output when it's printable ASCII. Real
API keys are always printable; decrypted ciphertext that looks like
binary is a mangled-plaintext signal, so we fall back to raw. Also
trim() the chosen value to defeat a second foot-gun — admin UIs can
silently append a newline on paste, which would land in the header
verbatim and produce the same 401.
Diagnosed via direct WHMCS tbladdonmodules inspection on a user's
affected install; confirmed the fix end-to-end with a live ping()
returning HTTP 200 post-deploy.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Enriches class-level docblocks and inline comments across the shared
utility classes with the "why" behind design decisions that aren't
obvious from reading the code alone:
- Cache two-tier rationale, atomic-write semantics, failure modes
- Curl single-use-per-instance rationale, default option choices
- Log wrapper rationale, redaction expectations for callers
- Database auto-migration philosophy, schema-versioning approach
- ServerResource flat-array rationale, interfaces[0]-only limit called
out for future maintainers, unit-conversion map
- ConfigureService why a sibling of ModuleFunctions, catalogue caching
policy, cp-in-constructor reasoning
Pure documentation — no code changes, all files remain lint-clean and
Pint-formatted.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Introduces an opt-in reverse DNS management subsystem backed by a PowerDNS
Authoritative HTTP API. Runs via a companion WHMCS addon module
(modules/addons/VirtFusionDns) that holds settings and a Test Connection
page; the server module reads those settings from tbladdonmodules and
short-circuits when the addon is absent or disabled, so provisioning is
unaffected for operators who don't use the feature.
Lifecycle hooks:
- createAccount creates PTRs for every assigned IP (forward DNS must
already resolve to the IP — FCrDNS enforcement)
- renameServer updates only PTRs whose content matched the old hostname,
preserving client-custom records
- terminateAccount deletes all PTRs before the local state is purged
- TestConnection merges PowerDNS health check with the existing VirtFusion
check
- A DailyCronJob hook reconciles missing PTRs additive-only (never
overwrites)
Client UI: new "Reverse DNS" panel on the service overview with one
editable PTR input per assigned IP, per-row status badges, and
forward-DNS rejection on save. Admin services tab gets a parallel
widget with Reconcile (additive) and Reconcile (force reset) buttons.
New subsystem at lib/PowerDns/:
- Client.php PowerDNS API wrapper (X-API-Key, listZones/getZone/
patchRRset/notifyZone), auto-NOTIFY on successful PATCH
- Config.php Loads + decrypts addon settings from tbladdonmodules
- IpUtil.php PTR-name generation (IPv4 + IPv6), zone matching,
RFC 2317 classless parsing
- Resolver.php FCrDNS verification via dns_get_record with CNAME-chain
following and per-(hostname,ip) caching
- PtrManager.php Orchestrator: syncServer, deleteForServer, listPtrs,
setPtr, reconcile, reconcileAll
Security hardening helpers added to Module and applied to the rDNS
endpoints:
- requirePost() HTTP method gate (405 on non-POST mutations)
- requireSameOrigin() Origin/Referer check against WHMCS host (CSRF
defence against cross-site form POST)
- requireServiceStatus() tblhosting.domainstatus filter (Active for
writes, Active+Suspended for reads)
RFC 2317 classless delegations (e.g. 64/64.113.0.203.in-addr.arpa.)
supported with alignment validation: rejects misaligned start addresses
that don't correspond to any real delegation boundary.
PowerDNS zone IDs containing '/' are URL-encoded as '=2F' per the
PowerDNS API convention. PATCH success triggers PUT /zones/{id}/notify
so slaves pick up the SOA-bumped serial immediately.
Includes IPv4 + IPv6 support, per-IP write rate limit (10s), fresh
IP-ownership re-verification on every client write (defends against
stale-ownership after IP reassignment), and audit logging of every
successful edit to the WHMCS module log.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Auto-create 'Initial Operating System' and 'Initial SSH Key' custom fields
via Database::ensureCustomFields() on module load, eliminating the manual
modify.sql step
- Delete modify.sql (no longer needed)
- Add try/catch blocks around every DB operation and API call across all PHP
files per CLAUDE.md error handling rules
- Add comprehensive PHPDoc to all classes, methods, and properties
- Set up Laravel Pint (laravel/pint) with Laravel-style preset for consistent
code formatting across the codebase
- Add git pre-commit hook (hooks/pre-commit) that runs Pint on staged PHP
files, auto-installed via Composer post-install/post-update scripts
- Simplify README installation to a single copy-paste command
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
VirtFusion API returns an 'Other' category with icon=linux_logo.png by default.
Null out the icon in groupOsTemplates() so the JS SVG fallback renders instead.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Replace hardcoded date version strings with dynamic timestamps:
overview.tpl uses {$smarty.now}, hooks.php uses time(), AdminHTML uses
$cacheV = time() in heredoc
- Other category gets a gray server/terminal SVG icon instead of falling
through to the OS-specific letter badge
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add overflow:hidden to .vf-os-card and .vf-os-icon
- Constrain .vf-os-icon img with max-width/max-height:100%
- Only apply brand color background as fallback when image fails to load
- No background color when image is present (clean transparent display)
- Apply same logic to both category headers and template cards
- Update both module.js (rebuild panel) and hooks.php (checkout page)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The VirtFusion panel serves OS icons at /img/logo/{icon} not /storage/os/{icon}.
Restore image loading in both rebuild gallery (module.js) and checkout gallery
(hooks.php) with onerror fallback to letter badges. Also restore baseUrl
population in hooks.php for checkout page.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Remove client IP removal capability (keep backend methods removed too)
- Add copy-to-clipboard buttons for IP addresses with tooltip feedback
- Replace OS dropdown with tile gallery (grouped, searchable, brand colors, EOL badges) in rebuild panel and checkout page
- Add inline server rename with friendly name generator and RFC 1123 validation
- Add traffic statistics canvas chart with responsive resize in resources panel
- Add backup listing timeline in manage panel with show-all expansion
- Add VNC enable/disable toggle with connection details and password copy
- Add server root password reset with auto-clipboard copy (never displayed)
- Add skeleton loading placeholders, action cooldowns (power 3s, rebuild 30s), progress indicator with elapsed timer
- Sanitize all client-facing error messages (no raw API errors exposed)
- Convert all state-mutating AJAX calls from GET to POST
- Add explicit break after all output() calls in client.php
- Add Redis-backed API response caching (Cache.php): OS templates 10min, traffic/backups 2min, currencies 30min, packages 10min
- Add GitHub Actions workflow for weekly VirtFusion API change detection
- Move cache busting step after semantic-release in publish workflow
- Add endpoint doc generator script and OpenAPI baseline placeholder
- Improve hostname generation entropy (bin2hex random_bytes)
- Add .superpowers/ to .gitignore
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Escape $serverObject and $systemUrl in AdminHTML.php heredocs to prevent XSS
- Add null guard in Database::getSystemUrl() to prevent fatal error
- Guard primaryNetwork access in module.js to prevent null dereference
- Reset badge/traffic-bar CSS classes on refresh to prevent accumulation
- Add VNC popup-blocked check with user-facing message
- Add BS3 input-group-btn dual class for theme compatibility
- Escape billing template variables with |escape:'htmlall'
- Add cache-busting to admin CSS/JS includes
- Switch cache-busting format from version to date-based (20260207)
- Create .releaserc.json for automated CHANGELOG.md management
- Add changelog/git plugins to semantic-release workflow
- Remove manual [Unreleased] section from CHANGELOG.md
- Update README: install/upgrade with rsync, accuracy fixes, add keygen.js
- Update CLAUDE.md: add keygen.js, document removed features
- Fix SECURITY.md grammar and version operator
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Populate network panel from server data response instead of separate API call
- Conditionally render self-service billing panel based on selfServiceMode config
- Pass selfServiceMode to Smarty template vars
- Remove addIPv4, addIPv6, serverIPs client endpoints and UI buttons
- Remove upgrade/downgrade link from resources panel
- Bump cache-busting version to v0.0.20
- Update CHANGELOG.md
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Adds a "Generate a new key" button to the checkout SSH key section that
creates an Ed25519 keypair entirely in the browser using Web Crypto API.
The public key auto-fills the form field, and the private key is presented
for download/copy with a clear "save now" warning.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Use $params['serverhostname']/serverpassword directly in TestConnection
instead of database lookup (serverid=0 is falsy for new servers)
- Default traffic "Used" to 0 GB when allocated but no usage reported
- Add ?v=0.0.19 cache-busting to JS/CSS includes in overview.tpl
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- VNC panel auto-hides when VNC is disabled on the server
- SSH key paste textarea at checkout with API key creation during provisioning
- Resources panel with current allocation, traffic progress bar, and upgrade link
- changePackage() now applies individual resource modifications from configurable options
- Order form configurable option dropdowns replaced with styled range sliders
- Self-service billing: credit balance, usage breakdown, credit top-up from client area
- Self-service config options (mode, auto top-off threshold/amount) on products
- Auto top-off via WHMCS cron when credit falls below threshold
- CHANGELOG.md covering all versions from 0.0.6 to present
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add isset() guards before count() on ipv4/ipv6 arrays in ServerResource
to prevent PHP 8.0+ TypeError
- Add null checks after getWhmcsService() and getCP() in 18 Module methods
and 5 ModuleFunctions methods to prevent fatal null dereference errors
- Add null guards for $whmcsService and $cp in admin.php impersonateServerOwner
- Fix HTTP status codes throughout admin.php (404, 400, 500, 502 instead of 200)
- Guard ConfigureService methods against $this->cp === false
- Use null coalescing for customfields access in initServerBuild
- Check API response code in initServerBuild instead of always returning true
- Replace exit() with RuntimeException in Curl.php
- Change catch(Exception) to catch(Throwable) in hooks.php for PHP 8.0+
- Open VNC window before AJAX call to avoid popup blocker
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Firewall endpoints now use /firewall/{interface}/ where interface is
"primary" or "secondary" (was missing the interface segment)
- Add applyFirewallRulesets() method for applying predefined rulesets by ID
- Add firewallApplyRulesets client endpoint (comma-separated ruleset IDs)
- Add sanitizeFirewallInterface() helper for input validation
- All firewall methods now accept optional interface parameter (default: primary)
- Document that VirtFusion uses ruleset-based firewall (no individual rule CRUD)
- Update README with correct API paths and ruleset documentation
https://claude.ai/code/session_01TCsJ4WZCGuEX3zqh1tQ2zx
New features implemented:
- Firewall management: enable/disable, status display, apply rules
- IP address management: add/remove IPv4 and IPv6 with client UI
- VNC console access integration (VirtFusion v6.1.0+)
- Backup plan assignment/removal via API
- Resource modification: in-place memory/CPU/traffic changes
- UsageUpdate cron: automated bandwidth and disk usage sync to WHMCS
- Dry run validation: test server creation config before provisioning
- Admin "Validate Server Config" button for dry run testing
Client area additions:
- Firewall panel with enable/disable/apply controls and status badge
- Network panel with IPv4/IPv6 listing, add, and remove buttons
- VNC Console panel with browser-based access
- All panels load asynchronously with spinner indicators
Comprehensive README rewrite with:
- Table of contents, requirements matrix, step-by-step installation
- Detailed configuration guide for all features
- Theme compatibility documentation (Six, Twenty-One, Lagom)
- Complete API endpoints reference organized by category
- UsageUpdate cron documentation with data format details
- Troubleshooting tables for common issues
- Known issues section covering version requirements
- Security architecture documentation
- File structure reference
https://claude.ai/code/session_01TCsJ4WZCGuEX3zqh1tQ2zx
Security improvements:
- Enable SSL/TLS certificate verification by default (was disabled, MITM risk)
- Remove error_reporting(0) that silenced all errors
- Add input sanitization on all user parameters (int casting, regex filtering)
- Return proper HTTP status codes (401, 403, 400, 500) instead of always 200
- Add XSS protection with htmlspecialchars and encodeURIComponent
- Add null checks on API response data before property access
New features:
- Power management: boot, shutdown, restart, and force power off controls
- Server rebuild: reinstall with any available OS template from client area
- Server rename: change server display name via PATCH API
- OS template fetching: client-side endpoint for rebuild OS selection
- TestConnection: validate API credentials from WHMCS server settings
- ServiceSingleSignOn: native WHMCS SSO integration for VirtFusion panel
- Server status badge: visual indicator of server state in overview
- Traffic usage display: show bandwidth used vs allocated
- Checkout validation: ShoppingCartValidateCheckout hook ensures OS selection
Ordering process improvements:
- Add default "Select Operating System" placeholder option
- Add "No SSH Key (Optional)" default for SSH dropdown
- Hide SSH key field/container when no keys available
- Wrap hook in try/catch to prevent checkout page breakage
- Sanitize template names with htmlspecialchars
- Use JSON_HEX_* flags for safe script injection
Theme compatibility:
- Properly formatted Smarty templates with readable indentation
- Dual panel/card CSS classes for Bootstrap 3/4/5 compatibility
- Responsive power button layout with mobile breakpoint
- Framework-agnostic HTML that works with Six, Twenty-One, Lagom, and custom themes
- Suspended service state messaging
Code quality:
- Readable, unminified JavaScript with JSDoc header
- Structured CSS with logical section organization
- Improved error messages throughout all provisioning functions
- Added PATCH method support to Curl wrapper
- Added curl error capture on connection failures
- Added connection and request timeouts (10s/30s)
- Fixed memory conversion to check key name instead of display name
Documentation:
- Complete README rewrite with installation, configuration, and troubleshooting guides
- API endpoint reference table
- Configurable options mapping documentation
- Theme override instructions
- Security considerations section
https://claude.ai/code/session_01TCsJ4WZCGuEX3zqh1tQ2zx