Init Files

This commit is contained in:
Prophet731
2023-04-30 14:07:59 -04:00
parent caf77ea0aa
commit 0d4cff37b9
17 changed files with 1516 additions and 3 deletions

View File

@@ -1,8 +1,8 @@
# VirtFusion Direct Provisioning Moudle for WHMCS # VirtFusion Direct Provisioning Module for WHMCS
This module requires VirtFusion v1.7.3 or higher as this is what it's based on. Please refer to the offical [documenataion](https://docs.virtfusion.com/integrations/whmcs). This module requires VirtFusion v1.7.3 or higher as this is what it's based on. Please refer to the official [documenataion](https://docs.virtfusion.com/integrations/whmcs).
## Changes in this module ## Changes in this module
- Allow's using the configurable options quanity method for assigning Memory in Gigabytes instead of Megabyte. So you can use whole numbers to represent GB instead. - Allows using the configurable options quantity method for assigning Memory in Gigabytes instead of Megabyte. So you can use whole numbers to represent GB instead.
For example, prior to this change for two gigabytes of RAM you'd need to set 2048 as either the min or max amount, now you can just input 2 for 2GB. For example, prior to this change for two gigabytes of RAM you'd need to set 2048 as either the min or max amount, now you can just input 2 for 2GB.

104
VirtFusionDirect.php Normal file
View File

@@ -0,0 +1,104 @@
<?php
if (!defined("WHMCS")) {
die("This file cannot be accessed directly");
}
use WHMCS\Module\Server\VirtFusionDirect\ModuleFunctions;
function VirtFusionDirect_MetaData()
{
return [
'DisplayName' => 'VirtFusion Direct Provisioning',
'APIVersion' => '1.1',
'RequiresServer' => true,
'ServiceSingleSignOnLabel' => false,
'AdminSingleSignOnLabel' => false,
];
}
function VirtFusionDirect_ConfigOptions()
{
return [
"defaultHypervisorGroupId" => [
"FriendlyName" => "Hypervisor Group ID",
"Type" => "text",
"Size" => "20",
"Description" => "The default hypervisor group ID",
"Default" => "1",
],
"packageID" => [
"FriendlyName" => "Package ID",
"Type" => "text",
"Size" => "20",
"Description" => "The package ID",
"Default" => "1",
],
"defaultIPv4" => [
"FriendlyName" => "Default IPv4",
"Type" => "dropdown",
"Options" => "0,1,2,3,4,5,6,7,8,9,10",
"Description" => "The default amount of IPv4 addresses to assign to the server.",
"Default" => "1",
],
];
}
function VirtFusionDirect_AdminCustomButtonArray()
{
$buttonarray = array(
"Update Server Object" => "updateServerObject",
);
return $buttonarray;
}
/**
*
*
* Service functions
*
*/
function VirtFusionDirect_CreateAccount(array $params)
{
return (new ModuleFunctions())->createAccount($params);
}
function VirtFusionDirect_SuspendAccount(array $params)
{
return (new ModuleFunctions())->suspendAccount($params);
}
function VirtFusionDirect_UnsuspendAccount(array $params)
{
return (new ModuleFunctions())->unsuspendAccount($params);
}
function VirtFusionDirect_TerminateAccount(array $params)
{
return (new ModuleFunctions())->terminateAccount($params);
}
function VirtFusionDirect_updateServerObject(array $params)
{
return (new ModuleFunctions())->updateServerObject($params);
}
function VirtFusionDirect_ChangePackage(array $params)
{
return 'success';
}
function VirtFusionDirect_AdminServicesTabFields(array $params)
{
return (new ModuleFunctions())->adminServicesTabFields($params);
}
function VirtFusionDirect_AdminServicesTabFieldsSave(array $params)
{
(new ModuleFunctions())->adminServicesTabFieldsSave($params);
}
function VirtFusionDirect_ClientArea(array $params)
{
return (new ModuleFunctions())->clientArea($params);
}

85
admin.php Normal file
View File

@@ -0,0 +1,85 @@
<?php
require dirname(__DIR__, 3) . '/init.php';
use WHMCS\Module\Server\VirtFusionDirect\Database;
use WHMCS\Module\Server\VirtFusionDirect\Module;
use WHMCS\Module\Server\VirtFusionDirect\ServerResource;
$vf = new Module();
$vf->adminOnly();
switch ($vf->validateAction(true)) {
/**
*
* Get server information.
*
*/
case 'serverData':
if ($vf->validateServiceID(true)) {
/** No need to validate ownership **/
$whmcsService = Database::getWhmcsService((int)$_GET['serviceID']);
if (!$whmcsService) {
$vf->output(['success' => false, 'errors' => 'Service not found.'], true, true, 200);
}
if ($whmcsService->domainstatus == 'Pending' || $whmcsService->domainstatus == 'Terminated' || $whmcsService->domainstatus == 'Cancelled' || $whmcsService->domainstatus == 'Fraud') {
$vf->output(['success' => false, 'errors' => 'Server is not Active, Suspended or Completed. Not fetching remote data.'], true, true, 200);
}
$data = $vf->fetchServerData((int)$_GET['serviceID']);
if (!$data) {
$vf->output(['success' => false, 'errors' => 'No data returned from VirtFusion.'], true, true, 200);
}
(new Module())->updateWhmcsServiceParamsOnServerObject((int)$_GET['serviceID'], $data);
$vf->output(['success' => true, 'data' => (new ServerResource())->process($data)], true, true, 200);
}
break;
/**
*
* Impersonate server owner.
*
*/
case 'impersonateServerOwner':
if ($vf->validateServiceID(true)) {
$service = Database::getSystemService((int)$_GET['serviceID']);
if (!$service) {
$vf->output(['success' => false, 'errors' => 'Service not found'], true, true, 200);
}
$whmcsService = Database::getWhmcsService((int)$_GET['serviceID']);
$cp = $vf->getCP($whmcsService->server);
$request = $vf->initCurl($cp['token']);
$data = $request->get($cp['url'] . '/users/' . $whmcsService->userid . '/byExtRelation');
if ($request->getRequestInfo('http_code') === 200) {
$vf->output(['success' => true, 'url' => $cp['base_url'], 'user' => json_decode($data, true)['data']], true, true, 200);
}
$vf->output(['success' => false, 'errors' => 'Received HTTP code ' . $request->getRequestInfo('http_code')], true, true, 200);
}
break;
default:
/** No valid action was specified **/
$vf->output(['success' => false, 'errors' => 'invalid action'], true, true, 200);
}

111
client.php Normal file
View File

@@ -0,0 +1,111 @@
<?php
require dirname(__DIR__, 3) . '/init.php';
use WHMCS\Module\Server\VirtFusionDirect\Module;
use WHMCS\Module\Server\VirtFusionDirect\ServerResource;
$vf = new Module();
$vf->isAuthenticated();
switch ($vf->validateAction(true)) {
/**
*
* Reset Password.
*
*/
case 'resetPassword':
if ($vf->validateServiceID(true)) {
$client = $vf->validateUserOwnsService((int)$_GET['serviceID']);
if (!$client) {
$vf->output(['success' => false, 'errors' => 'service <> owner mismatch'], true, true, 200);
}
$data = $vf->resetUserPassword((int)$_GET['serviceID'], $client);
if ($data) {
$vf->output(['success' => true, 'data' => $data->data], true, true, 200);
}
$vf->output(['success' => false, 'errors' => 'error'], true, true, 200);
}
break;
/**
*
* Get server information.
*
*/
case 'serverData':
if ($vf->validateServiceID(true)) {
if (!$vf->validateUserOwnsService((int)$_GET['serviceID'])) {
$vf->output(['success' => false, 'errors' => 'service <> owner mismatch'], true, true, 200);
}
$data = $vf->fetchServerData((int)$_GET['serviceID']);
if ($data) {
(new Module())->updateWhmcsServiceParamsOnServerObject((int)$_GET['serviceID'], $data);
$vf->output(['success' => true, 'data' => (new ServerResource())->process($data)], true, true, 200);
}
$vf->output(['success' => false, 'errors' => 'error'], true, true, 200);
}
break;
/**
*
* Login as server owner.
*
*/
case 'loginAsServerOwner':
if ($vf->validateServiceID(true)) {
/**
* A client can't log in as any user. Ownership should be validated.
*/
if (!$vf->validateUserOwnsService((int)$_GET['serviceID'])) {
$vf->output(['success' => false, 'errors' => 'service <> owner mismatch'], true, true, 200);
}
$token = $vf->fetchLoginTokens((int)$_GET['serviceID']);
if ($token) {
/**
* A valid token/url was received.
*/
$vf->output(['success' => true, 'token_url' => $token], true, true, 200);
}
/**
* Failed to get the token from the control panel or the service ID doesn't exist.
*/
$vf->output(['success' => false, 'errors' => 'token request error'], true, true, 200);
}
break;
default:
/**
*
* No valid action was specified.
*
*/
$vf->output(['success' => false, 'errors' => 'invalid action'], true, true, 200);
}

View File

@@ -0,0 +1,19 @@
<?php
if (!defined("WHMCS")) {
die("This file cannot be accessed directly");
}
return [
'ipv4' => 'IPv4',
'packageId' => 'Package',
'hypervisorId' => 'Location',
'storage' => 'Storage',
'memory' => 'Memory',
'traffic' => 'Bandwidth',
'networkSpeedInbound' => 'Inbound Network Speed',
'networkSpeedOutbound' => 'Outbound Network Speed',
'cpuCores' => 'CPU Cores',
'networkProfile' => 'Network Type',
'storageProfile' => 'Storage Type',
];

1
hooks.php Normal file
View File

@@ -0,0 +1 @@
<?php

114
lib/AdminHTML.php Normal file
View File

@@ -0,0 +1,114 @@
<?php
namespace WHMCS\Module\Server\VirtFusionDirect;
class AdminHTML
{
public static function options($systemUrl, $serviceId)
{
return <<<EOT
<button onclick="impersonateServerOwner('${serviceId}', '${systemUrl}')" type="button" class="btn btn-primary">Impersonate Server Owner</button>
<span class="text-info">&nbsp;&nbsp;A valid VirtFusion admin session in the same browser is required for this functionality to work.</span>
EOT;
}
public static function serverObject($serverObject)
{
return <<<EOT
<textarea class="form-control" name="modulefields[1]" rows="10" style="width: 100%" disabled>${serverObject}</textarea>
EOT;
}
public static function serverId($serverId)
{
return <<<EOT
<input type="text" class="form-control input-200 input-inline" name="modulefields[0]" size="20" value="${serverId}" />
<span class="text-info">&nbsp;&nbsp;Changing the Sever ID manually is not recommended. Alterations to this field are usually handled automatically.</span>
EOT;
}
public static function serverInfo($systemUrl, $serviceId)
{
return <<<EOT
<link href="${systemUrl}modules/servers/VirtFusionDirect/templates/css/module.css" rel="stylesheet">
<script src="${systemUrl}modules/servers/VirtFusionDirect/templates/js/module.js"></script>
<div id="vf-loader" class="vf-loader">
<div id="vf-loading"></div>
</div>
<div id="vf-server-info-error">
<div class="alert alert-warning mb-0">
<div id="vf-server-info-error-message"></div>
</div>
</div>
<div id="vf-server-info" class="row mb-2">
<div class="col-12">
<div class="row">
<div class="col-md-6">
<div class="row p-1">
<div class="col-xs-4 col-4 text-right vf-bold">
Name:
</div>
<div class="col-xs-8 col-8" id="vf-data-server-name">
</div>
</div>
<div class="row p-1">
<div class="col-xs-4 col-4 text-right vf-bold">
Hostname:
</div>
<div class="col-xs-8 col-8" id="vf-data-server-hostname">
</div>
</div>
<div class="row p-1">
<div class="col-xs-4 col-4 text-right vf-bold">
Memory:
</div>
<div class="col-xs-8 col-8" id="vf-data-server-memory">
</div>
</div>
<div class="row p-1">
<div class="col-xs-4 col-4 text-right vf-bold">
CPU:
</div>
<div class="col-xs-8 col-8" id="vf-data-server-cpu">
</div>
</div>
</div>
<div class="col-md-6">
<div class="row p-1">
<div class="col-xs-4 col-4 text-right vf-bold">
IPv4:
</div>
<div class="col-xs-8 col-8" id="vf-data-server-ipv4">
</div>
</div>
<div class="row p-1">
<div class="col-xs-4 col-4 text-right vf-bold">
IPv6:
</div>
<div class="col-xs-8 col-8" id="vf-data-server-ipv6">
</div>
</div>
<div class="row p-1">
<div class="col-xs-4 col-4 text-right vf-bold">
Storage:
</div>
<div class="col-xs-8 col-8" id="vf-data-server-storage">
</div>
</div>
<div class="row p-1">
<div class="col-xs-4 col-4 text-right vf-bold">
Traffic:
</div>
<div class="col-xs-8 col-8" id="vf-data-server-traffic">
</div>
</div>
</div>
</div>
</div>
</div>
<script>vfServerDataAdmin("${serviceId}","${systemUrl}");</script>
EOT;
}
}

200
lib/Curl.php Normal file
View File

@@ -0,0 +1,200 @@
<?php
namespace WHMCS\Module\Server\VirtFusionDirect;
class Curl
{
private $ch;
private $data;
private $customOptions = [];
private $defaultOptions = [
CURLOPT_SSL_VERIFYPEER => false,
CURLOPT_SSL_VERIFYHOST => false,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_USERAGENT => 'CURL',
CURLOPT_HEADER => false,
CURLOPT_NOBODY => false,
];
public function __construct()
{
$this->ch = curl_init();
}
public function useCookies()
{
$cookiesFile = tempnam('/tmp', 'virtfusion_cookies');
$this->defaultOptions[CURLOPT_COOKIEFILE] = $cookiesFile;
$this->defaultOptions[CURLOPT_COOKIEJAR] = $cookiesFile;
}
public function setLog()
{
$log = fopen(__DIR__ . '/CURL.log', 'a');
if ($log) {
fwrite($log, str_repeat('=', 80) . PHP_EOL);
$this->addOption(CURLOPT_STDERR, $log);
$this->addOption(CURLOPT_VERBOSE, true);
}
}
/**
* @param $name
* @param $value
*/
public function addOption($name, $value)
{
$this->customOptions[$name] = $value;
}
/**
* @param null $url
* @return bool|string|void
*/
public function put($url = null)
{
return $this->send('PUT', $url);
}
/**
* @param $method
* @param $url
* @return bool|string|void
*/
private function send($method, $url)
{
if ($url === null) {
if (!isset($this->customOptions[CURLOPT_URL]) || empty($this->customOptions[CURLOPT_URL])) {
exit('empty url');
}
}
$this->addOption(CURLOPT_CUSTOMREQUEST, $method);
$this->addOption(CURLOPT_URL, $url);
return $this->exec();
}
/**
* @return bool|string
*/
private function exec()
{
$this->setOptions();
$response = curl_exec($this->ch);
$this->data['info'] = curl_getinfo($this->ch);
if (isset($this->customOptions[CURLOPT_HEADER]) && $this->customOptions[CURLOPT_HEADER]) {
$this->data['info']['request_header'] = trim($this->data['info']['request_header']);
$this->processHeaders($response);
}
curl_close($this->ch);
return $response;
}
private function setOptions()
{
if (isset($this->customOptions[CURLOPT_HEADER]) && $this->customOptions[CURLOPT_HEADER]) {
$this->addOption(CURLINFO_HEADER_OUT, true);
}
$options = $this->customOptions + $this->defaultOptions;
curl_setopt_array($this->ch, $options);
}
/**
* @param $data
*/
private function processHeaders(&$data)
{
$tmp = explode("\r\n\r\n", $data, 2);
$this->data['info']['response_header'] = $tmp[0];
$this->data['info']['response_body'] = $data = trim($tmp[1]);
$tmp = explode("\r\n", $this->data['info']['response_header']);
$this->data['data']['Message'] = $tmp[0];
for ($i = 1, $size = count($tmp); $i < $size; ++$i) {
$string = explode(': ', $tmp[$i], 2);
$this->data['data'][$string[0]] = $string[1];
}
}
/**
* @param null $url
* @return bool|string|void
*/
public function get($url = null)
{
return $this->send('GET', $url);
}
/**
* @param null $url
* @return bool|string|void
*/
public function delete($url = null)
{
return $this->send('DELETE', $url);
}
/**
* @param null $url
* @return bool|string|void
*/
public function post($url = null)
{
return $this->send('POST', $url);
}
/**
* @param null $url
* @return bool|string|void
*/
public function head($url = null)
{
return $this->send('HEAD', $url);
}
/**
* @param false $param
* @return mixed|null
*/
public function getRequestInfo($param = false)
{
if ($param) {
return $this->getDataItem('info', $param);
} else {
return $this->data['info'];
}
}
/**
* @param $what
* @param $name
* @return mixed|null
*/
private function getDataItem($what, $name)
{
if (isset($this->data[$what][$name])) {
return $this->data[$what][$name];
} else {
return null;
}
}
/**
* @param false $param
* @return mixed|null
*/
public function getHeadersData($param = false)
{
if ($param) {
return $this->getDataItem('data', $param);
}
return $this->data['data'];
}
}

121
lib/Database.php Normal file
View File

@@ -0,0 +1,121 @@
<?php
namespace WHMCS\Module\Server\VirtFusionDirect;
use WHMCS\Database\Capsule as DB;
class Database
{
const SYSTEM_TABLE = 'mod_virtfusion_direct';
public static function schema()
{
if (!DB::schema()->hasTable(self::SYSTEM_TABLE)) {
try {
DB::schema()->create(self::SYSTEM_TABLE, function ($table) {
$table->unsignedBigInteger('service_id')->nullable()->default(null)->index();
$table->unsignedBigInteger('server_id')->nullable()->default(null);
$table->timestamps();
});
} catch (\Exception $e) {
Log::insert(__FUNCTION__, [], $e->getMessage());
}
}
if (!DB::schema()->hasColumn(self::SYSTEM_TABLE, 'server_object')) {
try {
DB::schema()->table(self::SYSTEM_TABLE, function ($table) {
$table->longText('server_object')->nullable()->default(null);
});
} catch (\Exception $e) {
Log::insert(__FUNCTION__, [], $e->getMessage());
}
}
}
public static function getWhmcsServer(int $server, $any = false)
{
if ($server) {
return DB::table('tblservers')->where('type', 'VirtFusionDirect')->where('id', $server)->first();
}
if ($any) {
return DB::table('tblservers')->where('type', 'VirtFusionDirect')->where('disabled', 0)->first();
}
return false;
}
public static function userWhmcsService(int $serviceId, int $userId)
{
return DB::table('tblhosting')->where('id', $serviceId)->where('userid', $userId)->exists();
}
public static function getSystemUrl()
{
$url = DB::table('tblconfiguration')->where('setting', '=', 'SystemURL')->first();
return $url->value;
}
public static function getUser(int $id)
{
return DB::table('tblclients')->where('id', $id)->first();
}
public static function getWhmcsService(int $serviceId)
{
return DB::table('tblhosting')->where('id', $serviceId)->first();
}
public static function updateSystemServiceServerId(int $serviceId, int $serverId)
{
DB::table(self::SYSTEM_TABLE)->updateOrInsert(
[
"service_id" => $serviceId
],
[
'server_id' => $serverId
]
);
}
public static function updateWhmcsServiceParams(int $serviceId, $data)
{
if (count($data)) {
foreach ($data as $key => $items) {
DB::table($key)->where('id', $serviceId)->update($items);
}
}
}
public static function checkSystemService(int $serviceId)
{
return DB::table(self::SYSTEM_TABLE)->where('service_id', $serviceId)->exists();
}
public static function deleteSystemService(int $serviceId)
{
DB::table(self::SYSTEM_TABLE)->where('service_id', $serviceId)->delete();
}
public static function updateSystemServiceServerObject(int $serviceId, $data)
{
DB::table(self::SYSTEM_TABLE)->where('service_id', $serviceId)->update(['server_object' => json_encode($data, JSON_PRETTY_PRINT)]);
}
public static function systemOnServerCreate(int $serviceId, $data)
{
if (DB::table(self::SYSTEM_TABLE)->where('service_id', $serviceId)->exists()) {
DB::table(self::SYSTEM_TABLE)->where('service_id', $serviceId)->update(['server_id' => $data->data->id, 'server_object' => json_encode($data, JSON_PRETTY_PRINT)]);
} else {
DB::table(self::SYSTEM_TABLE)->insert(['service_id' => $serviceId, 'server_id' => $data->data->id, 'server_object' => json_encode($data, JSON_PRETTY_PRINT)]);
}
}
public static function getSystemService(int $serviceId)
{
return DB::table(self::SYSTEM_TABLE)->where('service_id', $serviceId)->first();
}
}

23
lib/Log.php Normal file
View File

@@ -0,0 +1,23 @@
<?php
namespace WHMCS\Module\Server\VirtFusionDirect;
class Log
{
const LOG_MODULE = 'VirtFusionDirect';
public static function insert($action, $requestString, $responseData)
{
/**
* Log module call.
*
* @param string $module The name of the module
* @param string $action The name of the action being performed
* @param string|array $requestString The input parameters for the API call
* @param string|array $responseData The response data from the API call
* @param string|array $processedData The resulting data after any post processing (eg. json decode, xml decode, etc...)
* @param array $replaceVars An array of strings for replacement
*/
logModuleCall(self::LOG_MODULE, $action, $requestString, $responseData);
}
}

235
lib/Module.php Normal file
View File

@@ -0,0 +1,235 @@
<?php
namespace WHMCS\Module\Server\VirtFusionDirect;
class Module
{
public function __construct()
{
error_reporting(0);
Database::schema();
}
/**
* @param bool $exitOnError
* @return mixed
*/
public function validateAction($exitOnError = true)
{
if (!isset($_GET['action'])) {
$this->output(['errors' => 'no action specified'], true, $exitOnError, 200);
}
return $_GET['action'];
}
/**
* @param bool $exitOnError
* @return mixed
*/
public function validateServiceID($exitOnError = true)
{
if (!isset($_GET['serviceID'])) {
$this->output(['errors' => 'no serviceID specified'], true, $exitOnError, 200);
}
return $_GET['serviceID'];
}
/**
* @param $serviceID
* @param bool $exitOnError
* @return bool
*/
public function validateUserOwnsService($serviceID, $exitOnError = true)
{
$currentUser = new \WHMCS\Authentication\CurrentUser;
$client = $currentUser->client();
if (!$client) {
return false;
}
if (Database::userWhmcsService($serviceID, $client->id)) {
return $client->id;
}
return false;
}
/**
* @param $serviceID
* @return false|string
*/
public function fetchLoginTokens($serviceID)
{
$service = Database::getSystemService($serviceID);
if ($service) {
$whmcsService = Database::getWhmcsService($serviceID);
$cp = $this->getCP($whmcsService->server);
$request = $this->initCurl($cp['token']);
$data = $request->post($cp['url'] . '/users/' . $whmcsService->userid . '/serverAuthenticationTokens/' . $service->server_id);
Log::insert(__FUNCTION__, $request->getRequestInfo(), $data);
if ($request->getRequestInfo('http_code') == '200') {
$data = json_decode($data);
return $cp['base_url'] . $data->data->authentication->endpoint_complete;
}
}
return false;
}
public function updateWhmcsServiceParamsOnServerObject($serviceId, $data)
{
$output = [];
$serverResource = (new ServerResource())->process($data);
$dedicatedIpv4 = null;
if (count($serverResource['primaryNetwork']['ipv4Unformatted'])) {
$dedicatedIpv4 = $serverResource['primaryNetwork']['ipv4Unformatted'][0];
}
if ($serverResource['hostname'] == '-') {
if ($serverResource['name'] == '-') {
$name = '';
} else {
$name = $serverResource['name'];
}
} else {
$name = $serverResource['hostname'];
}
$output['tblhosting'] = ["dedicatedip" => $dedicatedIpv4, "domain" => $name, "username" => $serverResource['username'], "password" => $serverResource['password']];
Database::updateWhmcsServiceParams($serviceId, $output);
}
public function updateWhmcsServiceParamsOnDestroy($serviceId)
{
$output['tblhosting'] = ["dedicatedip" => null];
Database::updateWhmcsServiceParams($serviceId, $output);
}
public function fetchServerData($serviceID)
{
$service = Database::getSystemService($serviceID);
if ($service) {
$whmcsService = Database::getWhmcsService($serviceID);
$cp = $this->getCP($whmcsService->server);
$request = $this->initCurl($cp['token']);
$data = $request->get($cp['url'] . '/servers/' . $service->server_id);
Log::insert(__FUNCTION__, $request->getRequestInfo(), $data);
if ($request->getRequestInfo('http_code') == '200') {
return json_decode($data);
}
}
return false;
}
public function resetUserPassword($serviceID, $clientID)
{
$service = Database::getSystemService($serviceID);
if ($service) {
$whmcsService = Database::getWhmcsService($serviceID);
$cp = $this->getCP($whmcsService->server);
$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') {
return json_decode($data);
}
}
return false;
}
/**
* @param $data
* @param bool $json
* @param bool $exit
* @param int $rspCode
*/
public function output($data, $json = true, $exit = true, $rspCode = 200)
{
http_response_code($rspCode);
if ($json) {
header('Content-Type: application/json; charset=utf-8');
echo json_encode($data);
} else {
echo $data;
}
if ($exit) {
exit();
}
}
/**
* @param $server
* @return array|false
*/
public function getCP($server, $any = false)
{
$cp = Database::getWhmcsServer($server, $any);
if ($cp) {
return [
'url' => 'https://' . $cp->hostname . '/api/v1',
'base_url' => 'https://' . $cp->hostname,
'token' => decrypt($cp->password)];
}
return false;
}
/**
* @return bool|void
*/
public function adminOnly()
{
if ((new \WHMCS\Authentication\CurrentUser)->isAuthenticatedAdmin()) {
return true;
}
$this->output(['errors' => 'unauthenticated'], true, true, 200);
}
/**
* @return bool|void
*/
public function isAuthenticated()
{
if ((new \WHMCS\Authentication\CurrentUser)->isAuthenticatedUser()) {
return true;
}
$this->output(['errors' => 'unauthenticated'], true, true, 200);
}
/**
* @param $token
* @return \WHMCS\Module\Server\VirtFusionDirect\Curl
*/
public function initCurl($token)
{
$curl = new Curl();
$curl->addOption(CURLOPT_HTTPHEADER, [
'Accept: application/json',
'Content-type: application/json; charset=utf-8',
'authorization: Bearer ' . $token
]);
return $curl;
}
}

