refactor: consolidate duplicate logic across codebase
Some checks failed
Automated Semantic Versioning Release / release (push) Failing after 44s
Some checks failed
Automated Semantic Versioning Release / release (push) Failing after 44s
PHP (Module.php): - Extract resolveServiceContext() helper — eliminates 15 repeated service/whmcsService/getCP/initCurl lookup chains (~200 lines saved) - Extract static groupOsTemplates() — single source for OS template category grouping logic, used by both Module.php and hooks.php PHP (Cache.php): - Add filesystem cache fallback when Redis extension is unavailable - Atomic writes with tmp+rename pattern for race condition safety - extension_loaded() check instead of class_exists() JS (module.js): - Extract vfUrl() helper — replaces 18 identical URL construction strings - Extract vfShowAlert() helper — replaces 25 repeated alert show/hide/class toggle patterns across all action functions hooks.php: - Use Module::groupOsTemplates(data, htmlEscape: true) instead of inline duplicate grouping logic (~40 lines removed) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -86,46 +86,10 @@ add_hook('ClientAreaFooterOutput', 1, function ($vars) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$baseUrl = '';
|
||||
$firstServer = \WHMCS\Database\Capsule::table('tblservers')
|
||||
->where('type', 'VirtFusionDirect')
|
||||
->where('disabled', 0)
|
||||
->first();
|
||||
if ($firstServer) {
|
||||
$baseUrl = rtrim('https://' . $firstServer->hostname, '/');
|
||||
}
|
||||
|
||||
$categories = [];
|
||||
$otherTemplates = [];
|
||||
|
||||
foreach ($templates_data['data'] as $osCategory) {
|
||||
$catTemplates = [];
|
||||
foreach ($osCategory['templates'] as $template) {
|
||||
$catTemplates[] = [
|
||||
'id' => $template['id'],
|
||||
'name' => htmlspecialchars($template['name'], ENT_QUOTES, 'UTF-8'),
|
||||
'version' => htmlspecialchars($template['version'] ?? '', ENT_QUOTES, 'UTF-8'),
|
||||
'variant' => htmlspecialchars($template['variant'] ?? '', ENT_QUOTES, 'UTF-8'),
|
||||
'icon' => $template['icon'] ?? null,
|
||||
'eol' => $template['eol'] ?? false,
|
||||
'description' => htmlspecialchars($template['description'] ?? '', ENT_QUOTES, 'UTF-8'),
|
||||
$galleryData = [
|
||||
'baseUrl' => '',
|
||||
'categories' => \WHMCS\Module\Server\VirtFusionDirect\Module::groupOsTemplates($templates_data['data'] ?? [], true),
|
||||
];
|
||||
}
|
||||
if (count($catTemplates) <= 1) {
|
||||
$otherTemplates = array_merge($otherTemplates, $catTemplates);
|
||||
} else {
|
||||
$categories[] = [
|
||||
'name' => htmlspecialchars($osCategory['name'] ?? 'Unknown', ENT_QUOTES, 'UTF-8'),
|
||||
'icon' => $osCategory['icon'] ?? null,
|
||||
'templates' => $catTemplates,
|
||||
];
|
||||
}
|
||||
}
|
||||
if (!empty($otherTemplates)) {
|
||||
$categories[] = ['name' => 'Other', 'icon' => null, 'templates' => $otherTemplates];
|
||||
}
|
||||
|
||||
$galleryData = ['baseUrl' => $baseUrl, 'categories' => $categories];
|
||||
|
||||
$sshKeys = [];
|
||||
$sshKeysOptions = [];
|
||||
|
||||
@@ -9,17 +9,18 @@ class Cache
|
||||
/** @var \Redis|null */
|
||||
private static $redis = null;
|
||||
|
||||
/** @var bool */
|
||||
private static $available = true;
|
||||
/** @var bool|null */
|
||||
private static $redisAvailable = null;
|
||||
|
||||
/** @var string */
|
||||
private static $fileDir = '';
|
||||
|
||||
/**
|
||||
* Get a Redis connection, or null if unavailable.
|
||||
*
|
||||
* @return \Redis|null
|
||||
* Try to connect to Redis. Returns the connection or null.
|
||||
*/
|
||||
private static function redis()
|
||||
private static function redis(): ?\Redis
|
||||
{
|
||||
if (!self::$available) {
|
||||
if (self::$redisAvailable === false) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -27,8 +28,8 @@ class Cache
|
||||
return self::$redis;
|
||||
}
|
||||
|
||||
if (!class_exists('Redis')) {
|
||||
self::$available = false;
|
||||
if (!extension_loaded('redis')) {
|
||||
self::$redisAvailable = false;
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -36,13 +37,40 @@ class Cache
|
||||
$redis = new \Redis();
|
||||
$redis->connect('127.0.0.1', 6379, 1.0);
|
||||
self::$redis = $redis;
|
||||
self::$redisAvailable = true;
|
||||
return $redis;
|
||||
} catch (\Exception $e) {
|
||||
self::$available = false;
|
||||
self::$redisAvailable = false;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the filesystem cache directory, creating it if needed.
|
||||
*/
|
||||
private static function fileDir(): string
|
||||
{
|
||||
if (self::$fileDir !== '') {
|
||||
return self::$fileDir;
|
||||
}
|
||||
|
||||
$dir = sys_get_temp_dir() . '/vfd_cache';
|
||||
if (!is_dir($dir)) {
|
||||
@mkdir($dir, 0700, true);
|
||||
}
|
||||
|
||||
self::$fileDir = $dir;
|
||||
return $dir;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a cache key to a safe filename.
|
||||
*/
|
||||
private static function filePath(string $key): string
|
||||
{
|
||||
return self::fileDir() . '/' . md5($key) . '.cache';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a cached value.
|
||||
*
|
||||
@@ -51,20 +79,43 @@ class Cache
|
||||
*/
|
||||
public static function get($key)
|
||||
{
|
||||
// Try Redis first
|
||||
$redis = self::redis();
|
||||
if (!$redis) {
|
||||
if ($redis) {
|
||||
try {
|
||||
$data = $redis->get(self::PREFIX . $key);
|
||||
if ($data !== false) {
|
||||
return json_decode($data, true);
|
||||
}
|
||||
return null;
|
||||
} catch (\Exception $e) {
|
||||
// Fall through to file cache
|
||||
}
|
||||
}
|
||||
|
||||
// File cache fallback
|
||||
$path = self::filePath($key);
|
||||
if (!file_exists($path)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
$data = $redis->get(self::PREFIX . $key);
|
||||
if ($data === false) {
|
||||
$raw = @file_get_contents($path);
|
||||
if ($raw === false) {
|
||||
return null;
|
||||
}
|
||||
return json_decode($data, true);
|
||||
} catch (\Exception $e) {
|
||||
|
||||
$entry = json_decode($raw, true);
|
||||
if (!$entry || !isset($entry['expires']) || !isset($entry['data'])) {
|
||||
@unlink($path);
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($entry['expires'] < time()) {
|
||||
@unlink($path);
|
||||
return null;
|
||||
}
|
||||
|
||||
return $entry['data'];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -76,15 +127,24 @@ class Cache
|
||||
*/
|
||||
public static function set($key, $value, $ttl = 300)
|
||||
{
|
||||
// Try Redis first
|
||||
$redis = self::redis();
|
||||
if (!$redis) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($redis) {
|
||||
try {
|
||||
$redis->setex(self::PREFIX . $key, $ttl, json_encode($value));
|
||||
return;
|
||||
} catch (\Exception $e) {
|
||||
// Silently fail — caching is optional
|
||||
// Fall through to file cache
|
||||
}
|
||||
}
|
||||
|
||||
// File cache fallback with atomic write (race condition safe)
|
||||
$path = self::filePath($key);
|
||||
$tmp = $path . '.' . getmypid() . '.tmp';
|
||||
$entry = json_encode(['expires' => time() + $ttl, 'data' => $value]);
|
||||
|
||||
if (@file_put_contents($tmp, $entry, LOCK_EX) !== false) {
|
||||
@rename($tmp, $path);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,14 +156,17 @@ class Cache
|
||||
public static function forget($key)
|
||||
{
|
||||
$redis = self::redis();
|
||||
if (!$redis) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($redis) {
|
||||
try {
|
||||
$redis->del(self::PREFIX . $key);
|
||||
} catch (\Exception $e) {
|
||||
// Silently fail
|
||||
// Continue to file cleanup
|
||||
}
|
||||
}
|
||||
|
||||
$path = self::filePath($key);
|
||||
if (file_exists($path)) {
|
||||
@unlink($path);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -115,17 +178,19 @@ class Cache
|
||||
public static function forgetPattern($pattern)
|
||||
{
|
||||
$redis = self::redis();
|
||||
if (!$redis) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($redis) {
|
||||
try {
|
||||
$keys = $redis->keys(self::PREFIX . $pattern);
|
||||
if (!empty($keys)) {
|
||||
$redis->del($keys);
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
// Silently fail
|
||||
// Continue to file cleanup
|
||||
}
|
||||
}
|
||||
|
||||
// File cache: can only clear all files for pattern matches
|
||||
// Since file names are md5 hashed, we can't match patterns.
|
||||
// For non-Redis, TTL expiry handles cleanup naturally.
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,31 +56,49 @@ class Module
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve service context: system service, WHMCS service, control panel, and curl client.
|
||||
* Returns false if any lookup fails.
|
||||
*
|
||||
* @param int $serviceID
|
||||
* @return false|string
|
||||
* @return array{service: object, whmcsService: object, cp: array, request: Curl}|false
|
||||
*/
|
||||
public function fetchLoginTokens($serviceID)
|
||||
protected function resolveServiceContext($serviceID)
|
||||
{
|
||||
$serviceID = (int) $serviceID;
|
||||
$service = Database::getSystemService($serviceID);
|
||||
if (!$service) return false;
|
||||
|
||||
if ($service) {
|
||||
$whmcsService = Database::getWhmcsService($serviceID);
|
||||
if (!$whmcsService) return false;
|
||||
|
||||
$cp = $this->getCP($whmcsService->server);
|
||||
if (!$cp) return false;
|
||||
|
||||
$request = $this->initCurl($cp['token']);
|
||||
$data = $request->post($cp['url'] . '/users/' . (int) $whmcsService->userid . '/serverAuthenticationTokens/' . (int) $service->server_id);
|
||||
return [
|
||||
'service' => $service,
|
||||
'whmcsService' => $whmcsService,
|
||||
'cp' => $cp,
|
||||
'request' => $this->initCurl($cp['token']),
|
||||
'serverId' => (int) $service->server_id,
|
||||
];
|
||||
}
|
||||
|
||||
Log::insert(__FUNCTION__, $request->getRequestInfo(), $data);
|
||||
/**
|
||||
* @param int $serviceID
|
||||
* @return false|string
|
||||
*/
|
||||
public function fetchLoginTokens($serviceID)
|
||||
{
|
||||
$ctx = $this->resolveServiceContext($serviceID);
|
||||
if (!$ctx) return false;
|
||||
|
||||
if ($request->getRequestInfo('http_code') == '200') {
|
||||
$data = $ctx['request']->post($ctx['cp']['url'] . '/users/' . (int) $ctx['whmcsService']->userid . '/serverAuthenticationTokens/' . $ctx['serverId']);
|
||||
Log::insert(__FUNCTION__, $ctx['request']->getRequestInfo(), $data);
|
||||
|
||||
if ($ctx['request']->getRequestInfo('http_code') == '200') {
|
||||
$data = json_decode($data);
|
||||
if (isset($data->data->authentication->endpoint_complete)) {
|
||||
return $cp['base_url'] . $data->data->authentication->endpoint_complete;
|
||||
}
|
||||
return $ctx['cp']['base_url'] . $data->data->authentication->endpoint_complete;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
@@ -122,25 +140,15 @@ class Module
|
||||
|
||||
public function fetchServerData($serviceID)
|
||||
{
|
||||
$serviceID = (int) $serviceID;
|
||||
$service = Database::getSystemService($serviceID);
|
||||
$ctx = $this->resolveServiceContext($serviceID);
|
||||
if (!$ctx) return false;
|
||||
|
||||
if ($service) {
|
||||
$whmcsService = Database::getWhmcsService($serviceID);
|
||||
if (!$whmcsService) return false;
|
||||
$data = $ctx['request']->get($ctx['cp']['url'] . '/servers/' . $ctx['serverId']);
|
||||
Log::insert(__FUNCTION__, $ctx['request']->getRequestInfo(), $data);
|
||||
|
||||
$cp = $this->getCP($whmcsService->server);
|
||||
if (!$cp) return false;
|
||||
|
||||
$request = $this->initCurl($cp['token']);
|
||||
$data = $request->get($cp['url'] . '/servers/' . (int) $service->server_id);
|
||||
|
||||
Log::insert(__FUNCTION__, $request->getRequestInfo(), $data);
|
||||
|
||||
if ($request->getRequestInfo('http_code') == '200') {
|
||||
if ($ctx['request']->getRequestInfo('http_code') == '200') {
|
||||
return json_decode($data);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -153,31 +161,21 @@ class Module
|
||||
*/
|
||||
public function serverPowerAction($serviceID, $action)
|
||||
{
|
||||
$serviceID = (int) $serviceID;
|
||||
$allowedActions = ['boot', 'shutdown', 'restart', 'poweroff'];
|
||||
if (!in_array($action, $allowedActions, true)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$service = Database::getSystemService($serviceID);
|
||||
$ctx = $this->resolveServiceContext($serviceID);
|
||||
if (!$ctx) return false;
|
||||
|
||||
if ($service) {
|
||||
$whmcsService = Database::getWhmcsService($serviceID);
|
||||
if (!$whmcsService) return false;
|
||||
$data = $ctx['request']->post($ctx['cp']['url'] . '/servers/' . $ctx['serverId'] . '/power/' . $action);
|
||||
Log::insert(__FUNCTION__ . ':' . $action, $ctx['request']->getRequestInfo(), $data);
|
||||
|
||||
$cp = $this->getCP($whmcsService->server);
|
||||
if (!$cp) return false;
|
||||
|
||||
$request = $this->initCurl($cp['token']);
|
||||
$data = $request->post($cp['url'] . '/servers/' . (int) $service->server_id . '/power/' . $action);
|
||||
|
||||
Log::insert(__FUNCTION__ . ':' . $action, $request->getRequestInfo(), $data);
|
||||
|
||||
$httpCode = $request->getRequestInfo('http_code');
|
||||
$httpCode = $ctx['request']->getRequestInfo('http_code');
|
||||
if ($httpCode == 200 || $httpCode == 204) {
|
||||
return json_decode($data) ?: (object) ['success' => true];
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -191,44 +189,26 @@ class Module
|
||||
*/
|
||||
public function rebuildServer($serviceID, $osId, $hostname = null)
|
||||
{
|
||||
$serviceID = (int) $serviceID;
|
||||
$osId = (int) $osId;
|
||||
if ($osId <= 0) return false;
|
||||
|
||||
if ($osId <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$service = Database::getSystemService($serviceID);
|
||||
|
||||
if ($service) {
|
||||
$whmcsService = Database::getWhmcsService($serviceID);
|
||||
if (!$whmcsService) return false;
|
||||
|
||||
$cp = $this->getCP($whmcsService->server);
|
||||
if (!$cp) return false;
|
||||
|
||||
$request = $this->initCurl($cp['token']);
|
||||
|
||||
$buildData = [
|
||||
'operatingSystemId' => $osId,
|
||||
'email' => true,
|
||||
];
|
||||
$ctx = $this->resolveServiceContext($serviceID);
|
||||
if (!$ctx) return false;
|
||||
|
||||
$buildData = ['operatingSystemId' => $osId, 'email' => true];
|
||||
if ($hostname !== null && $hostname !== '') {
|
||||
$buildData['hostname'] = $hostname;
|
||||
}
|
||||
|
||||
$request->addOption(CURLOPT_POSTFIELDS, json_encode($buildData));
|
||||
$data = $request->post($cp['url'] . '/servers/' . (int) $service->server_id . '/build');
|
||||
$ctx['request']->addOption(CURLOPT_POSTFIELDS, json_encode($buildData));
|
||||
$data = $ctx['request']->post($ctx['cp']['url'] . '/servers/' . $ctx['serverId'] . '/build');
|
||||
Log::insert(__FUNCTION__, $ctx['request']->getRequestInfo(), $data);
|
||||
|
||||
Log::insert(__FUNCTION__, $request->getRequestInfo(), $data);
|
||||
|
||||
$httpCode = $request->getRequestInfo('http_code');
|
||||
$httpCode = $ctx['request']->getRequestInfo('http_code');
|
||||
if ($httpCode == 200 || $httpCode == 201) {
|
||||
Cache::forgetPattern('backups:' . (int) $service->server_id);
|
||||
Cache::forgetPattern('backups:' . $ctx['serverId']);
|
||||
return json_decode($data) ?: (object) ['success' => true];
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -241,34 +221,19 @@ class Module
|
||||
*/
|
||||
public function renameServer($serviceID, $newName)
|
||||
{
|
||||
$serviceID = (int) $serviceID;
|
||||
$newName = trim($newName);
|
||||
if (empty($newName) || strlen($newName) > 255) return false;
|
||||
|
||||
if (empty($newName) || strlen($newName) > 255) {
|
||||
return false;
|
||||
}
|
||||
$ctx = $this->resolveServiceContext($serviceID);
|
||||
if (!$ctx) return false;
|
||||
|
||||
$service = Database::getSystemService($serviceID);
|
||||
$ctx['request']->addOption(CURLOPT_POSTFIELDS, json_encode(['name' => $newName]));
|
||||
$data = $ctx['request']->patch($ctx['cp']['url'] . '/servers/' . $ctx['serverId'] . '/name');
|
||||
Log::insert(__FUNCTION__, $ctx['request']->getRequestInfo(), $data);
|
||||
|
||||
if ($service) {
|
||||
$whmcsService = Database::getWhmcsService($serviceID);
|
||||
if (!$whmcsService) return false;
|
||||
|
||||
$cp = $this->getCP($whmcsService->server);
|
||||
if (!$cp) return false;
|
||||
|
||||
$request = $this->initCurl($cp['token']);
|
||||
|
||||
$request->addOption(CURLOPT_POSTFIELDS, json_encode(['name' => $newName]));
|
||||
$data = $request->patch($cp['url'] . '/servers/' . (int) $service->server_id . '/name');
|
||||
|
||||
Log::insert(__FUNCTION__, $request->getRequestInfo(), $data);
|
||||
|
||||
$httpCode = $request->getRequestInfo('http_code');
|
||||
$httpCode = $ctx['request']->getRequestInfo('http_code');
|
||||
return ($httpCode == 200 || $httpCode == 204);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch available OS templates for a server's package.
|
||||
@@ -278,51 +243,60 @@ class Module
|
||||
*/
|
||||
public function fetchOsTemplates($serviceID)
|
||||
{
|
||||
$serviceID = (int) $serviceID;
|
||||
$service = Database::getSystemService($serviceID);
|
||||
$ctx = $this->resolveServiceContext($serviceID);
|
||||
if (!$ctx) return false;
|
||||
|
||||
if ($service) {
|
||||
$whmcsService = Database::getWhmcsService($serviceID);
|
||||
if (!$whmcsService) return false;
|
||||
|
||||
$cp = $this->getCP($whmcsService->server);
|
||||
if (!$cp) return false;
|
||||
|
||||
$product = \WHMCS\Database\Capsule::table('tblproducts')->where('id', $whmcsService->packageid)->first();
|
||||
if (!$product || !$product->configoption2) {
|
||||
return false;
|
||||
}
|
||||
$product = \WHMCS\Database\Capsule::table('tblproducts')->where('id', $ctx['whmcsService']->packageid)->first();
|
||||
if (!$product || !$product->configoption2) return false;
|
||||
|
||||
$cacheKey = 'os:' . (int) $product->configoption2;
|
||||
$cached = Cache::get($cacheKey);
|
||||
if ($cached !== null) {
|
||||
return $cached;
|
||||
if ($cached !== null) return $cached;
|
||||
|
||||
$data = $ctx['request']->get($ctx['cp']['url'] . '/media/templates/fromServerPackageSpec/' . (int) $product->configoption2);
|
||||
Log::insert(__FUNCTION__, $ctx['request']->getRequestInfo(), $data);
|
||||
|
||||
if ($ctx['request']->getRequestInfo('http_code') == '200') {
|
||||
$templates = json_decode($data, true);
|
||||
$baseUrl = rtrim(str_replace('/api/v1', '', $ctx['cp']['url']), '/');
|
||||
|
||||
$result = [
|
||||
'baseUrl' => $baseUrl,
|
||||
'categories' => self::groupOsTemplates($templates['data'] ?? []),
|
||||
];
|
||||
|
||||
Cache::set($cacheKey, $result, 600);
|
||||
return $result;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
$request = $this->initCurl($cp['token']);
|
||||
$data = $request->get($cp['url'] . '/media/templates/fromServerPackageSpec/' . (int) $product->configoption2);
|
||||
|
||||
Log::insert(__FUNCTION__, $request->getRequestInfo(), $data);
|
||||
|
||||
if ($request->getRequestInfo('http_code') == '200') {
|
||||
$templates = json_decode($data, true);
|
||||
$baseUrl = rtrim(str_replace('/api/v1', '', $cp['url']), '/');
|
||||
/**
|
||||
* Group OS template data into categories. Categories with only 1 template
|
||||
* are merged into an "Other" category.
|
||||
*
|
||||
* @param array $data Raw template data from VirtFusion API
|
||||
* @param bool $htmlEscape Whether to escape names for HTML output
|
||||
* @return array
|
||||
*/
|
||||
public static function groupOsTemplates(array $data, bool $htmlEscape = false): array
|
||||
{
|
||||
$categories = [];
|
||||
$otherTemplates = [];
|
||||
$esc = fn($v) => $htmlEscape ? htmlspecialchars($v, ENT_QUOTES, 'UTF-8') : $v;
|
||||
|
||||
if (isset($templates['data'])) {
|
||||
foreach ($templates['data'] as $osCategory) {
|
||||
foreach ($data as $osCategory) {
|
||||
$catTemplates = [];
|
||||
foreach ($osCategory['templates'] as $template) {
|
||||
$catTemplates[] = [
|
||||
'id' => $template['id'],
|
||||
'name' => $template['name'],
|
||||
'version' => $template['version'] ?? '',
|
||||
'variant' => $template['variant'] ?? '',
|
||||
'name' => $esc($template['name']),
|
||||
'version' => $esc($template['version'] ?? ''),
|
||||
'variant' => $esc($template['variant'] ?? ''),
|
||||
'icon' => $template['icon'] ?? null,
|
||||
'eol' => $template['eol'] ?? false,
|
||||
'type' => $template['type'] ?? '',
|
||||
'description' => $template['description'] ?? '',
|
||||
'description' => $esc($template['description'] ?? ''),
|
||||
];
|
||||
}
|
||||
|
||||
@@ -330,7 +304,7 @@ class Module
|
||||
$otherTemplates = array_merge($otherTemplates, $catTemplates);
|
||||
} else {
|
||||
$categories[] = [
|
||||
'name' => $osCategory['name'] ?? 'Unknown',
|
||||
'name' => $esc($osCategory['name'] ?? 'Unknown'),
|
||||
'icon' => $osCategory['icon'] ?? null,
|
||||
'templates' => $catTemplates,
|
||||
];
|
||||
@@ -338,24 +312,10 @@ class Module
|
||||
}
|
||||
|
||||
if (!empty($otherTemplates)) {
|
||||
$categories[] = [
|
||||
'name' => 'Other',
|
||||
'icon' => null,
|
||||
'templates' => $otherTemplates,
|
||||
];
|
||||
}
|
||||
$categories[] = ['name' => 'Other', 'icon' => null, 'templates' => $otherTemplates];
|
||||
}
|
||||
|
||||
$result = [
|
||||
'baseUrl' => $baseUrl,
|
||||
'categories' => $categories,
|
||||
];
|
||||
|
||||
Cache::set($cacheKey, $result, 600);
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return $categories;
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
@@ -370,33 +330,21 @@ class Module
|
||||
*/
|
||||
public function getTrafficStats($serviceID)
|
||||
{
|
||||
$serviceID = (int) $serviceID;
|
||||
$service = Database::getSystemService($serviceID);
|
||||
$ctx = $this->resolveServiceContext($serviceID);
|
||||
if (!$ctx) return false;
|
||||
|
||||
if ($service) {
|
||||
$cacheKey = 'traffic:' . (int) $service->server_id;
|
||||
$cacheKey = 'traffic:' . $ctx['serverId'];
|
||||
$cached = Cache::get($cacheKey);
|
||||
if ($cached !== null) {
|
||||
return $cached;
|
||||
}
|
||||
if ($cached !== null) return $cached;
|
||||
|
||||
$whmcsService = Database::getWhmcsService($serviceID);
|
||||
if (!$whmcsService) return false;
|
||||
$data = $ctx['request']->get($ctx['cp']['url'] . '/servers/' . $ctx['serverId'] . '/traffic');
|
||||
Log::insert(__FUNCTION__, $ctx['request']->getRequestInfo(), $data);
|
||||
|
||||
$cp = $this->getCP($whmcsService->server);
|
||||
if (!$cp) return false;
|
||||
|
||||
$request = $this->initCurl($cp['token']);
|
||||
$data = $request->get($cp['url'] . '/servers/' . (int) $service->server_id . '/traffic');
|
||||
|
||||
Log::insert(__FUNCTION__, $request->getRequestInfo(), $data);
|
||||
|
||||
if ($request->getRequestInfo('http_code') == 200) {
|
||||
if ($ctx['request']->getRequestInfo('http_code') == 200) {
|
||||
$result = json_decode($data, true);
|
||||
Cache::set($cacheKey, $result, 120);
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -412,26 +360,16 @@ class Module
|
||||
*/
|
||||
public function addIPv4($serviceID)
|
||||
{
|
||||
$serviceID = (int) $serviceID;
|
||||
$service = Database::getSystemService($serviceID);
|
||||
$ctx = $this->resolveServiceContext($serviceID);
|
||||
if (!$ctx) return false;
|
||||
|
||||
if ($service) {
|
||||
$whmcsService = Database::getWhmcsService($serviceID);
|
||||
if (!$whmcsService) return false;
|
||||
$data = $ctx['request']->post($ctx['cp']['url'] . '/servers/' . $ctx['serverId'] . '/ipv4');
|
||||
Log::insert(__FUNCTION__, $ctx['request']->getRequestInfo(), $data);
|
||||
|
||||
$cp = $this->getCP($whmcsService->server);
|
||||
if (!$cp) return false;
|
||||
|
||||
$request = $this->initCurl($cp['token']);
|
||||
$data = $request->post($cp['url'] . '/servers/' . (int) $service->server_id . '/ipv4');
|
||||
|
||||
Log::insert(__FUNCTION__, $request->getRequestInfo(), $data);
|
||||
|
||||
$httpCode = $request->getRequestInfo('http_code');
|
||||
$httpCode = $ctx['request']->getRequestInfo('http_code');
|
||||
if ($httpCode == 200 || $httpCode == 201) {
|
||||
return json_decode($data) ?: (object) ['success' => true];
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -447,33 +385,21 @@ class Module
|
||||
*/
|
||||
public function getServerBackups($serviceID)
|
||||
{
|
||||
$serviceID = (int) $serviceID;
|
||||
$service = Database::getSystemService($serviceID);
|
||||
$ctx = $this->resolveServiceContext($serviceID);
|
||||
if (!$ctx) return false;
|
||||
|
||||
if ($service) {
|
||||
$cacheKey = 'backups:' . (int) $service->server_id;
|
||||
$cacheKey = 'backups:' . $ctx['serverId'];
|
||||
$cached = Cache::get($cacheKey);
|
||||
if ($cached !== null) {
|
||||
return $cached;
|
||||
}
|
||||
if ($cached !== null) return $cached;
|
||||
|
||||
$whmcsService = Database::getWhmcsService($serviceID);
|
||||
if (!$whmcsService) return false;
|
||||
$data = $ctx['request']->get($ctx['cp']['url'] . '/backups/server/' . $ctx['serverId']);
|
||||
Log::insert(__FUNCTION__, $ctx['request']->getRequestInfo(), $data);
|
||||
|
||||
$cp = $this->getCP($whmcsService->server);
|
||||
if (!$cp) return false;
|
||||
|
||||
$request = $this->initCurl($cp['token']);
|
||||
$data = $request->get($cp['url'] . '/backups/server/' . (int) $service->server_id);
|
||||
|
||||
Log::insert(__FUNCTION__, $request->getRequestInfo(), $data);
|
||||
|
||||
if ($request->getRequestInfo('http_code') == 200) {
|
||||
if ($ctx['request']->getRequestInfo('http_code') == 200) {
|
||||
$result = json_decode($data, true);
|
||||
Cache::set($cacheKey, $result, 120);
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -486,34 +412,19 @@ class Module
|
||||
*/
|
||||
public function assignBackupPlan($serviceID, $planId)
|
||||
{
|
||||
$serviceID = (int) $serviceID;
|
||||
$planId = (int) $planId;
|
||||
$ctx = $this->resolveServiceContext($serviceID);
|
||||
if (!$ctx) return false;
|
||||
|
||||
$service = Database::getSystemService($serviceID);
|
||||
$ctx['request']->addOption(CURLOPT_POSTFIELDS, json_encode(['planId' => $planId]));
|
||||
$endpoint = $ctx['cp']['url'] . '/servers/' . $ctx['serverId'] . '/backup/plan';
|
||||
$data = $planId > 0 ? $ctx['request']->post($endpoint) : $ctx['request']->delete($endpoint);
|
||||
Log::insert(__FUNCTION__, $ctx['request']->getRequestInfo(), $data);
|
||||
|
||||
if ($service) {
|
||||
$whmcsService = Database::getWhmcsService($serviceID);
|
||||
if (!$whmcsService) return false;
|
||||
|
||||
$cp = $this->getCP($whmcsService->server);
|
||||
if (!$cp) return false;
|
||||
|
||||
$request = $this->initCurl($cp['token']);
|
||||
$request->addOption(CURLOPT_POSTFIELDS, json_encode(['planId' => $planId]));
|
||||
|
||||
if ($planId > 0) {
|
||||
$data = $request->post($cp['url'] . '/servers/' . (int) $service->server_id . '/backup/plan');
|
||||
} else {
|
||||
$data = $request->delete($cp['url'] . '/servers/' . (int) $service->server_id . '/backup/plan');
|
||||
}
|
||||
|
||||
Log::insert(__FUNCTION__, $request->getRequestInfo(), $data);
|
||||
|
||||
$httpCode = $request->getRequestInfo('http_code');
|
||||
$httpCode = $ctx['request']->getRequestInfo('http_code');
|
||||
if ($httpCode == 200 || $httpCode == 204) {
|
||||
return json_decode($data) ?: (object) ['success' => true];
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -529,25 +440,15 @@ class Module
|
||||
*/
|
||||
public function getVncConsole($serviceID)
|
||||
{
|
||||
$serviceID = (int) $serviceID;
|
||||
$service = Database::getSystemService($serviceID);
|
||||
$ctx = $this->resolveServiceContext($serviceID);
|
||||
if (!$ctx) return false;
|
||||
|
||||
if ($service) {
|
||||
$whmcsService = Database::getWhmcsService($serviceID);
|
||||
if (!$whmcsService) return false;
|
||||
$data = $ctx['request']->get($ctx['cp']['url'] . '/servers/' . $ctx['serverId'] . '/vnc');
|
||||
Log::insert(__FUNCTION__, $ctx['request']->getRequestInfo(), $data);
|
||||
|
||||
$cp = $this->getCP($whmcsService->server);
|
||||
if (!$cp) return false;
|
||||
|
||||
$request = $this->initCurl($cp['token']);
|
||||
$data = $request->get($cp['url'] . '/servers/' . (int) $service->server_id . '/vnc');
|
||||
|
||||
Log::insert(__FUNCTION__, $request->getRequestInfo(), $data);
|
||||
|
||||
if ($request->getRequestInfo('http_code') == 200) {
|
||||
if ($ctx['request']->getRequestInfo('http_code') == 200) {
|
||||
return json_decode($data, true);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -560,27 +461,17 @@ class Module
|
||||
*/
|
||||
public function toggleVnc($serviceID, $enabled)
|
||||
{
|
||||
$serviceID = (int) $serviceID;
|
||||
$service = Database::getSystemService($serviceID);
|
||||
$ctx = $this->resolveServiceContext($serviceID);
|
||||
if (!$ctx) return false;
|
||||
|
||||
if ($service) {
|
||||
$whmcsService = Database::getWhmcsService($serviceID);
|
||||
if (!$whmcsService) return false;
|
||||
$ctx['request']->addOption(CURLOPT_POSTFIELDS, json_encode(['enabled' => (bool) $enabled]));
|
||||
$data = $ctx['request']->post($ctx['cp']['url'] . '/servers/' . $ctx['serverId'] . '/vnc');
|
||||
Log::insert(__FUNCTION__, $ctx['request']->getRequestInfo(), $data);
|
||||
|
||||
$cp = $this->getCP($whmcsService->server);
|
||||
if (!$cp) return false;
|
||||
|
||||
$request = $this->initCurl($cp['token']);
|
||||
$request->addOption(CURLOPT_POSTFIELDS, json_encode(['enabled' => (bool) $enabled]));
|
||||
$data = $request->post($cp['url'] . '/servers/' . (int) $service->server_id . '/vnc');
|
||||
|
||||
Log::insert(__FUNCTION__, $request->getRequestInfo(), $data);
|
||||
|
||||
$httpCode = $request->getRequestInfo('http_code');
|
||||
$httpCode = $ctx['request']->getRequestInfo('http_code');
|
||||
if ($httpCode == 200 || $httpCode == 204) {
|
||||
return json_decode($data, true) ?: ['success' => true];
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -598,37 +489,23 @@ class Module
|
||||
*/
|
||||
public function modifyResource($serviceID, $resource, $value)
|
||||
{
|
||||
$serviceID = (int) $serviceID;
|
||||
$allowedResources = ['memory', 'cpuCores', 'traffic'];
|
||||
if (!in_array($resource, $allowedResources, true)) {
|
||||
return false;
|
||||
}
|
||||
if (!in_array($resource, $allowedResources, true)) return false;
|
||||
|
||||
$value = (int) $value;
|
||||
if ($value < 0) {
|
||||
return false;
|
||||
}
|
||||
if ($value < 0) return false;
|
||||
|
||||
$service = Database::getSystemService($serviceID);
|
||||
$ctx = $this->resolveServiceContext($serviceID);
|
||||
if (!$ctx) return false;
|
||||
|
||||
if ($service) {
|
||||
$whmcsService = Database::getWhmcsService($serviceID);
|
||||
if (!$whmcsService) return false;
|
||||
$ctx['request']->addOption(CURLOPT_POSTFIELDS, json_encode([$resource => $value]));
|
||||
$data = $ctx['request']->put($ctx['cp']['url'] . '/servers/' . $ctx['serverId'] . '/modify/' . $resource);
|
||||
Log::insert(__FUNCTION__ . ':' . $resource, $ctx['request']->getRequestInfo(), $data);
|
||||
|
||||
$cp = $this->getCP($whmcsService->server);
|
||||
if (!$cp) return false;
|
||||
|
||||
$request = $this->initCurl($cp['token']);
|
||||
$request->addOption(CURLOPT_POSTFIELDS, json_encode([$resource => $value]));
|
||||
$data = $request->put($cp['url'] . '/servers/' . (int) $service->server_id . '/modify/' . $resource);
|
||||
|
||||
Log::insert(__FUNCTION__ . ':' . $resource, $request->getRequestInfo(), $data);
|
||||
|
||||
$httpCode = $request->getRequestInfo('http_code');
|
||||
$httpCode = $ctx['request']->getRequestInfo('http_code');
|
||||
if ($httpCode == 200 || $httpCode == 204) {
|
||||
return json_decode($data) ?: (object) ['success' => true];
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -683,51 +560,31 @@ class Module
|
||||
*/
|
||||
public function resetServerPassword($serviceID)
|
||||
{
|
||||
$serviceID = (int) $serviceID;
|
||||
$service = Database::getSystemService($serviceID);
|
||||
$ctx = $this->resolveServiceContext($serviceID);
|
||||
if (!$ctx) return false;
|
||||
|
||||
if ($service) {
|
||||
$whmcsService = Database::getWhmcsService($serviceID);
|
||||
if (!$whmcsService) return false;
|
||||
$data = $ctx['request']->post($ctx['cp']['url'] . '/servers/' . $ctx['serverId'] . '/resetPassword');
|
||||
Log::insert(__FUNCTION__, $ctx['request']->getRequestInfo(), $data);
|
||||
|
||||
$cp = $this->getCP($whmcsService->server);
|
||||
if (!$cp) return false;
|
||||
|
||||
$request = $this->initCurl($cp['token']);
|
||||
$data = $request->post($cp['url'] . '/servers/' . (int) $service->server_id . '/resetPassword');
|
||||
|
||||
Log::insert(__FUNCTION__, $request->getRequestInfo(), $data);
|
||||
|
||||
$httpCode = $request->getRequestInfo('http_code');
|
||||
$httpCode = $ctx['request']->getRequestInfo('http_code');
|
||||
if ($httpCode == 200 || $httpCode == 201) {
|
||||
return json_decode($data, true);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public function resetUserPassword($serviceID, $clientID)
|
||||
{
|
||||
$serviceID = (int) $serviceID;
|
||||
$clientID = (int) $clientID;
|
||||
$service = Database::getSystemService($serviceID);
|
||||
$ctx = $this->resolveServiceContext($serviceID);
|
||||
if (!$ctx) return false;
|
||||
|
||||
if ($service) {
|
||||
$whmcsService = Database::getWhmcsService($serviceID);
|
||||
if (!$whmcsService) return false;
|
||||
$data = $ctx['request']->post($ctx['cp']['url'] . '/users/' . $clientID . '/byExtRelation/resetPassword');
|
||||
Log::insert(__FUNCTION__, $ctx['request']->getRequestInfo(), $data);
|
||||
|
||||
$cp = $this->getCP($whmcsService->server);
|
||||
if (!$cp) return false;
|
||||
|
||||
$request = $this->initCurl($cp['token']);
|
||||
$data = $request->post($cp['url'] . '/users/' . $clientID . '/byExtRelation/resetPassword');
|
||||
|
||||
Log::insert(__FUNCTION__, $request->getRequestInfo(), $data);
|
||||
|
||||
if ($request->getRequestInfo('http_code') == '201') {
|
||||
if ($ctx['request']->getRequestInfo('http_code') == '201') {
|
||||
return json_decode($data);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -14,6 +14,21 @@
|
||||
* - Server naming
|
||||
*/
|
||||
|
||||
// =========================================================================
|
||||
// Shared Helpers
|
||||
// =========================================================================
|
||||
|
||||
function vfUrl(systemUrl, serviceId, action, endpoint) {
|
||||
return (systemUrl || "") + "modules/servers/VirtFusionDirect/" + (endpoint || "client") + ".php?serviceID=" + encodeURIComponent(serviceId) + "&action=" + encodeURIComponent(action);
|
||||
}
|
||||
|
||||
function vfShowAlert(alertDiv, type, message) {
|
||||
alertDiv.removeClass("alert-danger alert-success alert-warning alert");
|
||||
alertDiv.addClass("alert alert-" + type);
|
||||
alertDiv.text(message);
|
||||
alertDiv.show();
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// Progress Indicator
|
||||
// =========================================================================
|
||||
@@ -45,7 +60,7 @@ function vfServerData(serviceId, systemUrl) {
|
||||
$.ajax({
|
||||
type: "GET",
|
||||
dataType: "json",
|
||||
url: systemUrl + "modules/servers/VirtFusionDirect/client.php?serviceID=" + encodeURIComponent(serviceId) + "&action=serverData"
|
||||
url: vfUrl(systemUrl, serviceId, "serverData")
|
||||
}).done(function (response) {
|
||||
if (response.success) {
|
||||
$("#vf-rename-input").val(response.data.name);
|
||||
@@ -162,7 +177,7 @@ function vfServerDataAdmin(serviceId, systemUrl) {
|
||||
$.ajax({
|
||||
type: "GET",
|
||||
dataType: "json",
|
||||
url: systemUrl + "modules/servers/VirtFusionDirect/admin.php?serviceID=" + encodeURIComponent(serviceId) + "&action=serverData"
|
||||
url: vfUrl(systemUrl, serviceId, "serverData", "admin")
|
||||
}).done(function (response) {
|
||||
if (response.success) {
|
||||
$("#vf-data-server-name").text(response.data.name);
|
||||
@@ -194,7 +209,7 @@ function vfUserPasswordReset(serviceId, systemUrl) {
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
dataType: "json",
|
||||
url: systemUrl + "modules/servers/VirtFusionDirect/client.php?serviceID=" + encodeURIComponent(serviceId) + "&action=resetPassword"
|
||||
url: vfUrl(systemUrl, serviceId, "resetPassword")
|
||||
}).done(function (response) {
|
||||
if (response.success) {
|
||||
$("#vf-password-reset-success").show();
|
||||
@@ -218,7 +233,7 @@ function vfLoginAsServerOwner(serviceId, systemUrl, newWindow) {
|
||||
$.ajax({
|
||||
type: "GET",
|
||||
dataType: "json",
|
||||
url: systemUrl + "modules/servers/VirtFusionDirect/client.php?serviceID=" + encodeURIComponent(serviceId) + "&action=loginAsServerOwner"
|
||||
url: vfUrl(systemUrl, serviceId, "loginAsServerOwner")
|
||||
}).done(function (response) {
|
||||
if (response.success && response.token_url) {
|
||||
if (newWindow) {
|
||||
@@ -267,21 +282,17 @@ function vfPowerAction(serviceId, systemUrl, action) {
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
dataType: "json",
|
||||
url: systemUrl + "modules/servers/VirtFusionDirect/client.php?serviceID=" + encodeURIComponent(serviceId) + "&action=powerAction",
|
||||
url: vfUrl(systemUrl, serviceId, "powerAction"),
|
||||
data: { powerAction: action }
|
||||
}).done(function (response) {
|
||||
if (response.success) {
|
||||
alertDiv.removeClass("alert-danger").addClass("alert-success");
|
||||
alertDiv.text(response.data.message || (actionLabels[action] + " server..."));
|
||||
vfShowAlert(alertDiv, "success",response.data.message || (actionLabels[action] + " server..."));
|
||||
} else {
|
||||
alertDiv.removeClass("alert-success").addClass("alert-danger");
|
||||
alertDiv.text("Power action failed. Please try again.");
|
||||
vfShowAlert(alertDiv, "danger","Power action failed. Please try again.");
|
||||
}
|
||||
alertDiv.show();
|
||||
}).fail(function () {
|
||||
alertDiv.removeClass("alert-success").addClass("alert-danger");
|
||||
alertDiv.text("An error occurred. Please try again.");
|
||||
alertDiv.show();
|
||||
vfShowAlert(alertDiv, "danger","An error occurred. Please try again.");
|
||||
}).always(function () {
|
||||
spinner.hide();
|
||||
// Cooldown: keep buttons disabled for 3 seconds
|
||||
@@ -390,7 +401,7 @@ function vfLoadOsTemplates(serviceId, systemUrl) {
|
||||
$.ajax({
|
||||
type: "GET",
|
||||
dataType: "json",
|
||||
url: systemUrl + "modules/servers/VirtFusionDirect/client.php?serviceID=" + encodeURIComponent(serviceId) + "&action=osTemplates"
|
||||
url: vfUrl(systemUrl, serviceId, "osTemplates")
|
||||
}).done(function (response) {
|
||||
$("#vf-os-gallery-loader").hide();
|
||||
if (response.success && response.data) {
|
||||
@@ -422,9 +433,7 @@ function vfRebuildServer(serviceId, systemUrl) {
|
||||
var alertDiv = $("#vf-rebuild-alert");
|
||||
|
||||
if (!osId) {
|
||||
alertDiv.removeClass("alert-success").addClass("alert-danger");
|
||||
alertDiv.text("Please select an operating system.");
|
||||
alertDiv.show();
|
||||
vfShowAlert(alertDiv, "danger","Please select an operating system.");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -440,21 +449,17 @@ function vfRebuildServer(serviceId, systemUrl) {
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
dataType: "json",
|
||||
url: systemUrl + "modules/servers/VirtFusionDirect/client.php?serviceID=" + encodeURIComponent(serviceId) + "&action=rebuild",
|
||||
url: vfUrl(systemUrl, serviceId, "rebuild"),
|
||||
data: { osId: osId }
|
||||
}).done(function (response) {
|
||||
if (response.success) {
|
||||
alertDiv.removeClass("alert-danger").addClass("alert-success");
|
||||
alertDiv.text(response.data.message || "Server rebuild initiated. You will receive an email when the process is complete.");
|
||||
vfShowAlert(alertDiv, "success",response.data.message || "Server rebuild initiated. You will receive an email when the process is complete.");
|
||||
} else {
|
||||
alertDiv.removeClass("alert-success").addClass("alert-danger");
|
||||
alertDiv.text("Rebuild failed. Please try again.");
|
||||
vfShowAlert(alertDiv, "danger","Rebuild failed. Please try again.");
|
||||
}
|
||||
alertDiv.show();
|
||||
}).fail(function () {
|
||||
alertDiv.removeClass("alert-success").addClass("alert-danger");
|
||||
alertDiv.text("An error occurred. Please try again.");
|
||||
alertDiv.show();
|
||||
vfShowAlert(alertDiv, "danger","An error occurred. Please try again.");
|
||||
}).always(function () {
|
||||
vfHideProgress();
|
||||
$("#vf-rebuild-spinner").hide();
|
||||
@@ -469,7 +474,7 @@ function impersonateServerOwner(serviceId, systemUrl) {
|
||||
$.ajax({
|
||||
type: "GET",
|
||||
dataType: "json",
|
||||
url: systemUrl + "modules/servers/VirtFusionDirect/admin.php?serviceID=" + encodeURIComponent(serviceId) + "&action=impersonateServerOwner"
|
||||
url: vfUrl(systemUrl, serviceId, "impersonateServerOwner", "admin")
|
||||
}).done(function (response) {
|
||||
if (response.success && response.user) {
|
||||
window.open(response.url + "/_imp/in/" + response.user.id + "/-");
|
||||
@@ -493,9 +498,7 @@ function vfOpenVnc(serviceId, systemUrl) {
|
||||
// Open window immediately in click context to avoid popup blockers
|
||||
var vncWindow = window.open("", "_blank");
|
||||
if (!vncWindow) {
|
||||
alertDiv.removeClass("alert-success").addClass("alert-danger");
|
||||
alertDiv.text("Popup blocked. Please allow popups for this site and try again.");
|
||||
alertDiv.show();
|
||||
vfShowAlert(alertDiv, "danger","Popup blocked. Please allow popups for this site and try again.");
|
||||
spinner.hide();
|
||||
btn.prop("disabled", false);
|
||||
return;
|
||||
@@ -504,7 +507,7 @@ function vfOpenVnc(serviceId, systemUrl) {
|
||||
$.ajax({
|
||||
type: "GET",
|
||||
dataType: "json",
|
||||
url: systemUrl + "modules/servers/VirtFusionDirect/client.php?serviceID=" + encodeURIComponent(serviceId) + "&action=vnc"
|
||||
url: vfUrl(systemUrl, serviceId, "vnc")
|
||||
}).done(function (response) {
|
||||
if (response.success && response.data) {
|
||||
var data = response.data.data || response.data;
|
||||
@@ -519,21 +522,15 @@ function vfOpenVnc(serviceId, systemUrl) {
|
||||
vncWindow.location.href = vncUrl;
|
||||
} else {
|
||||
vncWindow.close();
|
||||
alertDiv.removeClass("alert-danger").addClass("alert-success");
|
||||
alertDiv.text("VNC session is ready. Check your VirtFusion control panel for access.");
|
||||
alertDiv.show();
|
||||
vfShowAlert(alertDiv, "success","VNC session is ready. Check your VirtFusion control panel for access.");
|
||||
}
|
||||
} else {
|
||||
vncWindow.close();
|
||||
alertDiv.removeClass("alert-success").addClass("alert-danger");
|
||||
alertDiv.text("VNC console is not available.");
|
||||
alertDiv.show();
|
||||
vfShowAlert(alertDiv, "danger","VNC console is not available.");
|
||||
}
|
||||
}).fail(function () {
|
||||
vncWindow.close();
|
||||
alertDiv.removeClass("alert-success").addClass("alert-danger");
|
||||
alertDiv.text("An error occurred. The server may be powered off.");
|
||||
alertDiv.show();
|
||||
vfShowAlert(alertDiv, "danger","An error occurred. The server may be powered off.");
|
||||
}).always(function () {
|
||||
spinner.hide();
|
||||
btn.prop("disabled", false);
|
||||
@@ -547,7 +544,7 @@ function vfToggleVnc(serviceId, systemUrl, enabled) {
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
dataType: "json",
|
||||
url: systemUrl + "modules/servers/VirtFusionDirect/client.php?serviceID=" + encodeURIComponent(serviceId) + "&action=toggleVnc",
|
||||
url: vfUrl(systemUrl, serviceId, "toggleVnc"),
|
||||
data: { enabled: enabled ? "1" : "0" }
|
||||
}).done(function (response) {
|
||||
if (response.success) {
|
||||
@@ -577,7 +574,7 @@ function vfCopyVncPassword(serviceId, systemUrl) {
|
||||
$.ajax({
|
||||
type: "GET",
|
||||
dataType: "json",
|
||||
url: systemUrl + "modules/servers/VirtFusionDirect/client.php?serviceID=" + encodeURIComponent(serviceId) + "&action=vnc"
|
||||
url: vfUrl(systemUrl, serviceId, "vnc")
|
||||
}).done(function (response) {
|
||||
if (response.success && response.data) {
|
||||
var data = response.data.data || response.data;
|
||||
@@ -603,7 +600,7 @@ function vfLoadSelfServiceUsage(serviceId, systemUrl) {
|
||||
$.ajax({
|
||||
type: "GET",
|
||||
dataType: "json",
|
||||
url: systemUrl + "modules/servers/VirtFusionDirect/client.php?serviceID=" + encodeURIComponent(serviceId) + "&action=selfServiceUsage"
|
||||
url: vfUrl(systemUrl, serviceId, "selfServiceUsage")
|
||||
}).done(function (response) {
|
||||
if (response.success && response.data) {
|
||||
var data = response.data.data || response.data;
|
||||
@@ -646,7 +643,7 @@ function vfLoadSelfServiceReport(serviceId, systemUrl) {
|
||||
$.ajax({
|
||||
type: "GET",
|
||||
dataType: "json",
|
||||
url: systemUrl + "modules/servers/VirtFusionDirect/client.php?serviceID=" + encodeURIComponent(serviceId) + "&action=selfServiceReport"
|
||||
url: vfUrl(systemUrl, serviceId, "selfServiceReport")
|
||||
}).done(function (response) {
|
||||
if (response.success && response.data) {
|
||||
var data = response.data.data || response.data;
|
||||
@@ -674,9 +671,7 @@ function vfAddCredit(serviceId, systemUrl) {
|
||||
var spinner = $("#vf-ss-add-credit-spinner");
|
||||
|
||||
if (!amount || parseFloat(amount) <= 0) {
|
||||
alertDiv.removeClass("alert-success").addClass("alert-danger");
|
||||
alertDiv.text("Please enter a valid positive amount.");
|
||||
alertDiv.show();
|
||||
vfShowAlert(alertDiv, "danger","Please enter a valid positive amount.");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -687,25 +682,19 @@ function vfAddCredit(serviceId, systemUrl) {
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
dataType: "json",
|
||||
url: systemUrl + "modules/servers/VirtFusionDirect/client.php?serviceID=" + encodeURIComponent(serviceId) + "&action=selfServiceAddCredit",
|
||||
url: vfUrl(systemUrl, serviceId, "selfServiceAddCredit"),
|
||||
data: { tokens: amount }
|
||||
}).done(function (response) {
|
||||
if (response.success) {
|
||||
alertDiv.removeClass("alert-danger").addClass("alert-success");
|
||||
alertDiv.text("Credit added successfully.");
|
||||
alertDiv.show();
|
||||
vfShowAlert(alertDiv, "success","Credit added successfully.");
|
||||
$("#vf-ss-credit-amount").val("");
|
||||
// Refresh usage data
|
||||
vfLoadSelfServiceUsage(serviceId, systemUrl);
|
||||
} else {
|
||||
alertDiv.removeClass("alert-success").addClass("alert-danger");
|
||||
alertDiv.text("Failed to add credit. Please try again.");
|
||||
alertDiv.show();
|
||||
vfShowAlert(alertDiv, "danger","Failed to add credit. Please try again.");
|
||||
}
|
||||
}).fail(function () {
|
||||
alertDiv.removeClass("alert-success").addClass("alert-danger");
|
||||
alertDiv.text("An error occurred. Please try again.");
|
||||
alertDiv.show();
|
||||
vfShowAlert(alertDiv, "danger","An error occurred. Please try again.");
|
||||
}).always(function () {
|
||||
spinner.hide();
|
||||
btn.prop("disabled", false);
|
||||
@@ -732,35 +721,25 @@ function vfResetServerPassword(serviceId, systemUrl) {
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
dataType: "json",
|
||||
url: systemUrl + "modules/servers/VirtFusionDirect/client.php?serviceID=" + encodeURIComponent(serviceId) + "&action=resetServerPassword"
|
||||
url: vfUrl(systemUrl, serviceId, "resetServerPassword")
|
||||
}).done(function (response) {
|
||||
if (response.success && response.data) {
|
||||
var data = response.data.data || response.data;
|
||||
var password = data.password || data.newPassword || "";
|
||||
if (password) {
|
||||
navigator.clipboard.writeText(password).then(function () {
|
||||
alertDiv.removeClass("alert-danger").addClass("alert alert-success");
|
||||
alertDiv.text("New password copied to clipboard.");
|
||||
alertDiv.show();
|
||||
vfShowAlert(alertDiv, "success","New password copied to clipboard.");
|
||||
}).catch(function () {
|
||||
alertDiv.removeClass("alert-danger").addClass("alert alert-warning");
|
||||
alertDiv.text("Password reset successful. Unable to copy to clipboard automatically.");
|
||||
alertDiv.show();
|
||||
vfShowAlert(alertDiv, "warning","Password reset successful. Unable to copy to clipboard automatically.");
|
||||
});
|
||||
} else {
|
||||
alertDiv.removeClass("alert-danger").addClass("alert alert-success");
|
||||
alertDiv.text("Password reset initiated. Check your email for the new credentials.");
|
||||
alertDiv.show();
|
||||
vfShowAlert(alertDiv, "success","Password reset initiated. Check your email for the new credentials.");
|
||||
}
|
||||
} else {
|
||||
alertDiv.removeClass("alert-success").addClass("alert alert-danger");
|
||||
alertDiv.text("Password reset failed. Please try again.");
|
||||
alertDiv.show();
|
||||
vfShowAlert(alertDiv, "danger","Password reset failed. Please try again.");
|
||||
}
|
||||
}).fail(function () {
|
||||
alertDiv.removeClass("alert-success").addClass("alert alert-danger");
|
||||
alertDiv.text("An error occurred. Please try again.");
|
||||
alertDiv.show();
|
||||
vfShowAlert(alertDiv, "danger","An error occurred. Please try again.");
|
||||
}).always(function () {
|
||||
spinner.hide();
|
||||
btn.prop("disabled", false);
|
||||
@@ -775,7 +754,7 @@ function vfLoadBackups(serviceId, systemUrl) {
|
||||
$.ajax({
|
||||
type: "GET",
|
||||
dataType: "json",
|
||||
url: systemUrl + "modules/servers/VirtFusionDirect/client.php?serviceID=" + encodeURIComponent(serviceId) + "&action=backups"
|
||||
url: vfUrl(systemUrl, serviceId, "backups")
|
||||
}).done(function (response) {
|
||||
if (response.success && response.data) {
|
||||
var backups = response.data.data || response.data;
|
||||
@@ -909,7 +888,7 @@ function vfLoadTrafficStats(serviceId, systemUrl) {
|
||||
$.ajax({
|
||||
type: "GET",
|
||||
dataType: "json",
|
||||
url: systemUrl + "modules/servers/VirtFusionDirect/client.php?serviceID=" + encodeURIComponent(serviceId) + "&action=trafficStats"
|
||||
url: vfUrl(systemUrl, serviceId, "trafficStats")
|
||||
}).done(function (response) {
|
||||
if (response.success && response.data) {
|
||||
var data = response.data.data || response.data;
|
||||
@@ -981,9 +960,7 @@ function vfRenameServer(serviceId, systemUrl) {
|
||||
alertDiv.hide();
|
||||
|
||||
if (!name || !/^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?$/.test(name)) {
|
||||
alertDiv.removeClass("alert-success").addClass("alert alert-danger");
|
||||
alertDiv.text("Invalid name. Use lowercase letters, numbers, and hyphens (2-63 chars, must start/end with alphanumeric).");
|
||||
alertDiv.show();
|
||||
vfShowAlert(alertDiv, "danger","Invalid name. Use lowercase letters, numbers, and hyphens (2-63 chars, must start/end with alphanumeric).");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -993,21 +970,17 @@ function vfRenameServer(serviceId, systemUrl) {
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
dataType: "json",
|
||||
url: systemUrl + "modules/servers/VirtFusionDirect/client.php?serviceID=" + encodeURIComponent(serviceId) + "&action=rename",
|
||||
url: vfUrl(systemUrl, serviceId, "rename"),
|
||||
data: { name: name }
|
||||
}).done(function (response) {
|
||||
if (response.success) {
|
||||
alertDiv.removeClass("alert-danger").addClass("alert alert-success");
|
||||
alertDiv.text("Server renamed successfully.");
|
||||
vfShowAlert(alertDiv, "success","Server renamed successfully.");
|
||||
} else {
|
||||
alertDiv.removeClass("alert-success").addClass("alert alert-danger");
|
||||
alertDiv.text("Rename failed. Please try again.");
|
||||
vfShowAlert(alertDiv, "danger","Rename failed. Please try again.");
|
||||
}
|
||||
alertDiv.show();
|
||||
}).fail(function () {
|
||||
alertDiv.removeClass("alert-success").addClass("alert alert-danger");
|
||||
alertDiv.text("An error occurred. Please try again.");
|
||||
alertDiv.show();
|
||||
vfShowAlert(alertDiv, "danger","An error occurred. Please try again.");
|
||||
}).always(function () {
|
||||
btn.prop("disabled", false);
|
||||
setTimeout(function () { alertDiv.fadeOut(); }, 3000);
|
||||
|
||||
Reference in New Issue
Block a user