527 lines
18 KiB
PHP
527 lines
18 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
if (!defined('WHMCS')) {
|
|
die('This file cannot be accessed directly');
|
|
}
|
|
|
|
require_once __DIR__ . '/lib/ApiClient.php';
|
|
require_once __DIR__ . '/lib/Idempotency.php';
|
|
require_once __DIR__ . '/lib/ServiceMetadata.php';
|
|
require_once __DIR__ . '/../../../lib/ProductMapping.php';
|
|
|
|
const HEXAGAMECLOUD_SERVER_VERSION = '1.1.0';
|
|
|
|
function hexagamecloud_MetaData(): array
|
|
{
|
|
return [
|
|
'DisplayName' => 'HexaHost GameCloud',
|
|
'APIVersion' => '1.1',
|
|
'RequiresServer' => true,
|
|
'DefaultNonSSLPort' => '443',
|
|
'DefaultSSLPort' => '443',
|
|
];
|
|
}
|
|
|
|
function hexagamecloud_ConfigOptions(): array
|
|
{
|
|
return [
|
|
'plan_slug' => [
|
|
'FriendlyName' => 'GameCloud Plan Slug',
|
|
'Type' => 'text',
|
|
'Size' => '32',
|
|
'Default' => 'free',
|
|
'Description' => 'Maps to a GameCloud plan slug',
|
|
],
|
|
'minecraft_version' => [
|
|
'FriendlyName' => 'Minecraft Version',
|
|
'Type' => 'text',
|
|
'Size' => '16',
|
|
'Default' => '1.21.1',
|
|
],
|
|
'software_family' => [
|
|
'FriendlyName' => 'Software Family',
|
|
'Type' => 'dropdown',
|
|
'Options' => 'VANILLA,PAPER,PURPUR,FABRIC,FORGE,NEOFORGE,QUILT',
|
|
'Default' => 'VANILLA',
|
|
],
|
|
];
|
|
}
|
|
|
|
function hexagamecloud_CreateAccount(array $params): string
|
|
{
|
|
try {
|
|
$client = new HexaGameCloudApiClient($params);
|
|
$externalClientId = (string) $params['clientsdetails']['userid'];
|
|
$externalServiceId = (string) $params['serviceid'];
|
|
|
|
$client->upsertClient($externalClientId, [
|
|
'email' => $params['clientsdetails']['email'],
|
|
'username' => $params['clientsdetails']['email'],
|
|
'displayName' => trim(($params['clientsdetails']['firstname'] ?? '') . ' ' . ($params['clientsdetails']['lastname'] ?? '')),
|
|
]);
|
|
|
|
$response = $client->provisionService([
|
|
'externalServiceId' => $externalServiceId,
|
|
'externalClientId' => $externalClientId,
|
|
'externalProductId' => (string) ($params['pid'] ?? ''),
|
|
'planSlug' => HexaGameCloudProductMapping::resolvePlanSlug($params),
|
|
'serverName' => $params['domain'] ?: ('server-' . $externalServiceId),
|
|
'minecraftVersion' => HexaGameCloudProductMapping::resolveMinecraftVersion($params),
|
|
'softwareFamily' => HexaGameCloudProductMapping::resolveSoftwareFamily($params),
|
|
'edition' => 'JAVA',
|
|
'eulaAccepted' => true,
|
|
]);
|
|
|
|
return 'success';
|
|
} catch (Throwable $exception) {
|
|
logModuleCall(
|
|
'hexagamecloud',
|
|
__FUNCTION__,
|
|
$params,
|
|
$exception->getMessage(),
|
|
$exception->getMessage(),
|
|
['password', 'secret', 'apiSecret']
|
|
);
|
|
return $exception->getMessage();
|
|
}
|
|
}
|
|
|
|
function hexagamecloud_SuspendAccount(array $params): string
|
|
{
|
|
return hexagamecloud_lifecycleAction($params, 'suspend', ['reason' => 'Suspended in WHMCS']);
|
|
}
|
|
|
|
function hexagamecloud_UnsuspendAccount(array $params): string
|
|
{
|
|
return hexagamecloud_lifecycleAction($params, 'unsuspend');
|
|
}
|
|
|
|
function hexagamecloud_TerminateAccount(array $params): string
|
|
{
|
|
return hexagamecloud_lifecycleAction($params, 'terminate');
|
|
}
|
|
|
|
function hexagamecloud_Renew(array $params): string
|
|
{
|
|
try {
|
|
$client = new HexaGameCloudApiClient($params);
|
|
$serviceId = (string) $params['serviceid'];
|
|
$payload = array_filter([
|
|
'invoiceId' => hexagamecloud_resolveRenewInvoiceId($params),
|
|
'periodEnd' => hexagamecloud_formatIso8601($params['nextduedate'] ?? null),
|
|
], static fn ($value) => $value !== null && $value !== '');
|
|
|
|
$client->renewService(
|
|
$serviceId,
|
|
$payload,
|
|
HexaGameCloudIdempotency::renewKey($serviceId, $params)
|
|
);
|
|
|
|
return 'success';
|
|
} catch (Throwable $exception) {
|
|
logModuleCall(
|
|
'hexagamecloud',
|
|
__FUNCTION__,
|
|
$params,
|
|
$exception->getMessage(),
|
|
$exception->getMessage(),
|
|
['password', 'secret', 'apiSecret']
|
|
);
|
|
return $exception->getMessage();
|
|
}
|
|
}
|
|
|
|
function hexagamecloud_ChangePackage(array $params): string
|
|
{
|
|
try {
|
|
$client = new HexaGameCloudApiClient($params);
|
|
$client->changePackage((string) $params['serviceid'], [
|
|
'planSlug' => HexaGameCloudProductMapping::resolvePlanSlug($params),
|
|
]);
|
|
return 'success';
|
|
} catch (Throwable $exception) {
|
|
logModuleCall('hexagamecloud', __FUNCTION__, $params, $exception->getMessage());
|
|
return $exception->getMessage();
|
|
}
|
|
}
|
|
|
|
function hexagamecloud_ServiceSingleSignOn(array $params)
|
|
{
|
|
return hexagamecloud_requestSso($params, 'service');
|
|
}
|
|
|
|
function hexagamecloud_AdminSingleSignOn(array $params)
|
|
{
|
|
return hexagamecloud_requestSso($params, 'admin');
|
|
}
|
|
|
|
function hexagamecloud_ClientArea(array $params): array
|
|
{
|
|
hexagamecloud_loadLanguage($params);
|
|
|
|
$serviceId = (string) $params['serviceid'];
|
|
$snapshot = hexagamecloud_fetchServiceSnapshot($params);
|
|
$server = $snapshot['service']['server'] ?? [];
|
|
$linkStatus = (string) ($snapshot['service']['status'] ?? 'PENDING');
|
|
|
|
return [
|
|
'templatefile' => 'templates/clientarea',
|
|
'vars' => [
|
|
'LANG' => hexagamecloud_langVars(),
|
|
'apiWarning' => $snapshot['warning'],
|
|
'notProvisioned' => $linkStatus === 'PENDING' && ($server['id'] ?? '') === '',
|
|
'serverName' => (string) ($server['name'] ?? ($params['domain'] ?? '')),
|
|
'technicalStatus' => (string) ($server['status'] ?? 'UNKNOWN'),
|
|
'joinAddress' => (string) ($server['joinHostname'] ?? '-'),
|
|
'edition' => (string) ($server['edition'] ?? 'JAVA'),
|
|
'softwareFamily' => (string) ($server['softwareFamily'] ?? HexaGameCloudProductMapping::resolveSoftwareFamily($params)),
|
|
'minecraftVersion' => (string) ($server['minecraftVersion'] ?? HexaGameCloudProductMapping::resolveMinecraftVersion($params)),
|
|
'planSlug' => HexaGameCloudProductMapping::resolvePlanSlug($params),
|
|
'ramMb' => (int) ($server['ramMb'] ?? 0),
|
|
'nextDueDate' => (string) ($params['nextduedate'] ?? '-'),
|
|
'billingStatus' => $linkStatus === 'SUSPENDED'
|
|
? hexagamecloud_lang('hexagamecloud_suspended_yes')
|
|
: hexagamecloud_lang('hexagamecloud_suspended_no'),
|
|
'lastSync' => $snapshot['syncedAt'],
|
|
],
|
|
];
|
|
}
|
|
|
|
function hexagamecloud_ClientAreaCustomButtonArray(): array
|
|
{
|
|
return [
|
|
hexagamecloud_lang('hexagamecloud_manage_panel') => 'ManagePanel',
|
|
hexagamecloud_lang('hexagamecloud_action_start') => 'StartServer',
|
|
hexagamecloud_lang('hexagamecloud_action_stop') => 'StopServer',
|
|
hexagamecloud_lang('hexagamecloud_action_restart') => 'RestartServer',
|
|
hexagamecloud_lang('hexagamecloud_action_backup') => 'BackupServer',
|
|
];
|
|
}
|
|
|
|
function hexagamecloud_StartServer(array $params): string
|
|
{
|
|
return hexagamecloud_serviceAction($params, 'start');
|
|
}
|
|
|
|
function hexagamecloud_StopServer(array $params): string
|
|
{
|
|
return hexagamecloud_serviceAction($params, 'stop');
|
|
}
|
|
|
|
function hexagamecloud_RestartServer(array $params): string
|
|
{
|
|
return hexagamecloud_serviceAction($params, 'restart');
|
|
}
|
|
|
|
function hexagamecloud_BackupServer(array $params): string
|
|
{
|
|
return hexagamecloud_serviceAction($params, 'backup');
|
|
}
|
|
|
|
function hexagamecloud_serviceAction(array $params, string $action): string
|
|
{
|
|
try {
|
|
$client = new HexaGameCloudApiClient($params);
|
|
$serviceId = (string) $params['serviceid'];
|
|
if ($action === 'start') {
|
|
$client->startService($serviceId);
|
|
} elseif ($action === 'stop') {
|
|
$client->stopService($serviceId);
|
|
} elseif ($action === 'restart') {
|
|
$client->restartService($serviceId);
|
|
} else {
|
|
$client->backupService($serviceId);
|
|
}
|
|
return 'success';
|
|
} catch (Throwable $exception) {
|
|
logModuleCall('hexagamecloud', $action, $params, $exception->getMessage());
|
|
return $exception->getMessage();
|
|
}
|
|
}
|
|
|
|
function hexagamecloud_ManagePanel(array $params): array
|
|
{
|
|
return hexagamecloud_requestSso($params, 'service');
|
|
}
|
|
|
|
function hexagamecloud_AdminServicesTabFields(array $params): array
|
|
{
|
|
hexagamecloud_loadLanguage($params);
|
|
|
|
$serviceId = (string) $params['serviceid'];
|
|
$snapshot = hexagamecloud_fetchServiceSnapshot($params);
|
|
$service = $snapshot['service'];
|
|
$server = $service['server'] ?? [];
|
|
$metadata = HexaGameCloudServiceMetadata::load($serviceId);
|
|
$warning = $snapshot['warning'] !== null
|
|
? '<div class="alert alert-warning">' . htmlspecialchars($snapshot['warning'], ENT_QUOTES, 'UTF-8') . '</div>'
|
|
: '';
|
|
|
|
return [
|
|
hexagamecloud_lang('hexagamecloud_admin_server_id') => $warning . htmlspecialchars((string) ($server['id'] ?? '-'), ENT_QUOTES, 'UTF-8'),
|
|
hexagamecloud_lang('hexagamecloud_admin_user_id') => htmlspecialchars((string) ($server['userId'] ?? '-'), ENT_QUOTES, 'UTF-8'),
|
|
hexagamecloud_lang('hexagamecloud_admin_link_status') => htmlspecialchars((string) ($service['status'] ?? 'PENDING'), ENT_QUOTES, 'UTF-8'),
|
|
hexagamecloud_lang('hexagamecloud_admin_technical_status') => htmlspecialchars((string) ($server['status'] ?? 'UNKNOWN'), ENT_QUOTES, 'UTF-8'),
|
|
hexagamecloud_lang('hexagamecloud_admin_node') => htmlspecialchars((string) ($server['nodeId'] ?? '-'), ENT_QUOTES, 'UTF-8'),
|
|
hexagamecloud_lang('hexagamecloud_admin_host_port') => htmlspecialchars((string) ($server['hostPort'] ?? '-'), ENT_QUOTES, 'UTF-8'),
|
|
hexagamecloud_lang('hexagamecloud_plan') => htmlspecialchars(HexaGameCloudProductMapping::resolvePlanSlug($params), ENT_QUOTES, 'UTF-8'),
|
|
hexagamecloud_lang('hexagamecloud_ram') => htmlspecialchars((string) ((int) ($server['ramMb'] ?? 0)) . ' MiB', ENT_QUOTES, 'UTF-8'),
|
|
hexagamecloud_lang('hexagamecloud_admin_last_sync') => htmlspecialchars((string) ($snapshot['syncedAt'] ?? '-'), ENT_QUOTES, 'UTF-8'),
|
|
hexagamecloud_lang('hexagamecloud_admin_correlation_note') => '<textarea class="form-control" rows="3" name="hexagamecloud_correlation_note">'
|
|
. htmlspecialchars((string) ($metadata['correlation_note'] ?? ''), ENT_QUOTES, 'UTF-8')
|
|
. '</textarea>',
|
|
];
|
|
}
|
|
|
|
function hexagamecloud_AdminServicesTabFieldsSave(array $params): void
|
|
{
|
|
$serviceId = (string) $params['serviceid'];
|
|
$note = trim((string) ($params['hexagamecloud_correlation_note'] ?? ''));
|
|
$existing = HexaGameCloudServiceMetadata::load($serviceId);
|
|
|
|
HexaGameCloudServiceMetadata::save($serviceId, array_merge($existing, [
|
|
'correlation_note' => $note,
|
|
'updated_at' => gmdate('c'),
|
|
'updated_by_admin_id' => (string) ($params['adminid'] ?? ''),
|
|
]));
|
|
}
|
|
|
|
function hexagamecloud_TestConnection(array $params): array
|
|
{
|
|
try {
|
|
$client = new HexaGameCloudApiClient($params);
|
|
$health = $client->health();
|
|
return [
|
|
'success' => true,
|
|
'error' => 'Connected to ' . ($health['integrationId'] ?? 'GameCloud'),
|
|
];
|
|
} catch (Throwable $exception) {
|
|
return ['success' => false, 'error' => $exception->getMessage()];
|
|
}
|
|
}
|
|
|
|
function hexagamecloud_ListAccounts(array $params): array
|
|
{
|
|
try {
|
|
$client = new HexaGameCloudApiClient($params);
|
|
$response = $client->listAccounts();
|
|
$accounts = [];
|
|
|
|
foreach ($response['accounts'] ?? [] as $account) {
|
|
$accounts[] = [
|
|
'uuid' => $account['externalServiceId'] ?? '',
|
|
'domain' => $account['domain'] ?? '',
|
|
'username' => $account['username'] ?? '',
|
|
'status' => $account['status'] ?? 'Active',
|
|
];
|
|
}
|
|
|
|
return $accounts;
|
|
} catch (Throwable $exception) {
|
|
logModuleCall('hexagamecloud', __FUNCTION__, $params, $exception->getMessage());
|
|
return [];
|
|
}
|
|
}
|
|
|
|
function hexagamecloud_MetricProvider(array $params)
|
|
{
|
|
try {
|
|
$client = new HexaGameCloudApiClient($params);
|
|
$serviceId = (string) $params['serviceid'];
|
|
$usage = $client->getServiceUsage($serviceId, hexagamecloud_resolveUsageQuery($params));
|
|
|
|
return [
|
|
'metrics' => $usage['metrics'] ?? [],
|
|
];
|
|
} catch (Throwable $exception) {
|
|
logModuleCall('hexagamecloud', __FUNCTION__, $params, $exception->getMessage());
|
|
return ['metrics' => []];
|
|
}
|
|
}
|
|
|
|
function hexagamecloud_UsageUpdate(array $params)
|
|
{
|
|
try {
|
|
$client = new HexaGameCloudApiClient($params);
|
|
$serviceId = (string) $params['serviceid'];
|
|
$usage = $client->getServiceUsage($serviceId, hexagamecloud_resolveUsageQuery($params));
|
|
$usageUpdate = $usage['usageUpdate'] ?? [];
|
|
|
|
return [
|
|
'diskusage' => (float) ($usageUpdate['diskUsageGiB'] ?? 0),
|
|
'bandwidth' => (float) ($usageUpdate['bandwidthGiB'] ?? 0),
|
|
];
|
|
} catch (Throwable $exception) {
|
|
logModuleCall('hexagamecloud', __FUNCTION__, $params, $exception->getMessage());
|
|
return [
|
|
'diskusage' => 0,
|
|
'bandwidth' => 0,
|
|
];
|
|
}
|
|
}
|
|
|
|
function hexagamecloud_resolveUsageQuery(array $params): array
|
|
{
|
|
$query = [];
|
|
|
|
if (!empty($params['usageperiodstart'])) {
|
|
$query['periodStart'] = gmdate('c', strtotime((string) $params['usageperiodstart']));
|
|
}
|
|
|
|
if (!empty($params['usageperiodend'])) {
|
|
$query['periodEnd'] = gmdate('c', strtotime((string) $params['usageperiodend']));
|
|
}
|
|
|
|
if (!empty($params['testmode']) || !empty($params['configoption4'])) {
|
|
$query['testMode'] = true;
|
|
}
|
|
|
|
return $query;
|
|
}
|
|
|
|
function hexagamecloud_lifecycleAction(array $params, string $action, array $body = []): string
|
|
{
|
|
try {
|
|
$client = new HexaGameCloudApiClient($params);
|
|
$serviceId = (string) $params['serviceid'];
|
|
if ($action === 'suspend') {
|
|
$client->suspendService($serviceId, $body);
|
|
} elseif ($action === 'unsuspend') {
|
|
$client->unsuspendService($serviceId);
|
|
} else {
|
|
$client->terminateService($serviceId);
|
|
}
|
|
return 'success';
|
|
} catch (Throwable $exception) {
|
|
logModuleCall('hexagamecloud', $action, $params, $exception->getMessage());
|
|
return $exception->getMessage();
|
|
}
|
|
}
|
|
|
|
function hexagamecloud_fetchServiceSnapshot(array $params): array
|
|
{
|
|
$serviceId = (string) $params['serviceid'];
|
|
$cached = HexaGameCloudServiceMetadata::load($serviceId);
|
|
$snapshot = [
|
|
'service' => $cached['service'] ?? [
|
|
'externalServiceId' => $serviceId,
|
|
'status' => 'PENDING',
|
|
'server' => [],
|
|
],
|
|
'warning' => null,
|
|
'syncedAt' => $cached['synced_at'] ?? null,
|
|
];
|
|
|
|
try {
|
|
$client = new HexaGameCloudApiClient($params);
|
|
$service = $client->getService($serviceId);
|
|
$snapshot['service'] = $service;
|
|
$snapshot['syncedAt'] = gmdate('c');
|
|
|
|
HexaGameCloudServiceMetadata::save($serviceId, [
|
|
'service' => $service,
|
|
'synced_at' => $snapshot['syncedAt'],
|
|
'correlation_note' => $cached['correlation_note'] ?? '',
|
|
'updated_by_admin_id' => $cached['updated_by_admin_id'] ?? '',
|
|
]);
|
|
} catch (Throwable $exception) {
|
|
logModuleCall('hexagamecloud', 'fetchServiceSnapshot', $params, $exception->getMessage());
|
|
$snapshot['warning'] = hexagamecloud_lang('hexagamecloud_api_unavailable');
|
|
}
|
|
|
|
return $snapshot;
|
|
}
|
|
|
|
function hexagamecloud_resolveRenewInvoiceId(array $params): ?string
|
|
{
|
|
$invoiceId = (string) ($params['invoiceid'] ?? '');
|
|
if ($invoiceId !== '') {
|
|
return $invoiceId;
|
|
}
|
|
|
|
if (!empty($params['model']['invoiceid'])) {
|
|
return (string) $params['model']['invoiceid'];
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
function hexagamecloud_formatIso8601(?string $value): ?string
|
|
{
|
|
if ($value === null || trim($value) === '') {
|
|
return null;
|
|
}
|
|
|
|
$timestamp = strtotime($value);
|
|
if ($timestamp === false) {
|
|
return null;
|
|
}
|
|
|
|
return gmdate('c', $timestamp);
|
|
}
|
|
|
|
function hexagamecloud_loadLanguage(array $params): void
|
|
{
|
|
static $loaded = false;
|
|
if ($loaded) {
|
|
return;
|
|
}
|
|
|
|
$language = strtolower((string) ($params['clientsdetails']['language'] ?? $params['language'] ?? 'english'));
|
|
if (!in_array($language, ['english', 'german'], true)) {
|
|
$language = 'english';
|
|
}
|
|
|
|
$langFile = __DIR__ . '/lang/' . $language . '.php';
|
|
if (!is_readable($langFile)) {
|
|
$langFile = __DIR__ . '/lang/english.php';
|
|
}
|
|
|
|
require $langFile;
|
|
$loaded = true;
|
|
}
|
|
|
|
function hexagamecloud_lang(string $key): string
|
|
{
|
|
global $_LANG;
|
|
|
|
return (string) ($_LANG[$key] ?? $key);
|
|
}
|
|
|
|
function hexagamecloud_langVars(): array
|
|
{
|
|
global $_LANG;
|
|
|
|
return is_array($_LANG ?? null) ? $_LANG : [];
|
|
}
|
|
|
|
function hexagamecloud_requestSso(array $params, string $kind)
|
|
{
|
|
try {
|
|
$client = new HexaGameCloudApiClient($params);
|
|
$externalServiceId = (string) $params['serviceid'];
|
|
$externalClientId = (string) ($params['clientsdetails']['userid'] ?? $params['userid'] ?? '');
|
|
$externalUserId = (string) ($params['clientsdetails']['userid'] ?? $params['adminid'] ?? $externalClientId);
|
|
|
|
$response = $client->createServiceSso($externalServiceId, [
|
|
'externalClientId' => $externalClientId,
|
|
'externalUserId' => $externalUserId,
|
|
'role' => $kind === 'admin' ? 'admin' : 'owner',
|
|
'kind' => $kind,
|
|
]);
|
|
|
|
return [
|
|
'success' => true,
|
|
'redirectTo' => $response['redirectUrl'],
|
|
];
|
|
} catch (Throwable $exception) {
|
|
logModuleCall('hexagamecloud', 'sso:' . $kind, $params, $exception->getMessage());
|
|
return [
|
|
'success' => false,
|
|
'error' => $exception->getMessage(),
|
|
];
|
|
}
|
|
}
|