434
lib/ModuleFunctions.php Normal file
View File

@@ -0,0 +1,434 @@
<?php
namespace WHMCS\Module\Server\VirtFusionDirect;
class ModuleFunctions extends Module
{
public function __construct()
{
parent::__construct();
}
/**
*
* CREATE SERVER
*
* Before creating a server, we check to see if a user exists in VirtFusion that matches
* the WHMCS user. If it matches, We move on to create the server, if not, we attempt to
* create a user to assign to the new server.
*
*/
public function createAccount($params)
{
try {
/**
*
* If the service exists in the custom table, Cancel the create account action.
*
*/
if (Database::checkSystemService($params['serviceid'])) {
return 'Service already exists. You must run a termination first.';
}
/**
*
* If no VirtFusionDirect control server exists, cancel the create account action.
*
*/
$server = $params['serverid'] ?: false;
$cp = $this->getCP($server, $server ? false : true);
if (!$cp) {
return 'No Control server found.';
}
Log::insert(__FUNCTION__, $params, []);
/**
*
* Does a user account in VirtFusion match this account (byExtRelationId) in WHMCS.
*
*/
$request = $this->initCurl($cp['token']);
$data = $request->get($cp['url'] . '/users/' . $params['userid'] . '/byExtRelation');
Log::insert(__FUNCTION__, $request->getRequestInfo(), $data);
switch ($request->getRequestInfo('http_code')) {
case 200:
/**
*
* A user with relation ID exists in VirtFusion. We can provision under that account.
*
*/
break;
case 404:
/**
*
* A user doesn't exist in VirtFusion. We should attempt to create one.
*
*/
$user = Database::getUser($params['userid']);
$request = $this->initCurl($cp['token']);
$request->addOption(CURLOPT_POSTFIELDS, json_encode(
[
"name" => $user->firstname . ' ' . $user->lastname,
"email" => $user->email,
"extRelationId" => $user->id
]
));
$data = $request->post($cp['url'] . '/users');
Log::insert(__FUNCTION__, $request->getRequestInfo(), $data);
if ($request->getRequestInfo('http_code') !== 201) {
return 'Unable to create user.';
}
break;
default:
return 'Error processing user account.';
break;
}
$data = json_decode($data);
/**
*
* A user is available. We can now attempt to create a server.
*
*/
$configOptionDefaultNaming = [
'ipv4' => 'IPv4',
'packageId' => 'Package',
'hypervisorId' => 'Location',
'storage' => 'Storage',
'memory' => 'Memory',
'traffic' => 'Bandwidth',
'networkSpeedInbound' => 'Inbound Network Speed',
'networkSpeedOutbound' => 'Outbound Network Speed',
'cpuCores' => 'CPU Cores',
'networkProfile' => 'Network Type',
'storageProfile' => 'Storage Type',
];
$configOptionCustomNaming = [];
if (file_exists(ROOTDIR . '/modules/servers/VirtFusionDirect/config/ConfigOptionMapping.php')) {
$configOptionCustomNaming = require_once ROOTDIR . '/modules/servers/VirtFusionDirect/config/ConfigOptionMapping.php';
}
$options = [
"packageId" => $params['configoption2'],
"userId" => $data->data->id,
"hypervisorId" => $params['configoption1'],
"ipv4" => $params['configoption3'],
];
if (array_key_exists('configoptions', $params)) {
foreach ($configOptionDefaultNaming as $key => $option) {
$currentOption = array_key_exists($key, $configOptionCustomNaming) ? $configOptionCustomNaming[$key] : $option;
if (array_key_exists($currentOption, $params['configoptions'])) {
// If the option key is "Memory" and the value is less than 1024, we need to convert it to MB
// VirtFusion expects memory in MB.
if ($currentOption === 'Memory' && $params['configoptions'][$currentOption] < 1024) {
$options[$key] = $params['configoptions'][$currentOption] * 1024;
} else {
$options[$key] = $params['configoptions'][$currentOption];
}
}
}
}
$request = $this->initCurl($cp['token']);
$request->addOption(CURLOPT_POSTFIELDS, json_encode($options));
$data = $request->post($cp['url'] . '/servers');
$data = json_decode($data);
Log::insert(__FUNCTION__, $request->getRequestInfo(), $data);
if ($request->getRequestInfo('http_code') === 201) {
Database::systemOnServerCreate($params['serviceid'], $data);
$this->updateWhmcsServiceParamsOnServerObject($params['serviceid'], $data);
/**
*
* Server was created successfully.
*
*/
return 'success';
} else {
if ($data->errors[0]) {
return $data->errors[0];
}
return 'Unknown error.';
}
} catch (\Exception $e) {
Log::insert(__FUNCTION__, $params, $e->getMessage());
return $e->getMessage();
}
}
/**
*
* TERMINATE SERVER
*
* When requesting to terminate a server in VirtFusion, we leave it set to
* the default 5-minute delay allowing to un-terminate in VirtFusion if the
* request was done in error.
*
*/
public function terminateAccount($params)
{
$service = Database::getSystemService($params['serviceid']);
if ($service) {
$whmcsService = Database::getWhmcsService($params['serviceid']);
$cp = $this->getCP($whmcsService->server);
$request = $this->initCurl($cp['token']);
$data = $request->delete($cp['url'] . '/servers/' . $service->server_id);
$data = json_decode($data);
Log::insert(__FUNCTION__, $request->getRequestInfo(), $data);
switch ($request->getRequestInfo('http_code')) {
case 204:
Database::deleteSystemService($params['serviceid']);
$this->updateWhmcsServiceParamsOnDestroy($params['serviceid']);
return 'success';
break;
case 404:
if (property_exists($data, 'msg')) {
if ($data->msg == 'server not found') {
Database::deleteSystemService($params['serviceid']);
return 'success';
} else {
return '404 was returned from the web service with the msg property but doesn\'t contain appropriate data to process a termination.';
}
} else {
return '404 was returned from the web service without the msg property. The service may be currently unavailable.';
}
break;
default:
return 'Termination request failed. The web service reported HTTP code ' . $request->getRequestInfo('http_code');
break;
}
}
return 'Service not found. Termination routine has already been run?';
}
/**
*
* SUSPEND SERVER
*
* When requesting to suspend a server in VirtFusion it may be delayed if another action
* is being processed. This function will return success if the server is either suspended
* now or has been queued for suspension.
*
*/
public function suspendAccount($params)
{
$service = Database::getSystemService($params['serviceid']);
if ($service) {
$whmcsService = Database::getWhmcsService($params['serviceid']);
$cp = $this->getCP($whmcsService->server);
$request = $this->initCurl($cp['token']);
$data = $request->post($cp['url'] . '/servers/' . $service->server_id . '/suspend');
$data = json_decode($data);
Log::insert(__FUNCTION__, $request->getRequestInfo(), $data);
switch ($request->getRequestInfo('http_code')) {
case 204:
return 'success';
break;
case 404:
if (property_exists($data, 'msg')) {
if ($data->msg == 'server not found') {
Database::deleteSystemService($params['serviceid']);
return 'success';
} else {
return '404 was returned from the web service with the msg property but doesn\'t contain appropriate data to process a suspension.';
}
} else {
return '404 was returned from the web service without the msg property. The service may be currently unavailable.';
}
break;
case 423:
if (property_exists($data, 'msg')) {
return $data->msg;
}
default:
return 'Suspend request failed. The web service reported HTTP code ' . $request->getRequestInfo('http_code');
break;
}
}
return 'Service not found.';
}
function updateServerObject($params)
{
$service = Database::getSystemService($params['serviceid']);
if ($service) {
$whmcsService = Database::getWhmcsService($params['serviceid']);
$cp = $this->getCP($whmcsService->server);
$request = $this->initCurl($cp['token']);
$data = $request->get($cp['url'] . '/servers/' . $service->server_id);
$data = json_decode($data);
Log::insert(__FUNCTION__, $request->getRequestInfo(), $data);
switch ($request->getRequestInfo('http_code')) {
case 200:
Database::updateSystemServiceServerObject($params['serviceid'], $data);
$this->updateWhmcsServiceParamsOnServerObject($params['serviceid'], $data);
return 'success';
break;
default:
return 'Request failed. The web service reported HTTP code ' . $request->getRequestInfo('http_code');
break;
}
}
return 'Service not found.';
}
public function unsuspendAccount($params)
{
$service = Database::getSystemService($params['serviceid']);
if ($service) {
$whmcsService = Database::getWhmcsService($params['serviceid']);
$cp = $this->getCP($whmcsService->server);
$request = $this->initCurl($cp['token']);
$data = $request->post($cp['url'] . '/servers/' . $service->server_id . '/unsuspend');
$data = json_decode($data);
Log::insert(__FUNCTION__, $request->getRequestInfo(), $data);
switch ($request->getRequestInfo('http_code')) {
case 204:
return 'success';
break;
case 404:
if (property_exists($data, 'msg')) {
if ($data->msg == 'server not found') {
Database::deleteSystemService($params['serviceid']);
return 'success';
} else {
return '404 was returned from the web service with the msg property but doesn\'t contain appropriate data to process an unsuspension.';
}
} else {
return '404 was returned from the web service without the msg property. The service may be currently unavailable.';
}
break;
case 423:
if (property_exists($data, 'msg')) {
return $data->msg;
}
default:
return 'Unsuspend request failed. The web service reported HTTP code ' . $request->getRequestInfo('http_code');
break;
}
}
return 'Service not found';
}
public function adminServicesTabFields($params)
{
$serverId = '';
$serverObject = '';
$service = Database::getSystemService($params['serviceid']);
$systemUrl = Database::getSystemUrl();
if ($service) {
$serverId = $service->server_id;
$serverObject = $service->server_object;
}
$fields = [
'Server ID' => AdminHTML::serverId($serverId),
'Server Info' => AdminHTML::serverInfo($systemUrl, $params['serviceid']),
'Server Object' => AdminHTML::serverObject($serverObject),
];
if ($params['status'] != 'Terminated') {
$fields['Options'] = AdminHTML::options($systemUrl, $params['serviceid']);
}
return $fields;
}
public function adminServicesTabFieldsSave($params)
{
if ($_POST['modulefields'][0] == '') {
Database::deleteSystemService($params['serviceid']);
} else {
Database::updateSystemServiceServerId($params['serviceid'], $_POST['modulefields'][0]);
}
}
public function clientArea($params)
{
$serverHostname = null;
if (array_key_exists('serverhostname', $params)) {
$serverHostname = $params['serverhostname'];
}
try {
return [
'tabOverviewReplacementTemplate' => 'overview',
'templateVariables' => [
'systemURL' => Database::getSystemUrl(),
'serviceStatus' => $params['status'],
'serverHostname' => $serverHostname,
],
];
} catch (\Throwable $e) {
Log::insert(__FUNCTION__, $params, $e->getMessage());
return [
'tabOverviewReplacementTemplate' => 'error',
'templateVariables' => [],
];
}
}
}

