Files
HexaHost-GameCloud/integrations/whmcs/modules/servers/hexagamecloud/hexagamecloud.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(),
];
}
}