Sindbad~EG File Manager
<?php
namespace O2switch\CpanelLib\Client;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\GuzzleException;
use O2switch\CpanelLib\DataEntity\ApiVhost;
use O2switch\CpanelLib\DataEntity\NginxVarnishConfig;
use O2switch\CpanelLib\Exception\InvalidApiArgument;
use O2switch\CpanelLib\Exception\InvalidApiResponse;
use O2switch\CpanelLib\Exception\UnsuccessfulApiResponse;
use O2switch\CpanelLib\Helper\Truncator;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Log\LoggerInterface;
class NginxVarnish
{
// Routes
const R_NGINX_DELETE = '/api/nginx/delete';
const R_NGINX_VHOST = '/api/nginx/'; // + templateName
const R_NGINX_USER_DELETE = '/api/nginx/delete-all/'; // + username
const R_VARNISH_DELETE = '/api/varnish/delete';
const R_VARNISH_VHOST = "/api/varnish/"; // + templateName
const R_VARNISH_USER_DELETE = '/api/varnish/delete-all/'; // + username
const R_LSLB_DELETE = '/api/lslb/delete';
const R_LSLB_VHOST = '/api/lslb/vhost';
const R_LSLB_USER_DELETE = '/api/varnish/delete-all/'; // + username
const R_HEALTH = '/api/health/'; // + appName
const APP_NGINX = 1;
const APP_VARNISH = 2;
const APP_LSCACHE = 3;
/** @var NginxVarnishConfig */
protected $config;
/** @var Client */
private $httpClient;
/** @var LoggerInterface */
private $logger;
/** @var int */
private $timeout = 120;
public function __construct(Client $httpClient, LoggerInterface $logger){
$this->httpClient = $httpClient;
$this->logger = $logger;
}
/**
* Remove all vhosts for a given user. For instance, to clean up on account deletion.
* @param string $appType
* @param string $cpUser
* @throws InvalidApiArgument
* @throws InvalidApiResponse
* @throws UnsuccessfulApiResponse
*/
public function removeVhostsByUser(string $appType, string $cpUser){
// NOTE : UNTESTED.
switch($appType){
case 'nginx':
case 'forwarder':
$route = self::R_NGINX_USER_DELETE;
break;
case 'varnish':
case 'xtremcache':
$route = self::R_VARNISH_USER_DELETE;
break;
case 'litespeed':
case 'lslb':
$route = self::R_LSLB_USER_DELETE;
break;
default:
throw new InvalidApiArgument(sprintf("The appType '%s' provided to '%s' is invalid.", $appType, __METHOD__));
}
$route .= $cpUser;
$response = $this->rawCall($route, ['cpUser' => $cpUser]);
if(!isset($response['status']) || !$response['status']){
throw new UnsuccessfulApiResponse(sprintf("The call to '%s' returned json but the status flag indicate an error occurred.", $route));
}
}
/**
* Remove a Vhost
* @param string $appType One of nginx, forwarder, varnish (or xtremcache), litespeed (or lslb)
* @param string $domain
* @param string $cpUser
* @throws InvalidApiArgument
* @throws InvalidApiResponse
* @throws UnsuccessfulApiResponse
*/
public function removeVhost(string $appType, string $domain, string $cpUser) : void {
switch($appType){
case 'nginx':
case 'forwarder':
$route = self::R_NGINX_DELETE;
break;
case 'varnish':
case 'xtremcache':
$route = self::R_VARNISH_DELETE;
break;
case 'litespeed':
case 'lslb':
$route = self::R_LSLB_DELETE;
break;
default:
throw new InvalidApiArgument(sprintf("The appType '%s' provided to '%s' is invalid.", $appType, __METHOD__));
}
$response = $this->rawCall($route, [
'mainDomain' => $domain,
'cpUser' => $cpUser,
]);
if(!isset($response['status']) || !$response['status']){
throw new UnsuccessfulApiResponse(sprintf("The call to '%s' returned json but the status flag indicate an error occurred.", $route));
}
}
/**
* Configure a Vhost.
* @param string $appType Possible values are nginx, forwarder, varnish, litespeed, lslb.
* @param ApiVhost $data
* @throws InvalidApiArgument
* @throws InvalidApiResponse
* @throws UnsuccessfulApiResponse
*/
public function configureVhost(string $appType, ApiVhost $data) : ApiVhost {
switch($appType){
case 'nginx':
case 'forwarder':
$route = self::R_NGINX_VHOST . $data->getTemplateName();
break;
case 'varnish':
$route = self::R_VARNISH_VHOST . $data->getTemplateName();
break;
case 'litespeed':
case 'lslb':
$route = self::R_LSLB_VHOST;
break;
default:
throw new InvalidApiArgument(sprintf("The appType '%s' provided to '%s' is invalid.", $appType, __METHOD__));
}
$response = $this->rawCall($route, $data->toArray());
if(!isset($response['status']) || !$response['status']){
throw new UnsuccessfulApiResponse(sprintf("The call to '%s' returned json but the status flag indicate an error occurred.", $route));
}
return new ApiVhost($response['response']);
}
/**
* Return true if the $appType API is online and seems OK.
* @param string $appType
* @return bool
*/
public function health(string $appType) : bool{
try {
$response = $this->rawCall(self::R_HEALTH . $appType);
} catch (\Exception $e){
return false;
}
return isset($response['status']) && $response['status'] === true;
}
/**
* Low level function to make a custom call to the API. Please dont use this directory and instead use the shortcuts
* methods or higher level API Call functions.
* @param string $route
* @param array $params
* @param array $query
* @param string $method
* @return array
* @throws InvalidApiResponse
* @throws \RuntimeException
*/
public function rawCall(string $route, array $params = [], array $query = [], string $method = 'POST') : array {
if(!$this->config instanceof NginxVarnishConfig){
throw new \RuntimeException(sprintf("Before calling the '%s' method you must first set the '%s' property with the appropriate setter.", __METHOD__, 'config'));
}
try {
$response = $this->httpClient->request(
$method,
$this->getEndpoint($route, $query),
[
'connect_timeout' => $this->timeout,
'form_params' => $this->getParams($params),
'verify' => false,
]
);
} catch (\Exception | GuzzleException $e) {
$context = [
'exception' => $e->getMessage(),
'params' => $this->getParams($params, true),
];
$this->logger->warning(
sprintf("NginxVarnish [KO] (exception) %s %s", $method, $this->getEndpoint($route, $query)),
$context
);
throw new InvalidApiResponse(sprintf("NginxVarnish %s %s. Error received: %s", $method, $this->getEndpoint($route, $query), $e->getMessage()));
}
$response = $response->getBody()->getContents();
$context = [
'params' => $this->getParams($params, true),
'response' => Truncator::truncate($response, 200),
];
$response = json_decode($response, true);
if($response === false || !is_array($response) || empty($response)){
$this->logger->warning(
sprintf("NginxVarnish [KO] (!json) %s %s", $method, $this->getEndpoint($route, $query)),
$context
);
throw new InvalidApiResponse(sprintf("NginxVarnish %s %s. Error received: Not a Json Response.", $method, $this->getEndpoint($route, $query)));
}
$context['response'] = Truncator::truncate($response);
$this->logger->info(
sprintf("NginxVarnish [OK] %s %s", $method, $this->getEndpoint($route, $query)),
$context
);
return $response;
}
/**
* @param NginxVarnishConfig $config
* @return NginxVarnish
*/
public function setConfig(NginxVarnishConfig $config): NginxVarnish
{
$this->config = $config;
return $this;
}
/**
* @param int $timeout
* @return NginxVarnish
*/
public function setTimeout(int $timeout): NginxVarnish
{
$this->timeout = $timeout;
return $this;
}
private function getEndpoint(string $route, array $query = []){
$q = http_build_query($query);
return
'https://'
. $this->config->getServerIp()
. ':' . $this->config->getPort()
. (strpos($route, '/') === 0 ? '' : '/') . $route
. (!empty($q) ? ('?') . $q : '')
;
}
// TODO : Initially, this was public ... find out why. Not sure it's needed anymore, maybe for the hook system ?
private function getParams(array $params = [], bool $debug = false) : array
{
$params['token'] = $this->config->getToken();
$params['hash'] = $this->createHash($params);
if($debug){
$params = Truncator::truncate($params);
}
return $params;
}
private function createHash(array $data=[]) : string
{
ksort($data);
$json = json_encode($data);
$hash = hash_hmac($this->config->getAlgo(), $json, $this->config->getSecretHash());
return $hash;
}
}
Sindbad File Manager Version 1.0, Coded By Sindbad EG ~ The Terrorists