62
lib/ServerResource.php Normal file
View File

@@ -0,0 +1,62 @@
<?php
namespace WHMCS\Module\Server\VirtFusionDirect;
class ServerResource
{
public function process($data)
{
$server = json_decode(json_encode($data->data), true);
$traffic = '∞';
if ($server['settings']['resources']['traffic']) {
if ($server['settings']['resources']['traffic'] > 0) {
$traffic = $server['settings']['resources']['traffic'] . ' GB';
}
}
$data = [
'name' => $server['name'] ?: '-',
'hostname' => $server['hostname'] ?: '-',
'memory' => $server['settings']['resources']['memory'] . ' MB',
'traffic' => $traffic,
'storage' => $server['settings']['resources']['storage'] . ' GB',
'cpu' => $server['settings']['resources']['cpuCores'] . ' Core(s)',
'primaryNetwork' => [
'ipv4' => ['-'],
'ipv4Unformatted' => [],
'ipv6' => ['-'],
'ipv6Unformatted' => [],
]
];
if (array_key_exists('network', $server)) {
if (array_key_exists('interfaces', $server['network'])) {
if (count($server['network']['interfaces'])) {
if (count($server['network']['interfaces'][0]['ipv4'])) {
$data['primaryNetwork']['ipv4'] = [];
foreach ($server['network']['interfaces'][0]['ipv4'] as $ip) {
$data['primaryNetwork']['ipv4'][] = $ip['address'];
}
}
if (count($server['network']['interfaces'][0]['ipv6'])) {
$data['primaryNetwork']['ipv6'] = [];
foreach ($server['network']['interfaces'][0]['ipv6'] as $ip) {
$data['primaryNetwork']['ipv6'][] = $ip['subnet'] . '/' . $ip['cidr'];
}
}
}
}
}
$data['primaryNetwork']['ipv4Unformatted'] = $data['primaryNetwork']['ipv4'];
$data['primaryNetwork']['ipv6Unformatted'] = $data['primaryNetwork']['ipv6'];
$data['primaryNetwork']['ipv4'] = implode(', ', $data['primaryNetwork']['ipv4']);
$data['primaryNetwork']['ipv6'] = implode(', ', $data['primaryNetwork']['ipv6']);
return $data;
}
}

