feat: add VNC check, SSH key paste, resources panel, sliders, and self-service billing

- 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>
This commit is contained in:
EZSCALE
2026-02-07 14:25:43 -06:00
parent 49fdd9e49b
commit 1e471affd0
13 changed files with 915 additions and 505 deletions

View File

@@ -130,9 +130,10 @@ class ConfigureService extends Module
/**
* @param int $id
* @param array $vars
* @param int|null $vfUserId VirtFusion user ID (for creating SSH keys from raw public key)
* @return bool
*/
public function initServerBuild(int $id, array $vars): bool
public function initServerBuild(int $id, array $vars, ?int $vfUserId = null): bool
{
if (!$this->cp) return false;
@@ -141,17 +142,27 @@ class ConfigureService extends Module
// Generate a random 8 character hostname
$hostname = substr(str_shuffle('abcdefghijklmnopqrstuvwxyz'), 0, 8);
$sshKeyValue = $vars['customfields']['Initial SSH Key'] ?? null;
$sshKeyId = null;
if (!empty($sshKeyValue)) {
if (is_numeric($sshKeyValue)) {
// Existing SSH key ID
$sshKeyId = (int) $sshKeyValue;
} elseif (preg_match('/^ssh-/', $sshKeyValue) && $vfUserId) {
// Raw public key — create it via API
$sshKeyId = $this->createUserSshKey($vfUserId, $sshKeyValue);
}
}
$inputData = [
"operatingSystemId" => $vars['customfields']['Initial Operating System'] ?? null,
"name" => $hostname,
"sshKeys" => [
$vars['customfields']['Initial SSH Key'] ?? null
],
'email' => true
];
if (empty($vars['customfields']['Initial SSH Key'] ?? null)) {
unset($inputData['sshKeys']);
if ($sshKeyId) {
$inputData['sshKeys'] = [$sshKeyId];
}
$request->addOption(CURLOPT_POSTFIELDS, json_encode($inputData));
@@ -165,4 +176,37 @@ class ConfigureService extends Module
return ($httpCode == 200 || $httpCode == 201);
}
/**
* Create an SSH key for a VirtFusion user from a raw public key string.
*
* @param int $userId VirtFusion user ID
* @param string $publicKey Raw SSH public key (ssh-rsa ..., ssh-ed25519 ..., etc.)
* @return int|null Created key ID or null on failure
*/
public function createUserSshKey(int $userId, string $publicKey): ?int
{
if (!$this->cp) return null;
$request = $this->initCurl($this->cp['token']);
$keyData = [
'userId' => $userId,
'name' => 'WHMCS-' . date('Y-m-d'),
'publicKey' => trim($publicKey),
];
$request->addOption(CURLOPT_POSTFIELDS, json_encode($keyData));
$response = $request->post($this->cp['url'] . '/ssh_keys');
Log::insert(__FUNCTION__, $request->getRequestInfo(), $response);
$httpCode = $request->getRequestInfo('http_code');
if ($httpCode == 200 || $httpCode == 201) {
$data = json_decode($response, true);
return $data['data']['id'] ?? null;
}
return null;
}
}