1
templates/css/module.css Normal file
View File

@@ -0,0 +1 @@
.vf-bold{font-weight:800}.vf-small{font-size:.9rem}.vf-button{font-size:.8rem;padding:.95rem 1.5rem;font-weight:600}.vf-button-small{font-size:.8rem;padding:.75rem 1.3rem;font-weight:500}.vf-spinner-margin{margin-right:7px}.vf-badge{font-size:.8rem;padding:.5rem .9rem;text-transform:uppercase;font-weight:800}.vf-badge-active{background-color:rgba(32,177,0,.12);color:#276900;border-radius:6px}.vf-badge-awaiting{background-color:rgba(177,89,0,.12);color:#692000;border-radius:6px}#vf-login-button-spinner{display:none}#vf-password-reset-button-spinner{display:none}#vf-password-reset-error{display:none}#vf-password-reset-success{display:none}#vf-login-error{display:none}#vf-server-info{display:none}#vf-server-info-error{display:none}#vf-server-info-loader{min-height:136px}#vf-loading{display:inline-block;width:30px;height:30px;border:3px solid rgba(225,224,224,.3);border-radius:50%;border-top-color:#0e151a;animation:vf-spin 1s ease-in-out infinite;-webkit-animation:vf-spin 1s ease-in-out infinite}.vf-loader{margin:30px}@keyframes vf-spin{to{transform:rotate(360deg)}}@-webkit-keyframes vf-spin{to{transform:rotate(360deg)}}#vf-server-info-error{margin:10px}

1
templates/error.tpl Normal file
View File

@@ -0,0 +1 @@
<div class="alert alert-danger"><p>Oops! Something went wrong.</p></div><p>Please go back and try again.</p><p>If the problem persists, please contact support.</p>

1
templates/js/module.js Normal file
View File

@@ -0,0 +1 @@
function vfServerData(e,r){$("#vf-server-info-error").hide(),$.ajax({type:"GET",dataType:"json",url:r+"modules/servers/VirtFusionDirect/client.php?serviceID="+e+"&action=serverData"}).done(function(e){e.success?($("#vf-data-server-name").text(e.data.name),$("#vf-data-server-hostname").text(e.data.hostname),$("#vf-data-server-memory").text(e.data.memory),$("#vf-data-server-traffic").text(e.data.traffic),$("#vf-data-server-storage").text(e.data.storage),$("#vf-data-server-cpu").text(e.data.cpu),$("#vf-data-server-ipv4").text(e.data.primaryNetwork.ipv4),$("#vf-data-server-ipv6").text(e.data.primaryNetwork.ipv6),$("#vf-server-info").show()):($("#vf-server-info-error").show(),$("#vf-server-info").hide())}).fail(function(e){}).always(function(e){$("#vf-server-info-loader-container").hide()})}function vfServerDataAdmin(e,r){$("#vf-loader").show(),$("#vf-server-info").hide(),$("#vf-server-info-error").hide(),$.ajax({type:"GET",dataType:"json",url:r+"modules/servers/VirtFusionDirect/admin.php?serviceID="+e+"&action=serverData"}).done(function(e){e.success?($("#vf-data-server-name").text(e.data.name),$("#vf-data-server-hostname").text(e.data.hostname),$("#vf-data-server-memory").text(e.data.memory),$("#vf-data-server-traffic").text(e.data.traffic),$("#vf-data-server-storage").text(e.data.storage),$("#vf-data-server-cpu").text(e.data.cpu),$("#vf-data-server-ipv4").text(e.data.primaryNetwork.ipv4),$("#vf-data-server-ipv6").text(e.data.primaryNetwork.ipv6),$("#vf-server-info").show()):($("#vf-server-info-error").show(),$("#vf-server-info-error-message").text(e.errors),$("#vf-server-info").hide())}).fail(function(e){}).always(function(e){$("#vf-loader").hide()})}function vfUserPasswordReset(e,r){$("#vf-password-reset-button-spinner").show(),$("#vf-password-reset-error").hide(),$("#vf-password-reset-success").hide(),$.ajax({type:"GET",dataType:"json",url:r+"modules/servers/VirtFusionDirect/client.php?serviceID="+e+"&action=resetPassword"}).done(function(e){e.success?($("#vf-password-reset-success").show(),$("#vf-data-user-email").text(e.data.email),$("#vf-data-user-password").text(e.data.password),console.log(e.data.email)):$("#vf-password-reset-error").show()}).fail(function(e){}).always(function(e){$("#vf-password-reset-button-spinner").hide()})}function vfLoginAsServerOwner(e,r,t=!0){vfLoginError(!1),$("#vf-login-button").prop("disabled",!0),$("#vf-login-button-spinner").show(),$.ajax({type:"GET",dataType:"json",url:r+"modules/servers/VirtFusionDirect/client.php?serviceID="+e+"&action=loginAsServerOwner"}).done(function(e){e.success?e.token_url&&(t?window.open(e.token_url):window.location.href=e.token_url):vfLoginError(!0)}).fail(function(e){vfLoginError(!0)}).always(function(e){$("#vf-login-button-spinner").hide(),$("#vf-login-button").prop("disabled",!1)})}function vfLoginError(e,r="Oops! Something went wrong. Try again later."){e?($("#vf-login-error").text(r),$("#vf-login-error").show()):$("#vf-login-error").hide()}function impersonateServerOwner(e,r){$.ajax({type:"GET",dataType:"json",url:r+"modules/servers/VirtFusionDirect/admin.php?serviceID="+e+"&action=impersonateServerOwner"}).done(function(e){e.success&&e.user&&window.open(e.url+"/_imp/in/"+e.user.id+"/-")}).fail(function(e){}).always(function(e){})}

1
templates/overview.tpl Normal file

File diff suppressed because one or more lines are too long