- new "ESI monitoring" UI dialog, closed #748
- new "Redis monitoring" UI on `/setup` page, closed #745 - improved request handling for 3rd party APIs (ESI, SSO, GitHub) see [exodus4d/pathfinder_esi/README.md](https://github.com/exodus4d/pathfinder_esi/blob/b5d4b19/README.md) - improved `/setup` page, new actions for clear cache/Redis data
This commit is contained in:
@@ -41,16 +41,35 @@ LANGUAGE = en-US
|
||||
SEED = {{ md5(@SERVER.SERVER_NAME) }}
|
||||
|
||||
; Cache backend
|
||||
; This sets the primary cache backend for Pathfinder. Used for e.g.:
|
||||
; DB query, DB schema, HTTP response, or even simple key->value caches
|
||||
; Can handle Redis, Memcache module, APC, WinCache, XCache and a filesystem-based cache.
|
||||
; Hint: Redis is recommended and gives the best performance.
|
||||
; Syntax: folder=[DIR] | redis=[SERVER]
|
||||
; Default: folder=tmp/cache/
|
||||
; Value: folder=[DIR]
|
||||
; Value: FALSE
|
||||
; - Disables caching
|
||||
; folder=[DIR]
|
||||
; - Cache data is stored on disc
|
||||
; redis=[SERVER]
|
||||
; - Cache data is stored in Redis (e.g. redis=localhost:6379)
|
||||
; - Cache data is stored in Redis. redis=[host]:[port]:[db] (e.g. redis=localhost:6379:1)
|
||||
CACHE = folder=tmp/cache/
|
||||
|
||||
; Cache backend for API data
|
||||
; This sets the cache backend for API response data and other temp data relates to API requests.
|
||||
; Response data with proper 'Expire' HTTP Header will be cached here and speed up further requests.
|
||||
; As default 'API_CACHE' and 'CACHE' share the same backend (cache location)
|
||||
; Hint1: You can specify e.g. a dedicated Redis DB here, then 'CACHE' and 'API_CACHE' can be cleared independently
|
||||
; Hint2: Redis is recommended and gives the best performance.
|
||||
; Default: {{@CACHE}}
|
||||
; Value: FALSE
|
||||
; - Disables caching
|
||||
; folder=[DIR]
|
||||
; - Cache data is stored on disc
|
||||
; redis=[SERVER]
|
||||
; - Cache data is stored in Redis. redis=[host]:[port]:[db] (e.g. redis=localhost:6379:2)
|
||||
API_CACHE = {{@CACHE}}
|
||||
|
||||
; Cache backend used by PHPs Session handler.
|
||||
; Hint1: Best performance and recommended configuration for Pathfinder is to configured Redis as PHPs default Session handler
|
||||
; in your php.ini and set 'default' value here in order to use Redis (fastest)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<?php
|
||||
<?php /** @noinspection PhpUndefinedMethodInspection */
|
||||
|
||||
/**
|
||||
* Created by PhpStorm.
|
||||
* User: exodus4d
|
||||
@@ -7,6 +8,8 @@
|
||||
*/
|
||||
|
||||
namespace Controller\Api;
|
||||
|
||||
|
||||
use lib\Config;
|
||||
use Controller;
|
||||
|
||||
@@ -18,135 +21,68 @@ use Controller;
|
||||
*/
|
||||
class GitHub extends Controller\Controller {
|
||||
|
||||
protected function getBaseRequestOptions() : array {
|
||||
return [
|
||||
'timeout' => 3,
|
||||
'user_agent' => $this->getUserAgent(),
|
||||
'follow_location' => false // otherwise CURLOPT_FOLLOWLOCATION will fail
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* get HTTP request options for API (curl) request
|
||||
* @return array
|
||||
*/
|
||||
protected function getRequestReleaseOptions() : array {
|
||||
$options = $this->getBaseRequestOptions();
|
||||
$options['method'] = 'GET';
|
||||
return $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* get HTTP request options for API (curl) request
|
||||
* @param string $text
|
||||
* @return array
|
||||
*/
|
||||
protected function getRequestMarkdownOptions(string $text) : array {
|
||||
$params = [
|
||||
'text' => $text,
|
||||
'mode' => 'gfm',
|
||||
'context' => 'exodus4d/pathfinder'
|
||||
];
|
||||
|
||||
$options = $this->getBaseRequestOptions();
|
||||
$options['method'] = 'POST';
|
||||
$options['content'] = json_encode($params, JSON_UNESCAPED_SLASHES);
|
||||
return $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* get release information from GitHub
|
||||
* @param \Base $f3
|
||||
*/
|
||||
public function releases(\Base $f3){
|
||||
$cacheKey = 'CACHE_GITHUB_RELEASES';
|
||||
$ttl = 60 * 30; // 30min
|
||||
$releaseCount = 4;
|
||||
|
||||
if( !$f3->exists($cacheKey, $return) ){
|
||||
$apiReleasePath = Config::getPathfinderData('api.git_hub') . '/repos/exodus4d/pathfinder/releases';
|
||||
$apiMarkdownPath = Config::getPathfinderData('api.git_hub') . '/markdown';
|
||||
$return = (object) [];
|
||||
$return->releasesData = [];
|
||||
$return->version = (object) [];
|
||||
$return->version->current = Config::getPathfinderData('version');
|
||||
$return->version->last = '';
|
||||
$return->version->delta = null;
|
||||
$return->version->dev = false;
|
||||
|
||||
// build request URL
|
||||
$apiResponse = \Web::instance()->request($apiReleasePath, $this->getRequestReleaseOptions() );
|
||||
$md = \Markdown::instance();
|
||||
|
||||
if($apiResponse['body']){
|
||||
$return = (object) [];
|
||||
$return->releasesData = [];
|
||||
$return->version = (object) [];
|
||||
$return->version->current = Config::getPathfinderData('version');
|
||||
$return->version->last = '';
|
||||
$return->version->delta = null;
|
||||
$return->version->dev = false;
|
||||
$releases = $f3->gitHubClient()->getProjectReleases('exodus4d/pathfinder', $releaseCount);
|
||||
|
||||
// request succeeded -> format "Markdown" to "HTML"
|
||||
// result is JSON formed
|
||||
$releasesData = (array)json_decode($apiResponse['body']);
|
||||
|
||||
// check max release count
|
||||
if(count($releasesData) > $releaseCount){
|
||||
$releasesData = array_slice($releasesData, 0, $releaseCount);
|
||||
foreach($releases as $key => &$release){
|
||||
// check version ------------------------------------------------------------------------------------------
|
||||
if($key === 0){
|
||||
$return->version->last = $release['name'];
|
||||
if(version_compare( $return->version->current, $return->version->last, '>')){
|
||||
$return->version->dev = true;
|
||||
}
|
||||
|
||||
$md = \Markdown::instance();
|
||||
foreach($releasesData as $key => &$releaseData){
|
||||
// check version ----------------------------------------------------------------------------------
|
||||
if($key === 0){
|
||||
$return->version->last = $releaseData->tag_name;
|
||||
|
||||
if(version_compare( $return->version->current, $return->version->last, '>')){
|
||||
$return->version->dev = true;
|
||||
}
|
||||
}
|
||||
|
||||
if(
|
||||
!$return->version->dev &&
|
||||
version_compare( $releaseData->tag_name, $return->version->current, '>=')
|
||||
){
|
||||
$return->version->delta = ($key === count($releasesData) - 1) ? '>= ' . $key : $key;
|
||||
}
|
||||
|
||||
// format body ------------------------------------------------------------------------------------
|
||||
if(isset($releaseData->body)){
|
||||
$body = $releaseData->body;
|
||||
|
||||
// remove "update information" from release text
|
||||
// -> keep everything until first "***" -> horizontal line
|
||||
if( ($pos = strpos($body, '***')) !== false){
|
||||
$body = substr($body, 0, $pos);
|
||||
}
|
||||
|
||||
// convert list style
|
||||
$body = str_replace(' - ', '* ', $body );
|
||||
|
||||
// convert Markdown to HTML -> use either gitHub API (in oder to create abs, issue links)
|
||||
// -> or F3´s markdown as fallback
|
||||
$markdownResponse = \Web::instance()->request($apiMarkdownPath, $this->getRequestMarkdownOptions($body) );
|
||||
|
||||
if($markdownResponse['body']){
|
||||
$body = $markdownResponse['body'];
|
||||
}else{
|
||||
$body = $md->convert( trim($body) );
|
||||
}
|
||||
|
||||
$releaseData->body = $body;
|
||||
}
|
||||
}
|
||||
|
||||
$return->releasesData = $releasesData;
|
||||
|
||||
$f3->set($cacheKey, $return, $ttl);
|
||||
}else{
|
||||
// request failed -> cache failed result (respect API request limit)
|
||||
$f3->set($cacheKey, false, 60 * 15);
|
||||
}
|
||||
|
||||
if(
|
||||
!$return->version->dev &&
|
||||
version_compare($release['name'], $return->version->current, '>=')
|
||||
){
|
||||
$return->version->delta = ($key === count($releases) - 1) ? '>= ' . $key : $key;
|
||||
}
|
||||
|
||||
// format body ------------------------------------------------------------------------------------
|
||||
$body = $release['body'];
|
||||
|
||||
// remove "update information" from release text
|
||||
// -> keep everything until first "***" -> horizontal line
|
||||
if( ($pos = strpos($body, '***')) !== false){
|
||||
$body = substr($body, 0, $pos);
|
||||
}
|
||||
|
||||
// convert list style
|
||||
$body = str_replace(' - ', '* ', $body);
|
||||
|
||||
// convert Markdown to HTML -> use either gitHub API (in oder to create abs, issue links)
|
||||
// -> or F3´s markdown as fallback
|
||||
$html = $f3->gitHubClient()->markdownToHtml('exodus4d/pathfinder', $body);
|
||||
|
||||
if(!empty($html)){
|
||||
$body = $html;
|
||||
}else{
|
||||
$body = $md->convert(trim($body));
|
||||
}
|
||||
|
||||
$release['body'] = $body;
|
||||
}
|
||||
|
||||
// set 503 if service unavailable or temp cached data = false
|
||||
if( !$f3->get($cacheKey) ){
|
||||
$f3->status(503);
|
||||
}
|
||||
$return->releasesData = $releases;
|
||||
|
||||
echo json_encode($f3->get($cacheKey));
|
||||
echo json_encode($return);
|
||||
}
|
||||
}
|
||||
@@ -879,7 +879,7 @@ class Map extends Controller\AccessController {
|
||||
|
||||
// update current location
|
||||
// -> suppress temporary timeout errors
|
||||
$activeCharacter = $activeCharacter->updateLog(['suppressHTTPErrors' => true]);
|
||||
$activeCharacter = $activeCharacter->updateLog();
|
||||
|
||||
if( !empty($mapIds) ){
|
||||
// IMPORTANT for now -> just update a single map (save performance)
|
||||
|
||||
@@ -590,7 +590,7 @@ class Route extends Controller\AccessController {
|
||||
'connections' => $connections
|
||||
];
|
||||
|
||||
$result = $this->getF3()->ccpClient->getRouteData($systemFromId, $systemToId, $options);
|
||||
$result = $this->getF3()->ccpClient()->getRouteData($systemFromId, $systemToId, $options);
|
||||
|
||||
// format result ------------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
@@ -135,7 +135,7 @@ class System extends Controller\AccessController {
|
||||
];
|
||||
|
||||
foreach($postData['systemData'] as $systemData){
|
||||
$response = $f3->ccpClient->setWaypoint($systemData['systemId'], $accessToken, $options);
|
||||
$response = $f3->ccpClient()->setWaypoint($systemData['systemId'], $accessToken, $options);
|
||||
|
||||
if(empty($response)){
|
||||
$return->systemData[] = $systemData;
|
||||
|
||||
@@ -221,7 +221,7 @@ class User extends Controller\Controller{
|
||||
if( $targetId = (int)$data['targetId']){
|
||||
$activeCharacter = $this->getCharacter();
|
||||
|
||||
$response = $f3->ccpClient->openWindow($targetId, $activeCharacter->getAccessToken());
|
||||
$response = $f3->ccpClient()->openWindow($targetId, $activeCharacter->getAccessToken());
|
||||
|
||||
if(empty($response)){
|
||||
$return->targetId = $targetId;
|
||||
|
||||
@@ -1,17 +1,18 @@
|
||||
<?php
|
||||
<?php /** @noinspection PhpUndefinedMethodInspection */
|
||||
|
||||
/**
|
||||
* Created by PhpStorm.
|
||||
* User: Exodus
|
||||
* Date: 23.01.2016
|
||||
* Time: 17:18
|
||||
*
|
||||
* Handles access to EVE-Online "ESI API" and "SSO" auth functions
|
||||
* Handles access to EVE-Online "ESI API" and "SSO" oAuth 2.0 functions
|
||||
* - Add your API credentials in "environment.ini"
|
||||
* - Check "PATHFINDER.API" in "pathfinder.ini" for correct API URLs
|
||||
* Hint: \Web::instance()->request automatically caches responses by their response "Cache-Control" header!
|
||||
*/
|
||||
|
||||
namespace Controller\Ccp;
|
||||
|
||||
use Controller;
|
||||
use Controller\Api as Api;
|
||||
use Model;
|
||||
@@ -124,7 +125,7 @@ class Sso extends Api\User{
|
||||
* @param array $scopes
|
||||
* @param string $rootAlias
|
||||
*/
|
||||
private function rerouteAuthorization(\Base $f3, $scopes = [], $rootAlias = 'login'){
|
||||
private function rerouteAuthorization(\Base $f3, array $scopes = [], string $rootAlias = 'login'){
|
||||
if( !empty( Controller\Controller::getEnvironmentData('CCP_SSO_CLIENT_ID') ) ){
|
||||
// used for "state" check between request and callback
|
||||
$state = bin2hex( openssl_random_pseudo_bytes(12) );
|
||||
@@ -138,7 +139,9 @@ class Sso extends Api\User{
|
||||
'state' => $state
|
||||
];
|
||||
|
||||
$ssoAuthUrl = self::getAuthorizationEndpoint() . '?' . http_build_query($urlParams, '', '&', PHP_QUERY_RFC3986 );
|
||||
$ssoAuthUrl = $f3->ssoClient()->getUrl();
|
||||
$ssoAuthUrl .= $f3->ssoClient()->getAuthorizationEndpointURI();
|
||||
$ssoAuthUrl .= '?' . http_build_query($urlParams, '', '&', PHP_QUERY_RFC3986 );
|
||||
|
||||
$f3->status(302);
|
||||
$f3->reroute($ssoAuthUrl);
|
||||
@@ -186,22 +189,22 @@ class Sso extends Api\User{
|
||||
// login succeeded -> get basic character data for current login
|
||||
$verificationCharacterData = $this->verifyCharacterData($accessData->accessToken);
|
||||
|
||||
if( !is_null($verificationCharacterData)){
|
||||
if( !empty($verificationCharacterData) ){
|
||||
|
||||
// check if login is restricted to a characterID
|
||||
|
||||
// verification available data. Data is needed for "ownerHash" check
|
||||
|
||||
// get character data from ESI
|
||||
$characterData = $this->getCharacterData((int)$verificationCharacterData->CharacterID);
|
||||
$characterData = $this->getCharacterData((int)$verificationCharacterData['characterId']);
|
||||
|
||||
if( isset($characterData->character) ){
|
||||
// add "ownerHash" and SSO tokens
|
||||
$characterData->character['ownerHash'] = $verificationCharacterData->CharacterOwnerHash;
|
||||
$characterData->character['ownerHash'] = $verificationCharacterData['characterOwnerHash'];
|
||||
$characterData->character['esiAccessToken'] = $accessData->accessToken;
|
||||
$characterData->character['esiAccessTokenExpires'] = $accessData->esiAccessTokenExpires;
|
||||
$characterData->character['esiRefreshToken'] = $accessData->refreshToken;
|
||||
$characterData->character['esiScopes'] = Lib\Util::convertScopesString($verificationCharacterData->Scopes);
|
||||
$characterData->character['esiScopes'] = $verificationCharacterData['scopes'];
|
||||
|
||||
// add/update static character data
|
||||
$characterModel = $this->updateCharacter($characterData);
|
||||
@@ -363,16 +366,15 @@ class Sso extends Api\User{
|
||||
* get new "access_token" by an existing "refresh_token"
|
||||
* -> if "access_token" is expired, this function gets a fresh one
|
||||
* @param string $refreshToken
|
||||
* @param array $additionalOptions
|
||||
* @return \stdClass
|
||||
*/
|
||||
public function refreshAccessToken(string $refreshToken, array $additionalOptions = []){
|
||||
public function refreshAccessToken(string $refreshToken){
|
||||
$requestParams = [
|
||||
'grant_type' => 'refresh_token',
|
||||
'refresh_token' => $refreshToken
|
||||
];
|
||||
|
||||
return $this->requestAccessData($requestParams, $additionalOptions);
|
||||
return $this->requestAccessData($requestParams);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -380,75 +382,41 @@ class Sso extends Api\User{
|
||||
* -> this can either be done by sending a valid "authorization code"
|
||||
* OR by providing a valid "refresh_token"
|
||||
* @param array $requestParams
|
||||
* @param array $additionalOptions
|
||||
* @return \stdClass
|
||||
*/
|
||||
protected function requestAccessData(array $requestParams, array $additionalOptions = []){
|
||||
$verifyAuthCodeUrl = self::getVerifyAuthorizationCodeEndpoint();
|
||||
$verifyAuthCodeUrlParts = parse_url($verifyAuthCodeUrl);
|
||||
|
||||
protected function requestAccessData(array $requestParams) : \stdClass {
|
||||
$accessData = (object) [];
|
||||
$accessData->accessToken = null;
|
||||
$accessData->refreshToken = null;
|
||||
$accessData->esiAccessTokenExpires = 0;
|
||||
|
||||
if($verifyAuthCodeUrlParts){
|
||||
$contentType = 'application/x-www-form-urlencoded';
|
||||
$requestOptions = [
|
||||
'timeout' => self::SSO_TIMEOUT,
|
||||
'method' => 'POST',
|
||||
'user_agent' => $this->getUserAgent(),
|
||||
'header' => [
|
||||
'Authorization: Basic ' . $this->getAuthorizationHeader(),
|
||||
'Content-Type: ' . $contentType,
|
||||
'Host: ' . $verifyAuthCodeUrlParts['host']
|
||||
]
|
||||
];
|
||||
$authCodeRequestData = $this->getF3()->ssoClient()->getAccessData($this->getAuthorizationData(), $requestParams);
|
||||
|
||||
// content (parameters to send with)
|
||||
$requestOptions['content'] = http_build_query($requestParams);
|
||||
if( !empty($authCodeRequestData) ){
|
||||
if( !empty($authCodeRequestData['accessToken']) ){
|
||||
// accessToken is required for endpoints that require Auth
|
||||
$accessData->accessToken = $authCodeRequestData['accessToken'];
|
||||
}
|
||||
|
||||
$apiResponse = Lib\Web::instance()->request($verifyAuthCodeUrl, $requestOptions, $additionalOptions);
|
||||
if( !empty($authCodeRequestData['expiresIn']) ){
|
||||
// expire time for accessToken
|
||||
try{
|
||||
$timezone = $this->getF3()->get('getTimeZone')();
|
||||
$accessTokenExpires = new \DateTime('now', $timezone);
|
||||
$accessTokenExpires->add(new \DateInterval('PT' . (int)$authCodeRequestData['expiresIn'] . 'S'));
|
||||
|
||||
if($apiResponse['body']){
|
||||
$authCodeRequestData = json_decode($apiResponse['body'], true);
|
||||
|
||||
if( !empty($authCodeRequestData) ){
|
||||
if( isset($authCodeRequestData['access_token']) ){
|
||||
// accessToken is required for endpoints that require Auth
|
||||
$accessData->accessToken = $authCodeRequestData['access_token'];
|
||||
}
|
||||
|
||||
if(isset($authCodeRequestData['expires_in'])){
|
||||
// expire time for accessToken
|
||||
try{
|
||||
$timezone = $this->getF3()->get('getTimeZone')();
|
||||
$accessTokenExpires = new \DateTime('now', $timezone);
|
||||
$accessTokenExpires->add(new \DateInterval('PT' . (int)$authCodeRequestData['expires_in'] . 'S'));
|
||||
|
||||
$accessData->esiAccessTokenExpires = $accessTokenExpires->format('Y-m-d H:i:s');
|
||||
}catch(\Exception $e){
|
||||
$this->getF3()->error(500, $e->getMessage(), $e->getTrace());
|
||||
}
|
||||
}
|
||||
|
||||
if(isset($authCodeRequestData['refresh_token'])){
|
||||
// this token is used to refresh/get a new access_token when expires
|
||||
$accessData->refreshToken = $authCodeRequestData['refresh_token'];
|
||||
}
|
||||
$accessData->esiAccessTokenExpires = $accessTokenExpires->format('Y-m-d H:i:s');
|
||||
}catch(\Exception $e){
|
||||
$this->getF3()->error(500, $e->getMessage(), $e->getTrace());
|
||||
}
|
||||
}else{
|
||||
self::getSSOLogger()->write(
|
||||
sprintf(
|
||||
self::ERROR_ACCESS_TOKEN,
|
||||
print_r($requestParams, true)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if( !empty($authCodeRequestData['refreshToken']) ){
|
||||
// this token is used to refresh/get a new access_token when expires
|
||||
$accessData->refreshToken = $authCodeRequestData['refreshToken'];
|
||||
}
|
||||
}else{
|
||||
self::getSSOLogger()->write(
|
||||
sprintf(self::ERROR_CCP_SSO_URL, __METHOD__)
|
||||
);
|
||||
self::getSSOLogger()->write(sprintf(self::ERROR_ACCESS_TOKEN, print_r($requestParams, true)));
|
||||
}
|
||||
|
||||
return $accessData;
|
||||
@@ -458,34 +426,17 @@ class Sso extends Api\User{
|
||||
* verify character data by "access_token"
|
||||
* -> get some basic information (like character id)
|
||||
* -> if more character information is required, use ESI "characters" endpoints request instead
|
||||
* @param $accessToken
|
||||
* @return mixed|null
|
||||
* @param string $accessToken
|
||||
* @return array
|
||||
*/
|
||||
public function verifyCharacterData($accessToken){
|
||||
$verifyUserUrl = self::getVerifyUserEndpoint();
|
||||
$verifyUrlParts = parse_url($verifyUserUrl);
|
||||
$characterData = null;
|
||||
public function verifyCharacterData(string $accessToken) : array {
|
||||
$characterData = $this->getF3()->ssoClient()->getVerifyCharacterData($accessToken);
|
||||
|
||||
if($verifyUrlParts){
|
||||
$requestOptions = [
|
||||
'timeout' => self::SSO_TIMEOUT,
|
||||
'method' => 'GET',
|
||||
'user_agent' => $this->getUserAgent(),
|
||||
'header' => [
|
||||
'Authorization: Bearer ' . $accessToken,
|
||||
'Host: ' . $verifyUrlParts['host']
|
||||
]
|
||||
];
|
||||
|
||||
$apiResponse = Lib\Web::instance()->request($verifyUserUrl, $requestOptions);
|
||||
|
||||
if($apiResponse['body']){
|
||||
$characterData = json_decode($apiResponse['body']);
|
||||
}else{
|
||||
self::getSSOLogger()->write(sprintf(self::ERROR_VERIFY_CHARACTER, __METHOD__));
|
||||
}
|
||||
if( !empty($characterData) ){
|
||||
// convert string with scopes to array
|
||||
$characterData['scopes'] = Lib\Util::convertScopesString($characterData['scopes']);
|
||||
}else{
|
||||
self::getSSOLogger()->write(sprintf(self::ERROR_CCP_SSO_URL, __METHOD__));
|
||||
self::getSSOLogger()->write(sprintf(self::ERROR_VERIFY_CHARACTER, __METHOD__));
|
||||
}
|
||||
|
||||
return $characterData;
|
||||
@@ -501,7 +452,7 @@ class Sso extends Api\User{
|
||||
$characterData = (object) [];
|
||||
|
||||
if($characterId){
|
||||
$characterDataBasic = $this->getF3()->ccpClient->getCharacterData($characterId);
|
||||
$characterDataBasic = $this->getF3()->ccpClient()->getCharacterData($characterId);
|
||||
|
||||
if( !empty($characterDataBasic) ){
|
||||
// remove some "unwanted" data -> not relevant for Pathfinder
|
||||
@@ -567,15 +518,16 @@ class Sso extends Api\User{
|
||||
}
|
||||
|
||||
/**
|
||||
* get "Authorization:" Header data
|
||||
* get data for HTTP "Authorization:" Header
|
||||
* -> This header is required for any Auth-required endpoints!
|
||||
* @return string
|
||||
* @return array
|
||||
*/
|
||||
protected function getAuthorizationHeader() : string {
|
||||
return base64_encode(
|
||||
Controller\Controller::getEnvironmentData('CCP_SSO_CLIENT_ID') . ':'
|
||||
. Controller\Controller::getEnvironmentData('CCP_SSO_SECRET_KEY')
|
||||
);
|
||||
protected function getAuthorizationData() : array {
|
||||
return [
|
||||
Controller\Controller::getEnvironmentData('CCP_SSO_CLIENT_ID'),
|
||||
Controller\Controller::getEnvironmentData('CCP_SSO_SECRET_KEY'),
|
||||
'basic'
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -596,18 +548,6 @@ class Sso extends Api\User{
|
||||
return $url;
|
||||
}
|
||||
|
||||
static function getAuthorizationEndpoint() : string {
|
||||
return self::getSsoUrlRoot() . '/oauth/authorize';
|
||||
}
|
||||
|
||||
static function getVerifyAuthorizationCodeEndpoint() : string {
|
||||
return self::getSsoUrlRoot() . '/oauth/token';
|
||||
}
|
||||
|
||||
static function getVerifyUserEndpoint() : string {
|
||||
return self::getSsoUrlRoot() . '/oauth/verify';
|
||||
}
|
||||
|
||||
/**
|
||||
* get logger for SSO logging
|
||||
* @return \Log
|
||||
|
||||
@@ -22,7 +22,7 @@ class Universe extends Controller {
|
||||
$regionsWhitelist = [
|
||||
10000002 // The Forge (13 constellations -> 93 systems)
|
||||
];
|
||||
$regionIds = $f3->ccpClient->getUniverseRegions();
|
||||
$regionIds = $f3->ccpClient()->getUniverseRegions();
|
||||
$regionIds = array_intersect ($regionsWhitelist, $regionIds);
|
||||
|
||||
$region = Model\Universe\BasicUniverseModel::getNew('RegionModel');
|
||||
@@ -43,7 +43,7 @@ class Universe extends Controller {
|
||||
$constellationsWhitelist = [
|
||||
20000014 // Mal (11 systems)
|
||||
];
|
||||
$constellationIds = $f3->ccpClient->getUniverseConstellations();
|
||||
$constellationIds = $f3->ccpClient()->getUniverseConstellations();
|
||||
$constellationIds = array_intersect ($constellationsWhitelist, $constellationIds);
|
||||
$constellation = Model\Universe\BasicUniverseModel::getNew('ConstellationModel');
|
||||
foreach($constellationIds as $constellationId){
|
||||
@@ -92,7 +92,7 @@ class Universe extends Controller {
|
||||
*/
|
||||
protected function setupCategories(array $categoriesWhitelist = []){
|
||||
$return = [];
|
||||
$categoryIds = $this->getF3()->ccpClient->getUniverseCategories();
|
||||
$categoryIds = $this->getF3()->ccpClient()->getUniverseCategories();
|
||||
$categoryIds = array_intersect ($categoriesWhitelist, $categoryIds);
|
||||
foreach($categoryIds as $categoryId){
|
||||
$return[$categoryId] = $this->setupCategory($categoryId);
|
||||
@@ -112,7 +112,7 @@ class Universe extends Controller {
|
||||
*/
|
||||
protected function setupGroups(array $groupsWhitelist = []){
|
||||
$return = [];
|
||||
$groupIds = $this->getF3()->ccpClient->getUniverseGroups();
|
||||
$groupIds = $this->getF3()->ccpClient()->getUniverseGroups();
|
||||
$groupIds = array_intersect ($groupsWhitelist, $groupIds);
|
||||
/**
|
||||
* @var $group Model\Universe\GroupModel
|
||||
@@ -288,13 +288,13 @@ class Universe extends Controller {
|
||||
$f3 = \Base::instance();
|
||||
$universeNameData = [];
|
||||
if( !empty($categories) && !empty($search)){
|
||||
$universeIds = $f3->ccpClient->search($categories, $search, $strict);
|
||||
$universeIds = $f3->ccpClient()->search($categories, $search, $strict);
|
||||
if(isset($universeIds['error'])){
|
||||
// ESI error
|
||||
$universeNameData = $universeIds;
|
||||
}elseif( !empty($universeIds) ){
|
||||
$universeIds = Util::arrayFlattenByValue($universeIds);
|
||||
$universeNameData = $f3->ccpClient->getUniverseNamesData($universeIds);
|
||||
$universeNameData = $f3->ccpClient()->getUniverseNamesData($universeIds);
|
||||
}
|
||||
}
|
||||
return $universeNameData;
|
||||
|
||||
@@ -541,34 +541,73 @@ class Controller {
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function getEveServerStatus(\Base $f3){
|
||||
$cacheKey = 'eve_server_status';
|
||||
if( !$f3->exists($cacheKey, $return) ){
|
||||
$return = (object) [];
|
||||
$return->error = [];
|
||||
$return->status = [
|
||||
'serverName' => strtoupper( self::getEnvironmentData('CCP_ESI_DATASOURCE') ),
|
||||
'serviceStatus' => 'offline'
|
||||
$esiStatusVersion = 'latest';
|
||||
|
||||
$return = (object) [];
|
||||
$return->error = [];
|
||||
|
||||
if($client = $f3->ccpClient()){
|
||||
$return->server = [
|
||||
'name' => strtoupper( self::getEnvironmentData('CCP_ESI_DATASOURCE') ),
|
||||
'status' => 'offline',
|
||||
'statusColor' => 'red',
|
||||
];
|
||||
$return->api = [
|
||||
'name' => 'ESI API',
|
||||
'status' => 'offline',
|
||||
'statusColor' => 'red',
|
||||
'url' => $client->getUrl(),
|
||||
'timeout' => $client->getTimeout(),
|
||||
'connectTimeout' => $client->getConnectTimeout(),
|
||||
'readTimeout' => $client->getReadTimeout(),
|
||||
'proxy' => ($proxy = $client->getProxy()) ? : 'false',
|
||||
'verify' => $client->getVerify(),
|
||||
'debug' => $client->getDebugRequests(),
|
||||
'dataSource' => $client->getDataSource(),
|
||||
'statusVersion' => $esiStatusVersion,
|
||||
'routes' => []
|
||||
];
|
||||
|
||||
$response = $f3->ccpClient->getServerStatus();
|
||||
|
||||
if( !empty($response) ){
|
||||
$serverStatus = $client->getServerStatus();
|
||||
if( !isset($serverStatus['error']) ){
|
||||
$statusData = $serverStatus['status'];
|
||||
// calculate time diff since last server restart
|
||||
$timezone = $f3->get('getTimeZone')();
|
||||
$dateNow = new \DateTime('now', $timezone);
|
||||
$dateServerStart = new \DateTime($response['startTime']);
|
||||
$dateServerStart = new \DateTime($statusData['startTime']);
|
||||
$interval = $dateNow->diff($dateServerStart);
|
||||
$startTimestampFormat = $interval->format('%hh %im');
|
||||
if($interval->days > 0){
|
||||
$startTimestampFormat = $interval->days . 'd ' . $startTimestampFormat;
|
||||
}
|
||||
|
||||
$response['serverName'] = strtoupper( self::getEnvironmentData('CCP_ESI_DATASOURCE') );
|
||||
$response['serviceStatus'] = 'online';
|
||||
$response['startTime'] = $startTimestampFormat;
|
||||
$return->status = $response;
|
||||
$statusData['name'] = $return->server['name'];
|
||||
$statusData['status'] = 'online';
|
||||
$statusData['statusColor'] = 'green';
|
||||
$statusData['startTime'] = $startTimestampFormat;
|
||||
$return->server = $statusData;
|
||||
}
|
||||
|
||||
$f3->set($cacheKey, $return, 60);
|
||||
$apiStatus = $client->getStatusForRoutes('latest');
|
||||
if( !isset($apiStatus['error']) ){
|
||||
// find top status
|
||||
$status = 'OK';
|
||||
$color = 'green';
|
||||
foreach($apiStatus['status'] as $statusData){
|
||||
if('red' == $statusData['status']){
|
||||
$status = 'unstable';
|
||||
$color = $statusData['status'];
|
||||
break;
|
||||
}
|
||||
if('yellow' == $statusData['status']){
|
||||
$status = 'degraded';
|
||||
$color = $statusData['status'];
|
||||
}
|
||||
}
|
||||
|
||||
$return->api['status'] = $status;
|
||||
$return->api['statusColor'] = $color;
|
||||
$return->api['routes'] = $apiStatus['status'];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -795,9 +834,8 @@ class Controller {
|
||||
* @param string $authType
|
||||
* @return array
|
||||
*/
|
||||
static function getScopesByAuthType($authType = ''){
|
||||
static function getScopesByAuthType(string $authType = '') : array {
|
||||
$scopes = array_filter((array)self::getEnvironmentData('CCP_ESI_SCOPES'));
|
||||
|
||||
switch($authType){
|
||||
case 'admin':
|
||||
$scopesAdmin = array_filter((array)self::getEnvironmentData('CCP_ESI_SCOPES_ADMIN'));
|
||||
|
||||
@@ -167,9 +167,6 @@ class Setup extends Controller {
|
||||
// js view (file)
|
||||
$f3->set('tplJsView', 'setup');
|
||||
|
||||
// set render functions (called within template)
|
||||
$f3->set('cacheType', $this->getCacheType($f3));
|
||||
|
||||
// simple counter (called within template)
|
||||
$counter = [];
|
||||
$f3->set('tplCounter', function(string $action = 'increment', string $type = 'default', $val = 0) use (&$counter){
|
||||
@@ -187,19 +184,6 @@ class Setup extends Controller {
|
||||
echo \Template::instance()->render( Config::getPathfinderData('view.index') );
|
||||
}
|
||||
|
||||
/**
|
||||
* get Cache backend type for F3
|
||||
* @param \Base $f3
|
||||
* @return string
|
||||
*/
|
||||
protected function getCacheType(\Base &$f3) : string {
|
||||
$cacheType = $f3->get('CACHE');
|
||||
if(strpos($cacheType, 'redis') !== false){
|
||||
$cacheType = 'redis';
|
||||
}
|
||||
return $cacheType;
|
||||
}
|
||||
|
||||
/**
|
||||
* main setup route handler
|
||||
* works as dispatcher for setup functions
|
||||
@@ -229,47 +213,63 @@ class Setup extends Controller {
|
||||
case 'exportTable':
|
||||
$this->exportTable($params['model']);
|
||||
break;
|
||||
case 'clearCache':
|
||||
$this->clearCache($f3);
|
||||
case 'clearFiles':
|
||||
$this->clearFiles((string)$params['path']);
|
||||
break;
|
||||
case 'flushRedisDb':
|
||||
$this->flushRedisDb((string)$params['host'], (int)$params['port'], (int)$params['db']);
|
||||
break;
|
||||
case 'invalidateCookies':
|
||||
$this->invalidateCookies($f3);
|
||||
break;
|
||||
}
|
||||
|
||||
// set template data ----------------------------------------------------------------
|
||||
// set environment information
|
||||
$f3->set('environmentInformation', $this->getEnvironmentInformation($f3));
|
||||
// ============================================================================================================
|
||||
// Template data
|
||||
// ============================================================================================================
|
||||
|
||||
// set server information
|
||||
// Server -----------------------------------------------------------------------------------------------------
|
||||
// Server information
|
||||
$f3->set('serverInformation', $this->getServerInformation($f3));
|
||||
|
||||
// set requirement check information
|
||||
$f3->set('checkRequirements', $this->checkRequirements($f3));
|
||||
// Pathfinder directory config
|
||||
$f3->set('directoryConfig', $this->getDirectoryConfig($f3));
|
||||
|
||||
// set php config check information
|
||||
$f3->set('checkPHPConfig', $this->checkPHPConfig($f3));
|
||||
|
||||
// set system config check information
|
||||
// Server environment variables
|
||||
$f3->set('checkSystemConfig', $this->checkSystemConfig($f3));
|
||||
|
||||
// set map default config
|
||||
// Environment ------------------------------------------------------------------------------------------------
|
||||
// Server requirement
|
||||
$f3->set('checkRequirements', $this->checkRequirements($f3));
|
||||
|
||||
// PHP config
|
||||
$f3->set('checkPHPConfig', $this->checkPHPConfig($f3));
|
||||
|
||||
// Settings ---------------------------------------------------------------------------------------------------
|
||||
// Pathfinder environment config
|
||||
$f3->set('environmentInformation', $this->getEnvironmentInformation($f3));
|
||||
|
||||
// Pathfinder map default config
|
||||
$f3->set('mapsDefaultConfig', $this->getMapsDefaultConfig($f3));
|
||||
|
||||
// set database connection information
|
||||
// Database ---------------------------------------------------------------------------------------------------
|
||||
// Database config
|
||||
$f3->set('checkDatabase', $this->checkDatabase($f3, $fixColumns));
|
||||
|
||||
// set socket information
|
||||
// Redis ------------------------------------------------------------------------------------------------------
|
||||
// Redis information
|
||||
$f3->set('checkRedisInformation', $this->checkRedisInformation($f3));
|
||||
|
||||
// Socket -----------------------------------------------------------------------------------------------------
|
||||
// WebSocket information
|
||||
$f3->set('socketInformation', $this->getSocketInformation());
|
||||
|
||||
// set index information
|
||||
// Administration ---------------------------------------------------------------------------------------------
|
||||
// Index information
|
||||
$f3->set('indexInformation', $this->getIndexData($f3));
|
||||
|
||||
// set cache size
|
||||
$f3->set('cacheSize', $this->getCacheData($f3));
|
||||
|
||||
// set Redis config check information
|
||||
$f3->set('checkRedisConfig', $this->checkRedisConfig($f3));
|
||||
// Filesystem (cache) size
|
||||
$f3->set('checkDirSize', $this->checkDirSize($f3));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -290,6 +290,9 @@ class Setup extends Controller {
|
||||
'database' => [
|
||||
'icon' => 'fa-database'
|
||||
],
|
||||
'cache' => [
|
||||
'icon' => 'fa-hdd'
|
||||
],
|
||||
'socket' => [
|
||||
'icon' => 'fa-exchange-alt'
|
||||
],
|
||||
@@ -306,7 +309,7 @@ class Setup extends Controller {
|
||||
* @param \Base $f3
|
||||
* @return array
|
||||
*/
|
||||
protected function getEnvironmentInformation(\Base $f3){
|
||||
protected function getEnvironmentInformation(\Base $f3) : array {
|
||||
$environmentData = [];
|
||||
// exclude some sensitive data (e.g. database, passwords)
|
||||
$excludeVars = [
|
||||
@@ -346,7 +349,7 @@ class Setup extends Controller {
|
||||
* @param \Base $f3
|
||||
* @return array
|
||||
*/
|
||||
protected function getServerInformation(\Base $f3){
|
||||
protected function getServerInformation(\Base $f3) : array {
|
||||
$serverInfo = [
|
||||
'time' => [
|
||||
'label' => 'Time',
|
||||
@@ -389,15 +392,90 @@ class Setup extends Controller {
|
||||
return $serverInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* get information for used directories
|
||||
* @param \Base $f3
|
||||
* @return array
|
||||
*/
|
||||
protected function getDirectoryConfig(\Base $f3) : array {
|
||||
$directoryData = [
|
||||
'TEMP' => [
|
||||
'label' => 'TEMP',
|
||||
'value' => $f3->get('TEMP'),
|
||||
'check' => true,
|
||||
'tooltip' => 'Temporary folder for pre compiled templates.',
|
||||
'chmod' => Util::filesystemInfo($f3->get('TEMP'))['chmod']
|
||||
],
|
||||
'CACHE' => [
|
||||
'label' => 'CACHE',
|
||||
'value' => $f3->get('CACHE'),
|
||||
'check' => true,
|
||||
'tooltip' => 'Cache backend. Support for Redis, Memcache, APC, WinCache, XCache and a filesystem-based (default) cache.',
|
||||
'chmod' => ((Config::parseDSN($f3->get('CACHE'), $confCache)) && $confCache['type'] == 'folder') ?
|
||||
Util::filesystemInfo((string)$confCache['folder'])['chmod'] : ''
|
||||
],
|
||||
'API_CACHE' => [
|
||||
'label' => 'API_CACHE',
|
||||
'value' => $f3->get('API_CACHE'),
|
||||
'check' => true,
|
||||
'tooltip' => 'Cache backend for API related cache data. Support for Redis and a filesystem-based (default) cache.',
|
||||
'chmod' => ((Config::parseDSN($f3->get('API_CACHE'), $confCacheApi)) && $confCacheApi['type'] == 'folder') ?
|
||||
Util::filesystemInfo((string)$confCacheApi['folder'])['chmod'] : ''
|
||||
],
|
||||
'LOGS' => [
|
||||
'label' => 'LOGS',
|
||||
'value' => $f3->get('LOGS'),
|
||||
'check' => true,
|
||||
'tooltip' => 'Folder for pathfinder logs (e.g. cronjob-, error-logs, ...).',
|
||||
'chmod' => Util::filesystemInfo($f3->get('LOGS'))['chmod']
|
||||
],
|
||||
'UI' => [
|
||||
'label' => 'UI',
|
||||
'value' => $f3->get('UI'),
|
||||
'check' => true,
|
||||
'tooltip' => 'Folder for public accessible resources (templates, js, css, images,..).',
|
||||
'chmod' => Util::filesystemInfo($f3->get('UI'))['chmod']
|
||||
],
|
||||
'AUTOLOAD' => [
|
||||
'label' => 'AUTOLOAD',
|
||||
'value' => $f3->get('AUTOLOAD'),
|
||||
'check' => true,
|
||||
'tooltip' => 'Autoload folder for PHP files.',
|
||||
'chmod' => Util::filesystemInfo($f3->get('AUTOLOAD'))['chmod']
|
||||
],
|
||||
'FAVICON' => [
|
||||
'label' => 'FAVICON',
|
||||
'value' => $f3->get('FAVICON'),
|
||||
'check' => true,
|
||||
'tooltip' => 'Folder for Favicons.',
|
||||
'chmod' => Util::filesystemInfo($f3->get('FAVICON'))['chmod']
|
||||
],
|
||||
'HISTORY' => [
|
||||
'label' => 'HISTORY [optional]',
|
||||
'value' => Config::getPathfinderData('history.log'),
|
||||
'check' => true,
|
||||
'tooltip' => 'Folder for log history files. (e.g. change logs for maps).',
|
||||
'chmod' => Util::filesystemInfo(Config::getPathfinderData('history.log'))['chmod']
|
||||
],
|
||||
'CONFIG' => [
|
||||
'label' => 'CONFIG PATH [optional]',
|
||||
'value' => implode(' ', (array)$f3->get('CONF')),
|
||||
'check' => true,
|
||||
'tooltip' => 'Folder for custom *.ini files. (e.g. when overwriting of default values in app/*.ini)'
|
||||
]
|
||||
];
|
||||
|
||||
return $directoryData;
|
||||
}
|
||||
|
||||
/**
|
||||
* check all required backend requirements
|
||||
* (Fat Free Framework)
|
||||
* @param \Base $f3
|
||||
* @return array
|
||||
*/
|
||||
protected function checkRequirements(\Base $f3){
|
||||
protected function checkRequirements(\Base $f3) : array {
|
||||
|
||||
// server type ------------------------------------------------------------------
|
||||
$serverData = self::getServerData(0);
|
||||
|
||||
$checkRequirements = [
|
||||
@@ -516,7 +594,7 @@ class Setup extends Controller {
|
||||
$modNotFoundMsg = 'Module status can not be identified. '
|
||||
. 'This can happen if PHP runs as \'FastCGI\'. Please check manual! ';
|
||||
|
||||
// mod_rewrite check ------------------------------------------------------------
|
||||
// mod_rewrite check --------------------------------------------------------------------------------------
|
||||
$modRewriteCheck = false;
|
||||
$modRewriteVersion = 'disabled';
|
||||
$modRewriteTooltip = false;
|
||||
@@ -539,7 +617,7 @@ class Setup extends Controller {
|
||||
'tooltip' => $modRewriteTooltip
|
||||
];
|
||||
|
||||
// mod_headers check ------------------------------------------------------------
|
||||
// mod_headers check --------------------------------------------------------------------------------------
|
||||
$modHeadersCheck = false;
|
||||
$modHeadersVersion = 'disabled';
|
||||
$modHeadersTooltip = false;
|
||||
@@ -572,6 +650,11 @@ class Setup extends Controller {
|
||||
* @return array
|
||||
*/
|
||||
protected function checkPHPConfig(\Base $f3): array {
|
||||
$memoryLimit = (int)ini_get('memory_limit');
|
||||
$maxInputVars = (int)ini_get('max_input_vars');
|
||||
$maxExecutionTime = (int)ini_get('max_execution_time'); // 0 == infinite
|
||||
$htmlErrors = (int)ini_get('html_errors');
|
||||
|
||||
$phpConfig = [
|
||||
'exec' => [
|
||||
'label' => 'exec()',
|
||||
@@ -583,29 +666,29 @@ class Setup extends Controller {
|
||||
'memoryLimit' => [
|
||||
'label' => 'memory_limit',
|
||||
'required' => $f3->get('REQUIREMENTS.PHP.MEMORY_LIMIT'),
|
||||
'version' => ini_get('memory_limit'),
|
||||
'check' => ini_get('memory_limit') >= $f3->get('REQUIREMENTS.PHP.MEMORY_LIMIT'),
|
||||
'version' => $memoryLimit,
|
||||
'check' => $memoryLimit >= $f3->get('REQUIREMENTS.PHP.MEMORY_LIMIT'),
|
||||
'tooltip' => 'PHP default = 64MB.'
|
||||
],
|
||||
'maxInputVars' => [
|
||||
'label' => 'max_input_vars',
|
||||
'required' => $f3->get('REQUIREMENTS.PHP.MAX_INPUT_VARS'),
|
||||
'version' => ini_get('max_input_vars'),
|
||||
'check' => ini_get('max_input_vars') >= $f3->get('REQUIREMENTS.PHP.MAX_INPUT_VARS'),
|
||||
'version' => $maxInputVars,
|
||||
'check' => $maxInputVars >= $f3->get('REQUIREMENTS.PHP.MAX_INPUT_VARS'),
|
||||
'tooltip' => 'PHP default = 1000. Increase it in order to import larger maps.'
|
||||
],
|
||||
'maxExecutionTime' => [
|
||||
'label' => 'max_execution_time',
|
||||
'required' => $f3->get('REQUIREMENTS.PHP.MAX_EXECUTION_TIME'),
|
||||
'version' => ini_get('max_execution_time'),
|
||||
'check' => ini_get('max_execution_time') >= $f3->get('REQUIREMENTS.PHP.MAX_EXECUTION_TIME'),
|
||||
'version' => $maxExecutionTime,
|
||||
'check' => !$maxExecutionTime || $maxExecutionTime >= $f3->get('REQUIREMENTS.PHP.MAX_EXECUTION_TIME'),
|
||||
'tooltip' => 'PHP default = 30. Max execution time for PHP scripts.'
|
||||
],
|
||||
'htmlErrors' => [
|
||||
'label' => 'html_errors',
|
||||
'required' => $f3->get('REQUIREMENTS.PHP.HTML_ERRORS'),
|
||||
'version' => (int)ini_get('html_errors'),
|
||||
'check' => (bool)ini_get('html_errors') == (bool)$f3->get('REQUIREMENTS.PHP.HTML_ERRORS'),
|
||||
'version' => $htmlErrors,
|
||||
'check' => (bool)$htmlErrors == (bool)$f3->get('REQUIREMENTS.PHP.HTML_ERRORS'),
|
||||
'tooltip' => 'Formatted HTML StackTrace on error.'
|
||||
],
|
||||
[
|
||||
@@ -640,70 +723,209 @@ class Setup extends Controller {
|
||||
* @param \Base $f3
|
||||
* @return array
|
||||
*/
|
||||
protected function checkRedisConfig(\Base $f3): array {
|
||||
protected function checkRedisInformation(\Base $f3): array {
|
||||
$redisConfig = [];
|
||||
if($this->getCacheType($f3) === 'redis'){
|
||||
// we need to access the "protected" member $ref from F3´s Cache class
|
||||
// to get access to the underlying Redis() class
|
||||
$ref = new \ReflectionObject($cache = \Cache::instance());
|
||||
$prop = $ref->getProperty('ref');
|
||||
$prop->setAccessible(true);
|
||||
|
||||
if(
|
||||
extension_loaded('redis') &&
|
||||
class_exists('\Redis')
|
||||
){
|
||||
// collection of DSN specific $conf array (host, port, db,..)
|
||||
$dsnData = [];
|
||||
|
||||
/**
|
||||
* @var $redis \Redis
|
||||
* get client information for a Redis client
|
||||
* @param \Redis $client
|
||||
* @param array $conf
|
||||
* @return array
|
||||
*/
|
||||
$redis = $prop->getValue($cache);
|
||||
$getClientInfo = function(\Redis $client, array $conf) : array {
|
||||
$redisInfo = [
|
||||
'dsn' => [
|
||||
'label' => 'DNS',
|
||||
'value' => $conf['host'] . ':' . $conf['port']
|
||||
],
|
||||
'connected' => [
|
||||
'label' => 'status',
|
||||
'value' => $client->isConnected()
|
||||
]
|
||||
];
|
||||
|
||||
$redisServerInfo = (array)$redis->info('SERVER');
|
||||
$redisMemoryInfo = (array)$redis->info('MEMORY');
|
||||
$redisStatsInfo = (array)$redis->info('STATS');
|
||||
return $redisInfo;
|
||||
};
|
||||
|
||||
$redisConfig = [
|
||||
'redisVersion' => [
|
||||
'label' => 'redis_version',
|
||||
'required' => number_format((float)$f3->get('REQUIREMENTS.REDIS.VERSION'), 1, '.', ''),
|
||||
'version' => $redisServerInfo['redis_version'],
|
||||
'check' => version_compare( $redisServerInfo['redis_version'], $f3->get('REQUIREMENTS.REDIS.VERSION'), '>='),
|
||||
'tooltip' => 'Redis server version'
|
||||
],
|
||||
'maxMemory' => [
|
||||
'label' => 'maxmemory',
|
||||
'required' => $this->convertBytes($f3->get('REQUIREMENTS.REDIS.MAX_MEMORY')),
|
||||
'version' => $this->convertBytes($redisMemoryInfo['maxmemory']),
|
||||
'check' => $redisMemoryInfo['maxmemory'] >= $f3->get('REQUIREMENTS.REDIS.MAX_MEMORY'),
|
||||
'tooltip' => 'Max memory limit for Redis'
|
||||
],
|
||||
'usedMemory' => [
|
||||
'label' => 'used_memory',
|
||||
'version' => $this->convertBytes($redisMemoryInfo['used_memory']),
|
||||
'check' => $redisMemoryInfo['used_memory'] < $redisMemoryInfo['maxmemory'],
|
||||
'tooltip' => 'Current memory used by Redis'
|
||||
],
|
||||
'usedMemoryPeak' => [
|
||||
'label' => 'used_memory_peak',
|
||||
'version' => $this->convertBytes($redisMemoryInfo['used_memory_peak']),
|
||||
'check' => $redisMemoryInfo['used_memory_peak'] <= $redisMemoryInfo['maxmemory'],
|
||||
'tooltip' => 'Peak memory used by Redis'
|
||||
],
|
||||
'maxmemoryPolicy' => [
|
||||
'label' => 'maxmemory_policy',
|
||||
'required' => $f3->get('REQUIREMENTS.REDIS.MAXMEMORY_POLICY'),
|
||||
'version' => $redisMemoryInfo['maxmemory_policy'],
|
||||
'check' => $redisMemoryInfo['maxmemory_policy'] == $f3->get('REQUIREMENTS.REDIS.MAXMEMORY_POLICY'),
|
||||
'tooltip' => 'How Redis behaves if \'maxmemory\' limit reached'
|
||||
],
|
||||
'evictedKeys' => [
|
||||
'label' => 'evicted_keys',
|
||||
'version' => $redisStatsInfo['evicted_keys'],
|
||||
'check' => !(bool)$redisStatsInfo['evicted_keys'],
|
||||
'tooltip' => 'Number of evicted keys due to maxmemory limit'
|
||||
],
|
||||
'dbSize' . $redis->getDbNum() => [
|
||||
'label' => 'Size DB (' . $redis->getDbNum() . ')',
|
||||
'version' => $redis->dbSize(),
|
||||
'check' => $redis->dbSize() > 0,
|
||||
'tooltip' => 'Keys found in DB (' . $redis->getDbNum() . ') [Cache DB]'
|
||||
]
|
||||
/**
|
||||
* get status information for a Redis client
|
||||
* @param \Redis $client
|
||||
* @return array
|
||||
*/
|
||||
$getClientStats = function(\Redis $client) use ($f3) : array {
|
||||
$redisStats = [];
|
||||
|
||||
if($client->isConnected()){
|
||||
$redisServerInfo = (array)$client->info('SERVER');
|
||||
$redisMemoryInfo = (array)$client->info('MEMORY');
|
||||
$redisStatsInfo = (array)$client->info('STATS');
|
||||
|
||||
$redisStats = [
|
||||
'redisVersion' => [
|
||||
'label' => 'redis_version',
|
||||
'required' => number_format((float)$f3->get('REQUIREMENTS.REDIS.VERSION'), 1, '.', ''),
|
||||
'version' => $redisServerInfo['redis_version'],
|
||||
'check' => version_compare( $redisServerInfo['redis_version'], $f3->get('REQUIREMENTS.REDIS.VERSION'), '>='),
|
||||
'tooltip' => 'Redis server version'
|
||||
],
|
||||
'maxMemory' => [
|
||||
'label' => 'maxmemory',
|
||||
'required' => $this->convertBytes($f3->get('REQUIREMENTS.REDIS.MAX_MEMORY')),
|
||||
'version' => $this->convertBytes($redisMemoryInfo['maxmemory']),
|
||||
'check' => $redisMemoryInfo['maxmemory'] >= $f3->get('REQUIREMENTS.REDIS.MAX_MEMORY'),
|
||||
'tooltip' => 'Max memory limit for Redis'
|
||||
],
|
||||
'usedMemory' => [
|
||||
'label' => 'used_memory',
|
||||
'version' => $this->convertBytes($redisMemoryInfo['used_memory']),
|
||||
'check' => $redisMemoryInfo['used_memory'] < $redisMemoryInfo['maxmemory'],
|
||||
'tooltip' => 'Current memory used by Redis'
|
||||
],
|
||||
'usedMemoryPeak' => [
|
||||
'label' => 'used_memory_peak',
|
||||
'version' => $this->convertBytes($redisMemoryInfo['used_memory_peak']),
|
||||
'check' => $redisMemoryInfo['used_memory_peak'] <= $redisMemoryInfo['maxmemory'],
|
||||
'tooltip' => 'Peak memory used by Redis'
|
||||
],
|
||||
'maxmemoryPolicy' => [
|
||||
'label' => 'maxmemory_policy',
|
||||
'required' => $f3->get('REQUIREMENTS.REDIS.MAXMEMORY_POLICY'),
|
||||
'version' => $redisMemoryInfo['maxmemory_policy'],
|
||||
'check' => $redisMemoryInfo['maxmemory_policy'] == $f3->get('REQUIREMENTS.REDIS.MAXMEMORY_POLICY'),
|
||||
'tooltip' => 'How Redis behaves if \'maxmemory\' limit reached'
|
||||
],
|
||||
'evictedKeys' => [
|
||||
'label' => 'evicted_keys',
|
||||
'version' => $redisStatsInfo['evicted_keys'],
|
||||
'check' => !(bool)$redisStatsInfo['evicted_keys'],
|
||||
'tooltip' => 'Number of evicted keys due to maxmemory limit'
|
||||
],
|
||||
[
|
||||
'label' => 'Databases'
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
return $redisStats;
|
||||
};
|
||||
|
||||
/**
|
||||
* get database status for current selected db
|
||||
* @param \Redis $client
|
||||
* @param string $tag
|
||||
* @return array
|
||||
*/
|
||||
$getDatabaseStatus = function(\Redis $client, string $tag) : array {
|
||||
$redisDatabases = [];
|
||||
if($client->isConnected()){
|
||||
$dbNum = $client->getDbNum();
|
||||
$dbSize = $client->dbSize();
|
||||
$redisDatabases = [
|
||||
'db_' . $dbNum => [
|
||||
'label' => '<i class="fas fa-fw fa-database"></i> db(' . $dbNum . ') : ' . $tag,
|
||||
'version' => $dbSize . ' keys',
|
||||
'check' => $dbSize > 0,
|
||||
'tooltip' => 'Keys in db(' . $dbNum . ')',
|
||||
'task' => [
|
||||
[
|
||||
'action' => http_build_query([
|
||||
'action' => 'flushRedisDb',
|
||||
'host' => $client->getHost(),
|
||||
'port' => $client->getPort(),
|
||||
'db' => $dbNum
|
||||
]) . '#pf-setup-cache',
|
||||
'label' => 'Flush',
|
||||
'icon' => 'fa-trash',
|
||||
'btn' => 'btn-danger' . (($dbSize > 0) ? '' : ' disabled')
|
||||
]
|
||||
]
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
return $redisDatabases;
|
||||
};
|
||||
|
||||
/**
|
||||
* build (modify) $redisConfig with DNS $conf data
|
||||
* @param array $conf
|
||||
*/
|
||||
$buildRedisConfig = function(array $conf) use (&$redisConfig, $getClientInfo, $getClientStats, $getDatabaseStatus){
|
||||
if($conf['type'] == 'redis'){
|
||||
// is Redis -> group all DNS by host:port
|
||||
$client = new \Redis();
|
||||
|
||||
try{
|
||||
$client->connect($conf['host'], $conf['port'], 0.3);
|
||||
if(isset($conf['db'])) {
|
||||
$client->select($conf['db']);
|
||||
}
|
||||
|
||||
$conf['db'] = $client->getDbNum();
|
||||
}catch(\RedisException $e){
|
||||
// connection failed
|
||||
}
|
||||
|
||||
if(!array_key_exists($uid = $conf['host'] . ':' . $conf['port'], $redisConfig)){
|
||||
$redisConfig[$uid] = $getClientInfo($client, $conf);
|
||||
$redisConfig[$uid]['status'] = $getClientStats($client) + $getDatabaseStatus($client, $conf['tag']);
|
||||
}elseif(!array_key_exists($uidDb = 'db_' . $conf['db'], $redisConfig[$uid]['status'])){
|
||||
$redisConfig[$uid]['status'] += $getDatabaseStatus($client, $conf['tag']);
|
||||
}else{
|
||||
$redisConfig[$uid]['status'][$uidDb]['label'] .= '; ' . $conf['tag'];
|
||||
}
|
||||
|
||||
$client->close();
|
||||
}
|
||||
};
|
||||
|
||||
// potential Redis caches ---------------------------------------------------------------------------------
|
||||
$redisCaches = [
|
||||
'CACHE' => $f3->get('CACHE'),
|
||||
'API_CACHE' => $f3->get('API_CACHE')
|
||||
];
|
||||
|
||||
foreach($redisCaches as $tag => $dsn){
|
||||
if(Config::parseDSN($dsn, $conf)){
|
||||
$conf['tag'] = $tag;
|
||||
$dsnData[] = $conf;
|
||||
}
|
||||
}
|
||||
|
||||
// if Session handler is also Redis -> add this as well ---------------------------------------------------
|
||||
// -> the DSN format is not the same, convert URL format into DSN
|
||||
if(
|
||||
strtolower(session_module_name()) == 'redis' &&
|
||||
($parts = parse_url(strtolower(session_save_path())))
|
||||
){
|
||||
// parse URL parameters
|
||||
parse_str((string)$parts['query'], $params);
|
||||
|
||||
$conf = [
|
||||
'type' => 'redis',
|
||||
'host' => $parts['host'],
|
||||
'port' => $parts['port'],
|
||||
'db' => !empty($params['database']) ? (int)$params['database'] : 0,
|
||||
'tag' => 'SESSION'
|
||||
];
|
||||
$dsnData[] = $conf;
|
||||
}
|
||||
|
||||
// sort all $dsnData by 'db' number -----------------------------------------------------------------------
|
||||
usort($dsnData, function($a, $b){
|
||||
return $a['db'] <=> $b['db'];
|
||||
});
|
||||
|
||||
foreach($dsnData as $conf){
|
||||
$buildRedisConfig($conf);
|
||||
}
|
||||
}
|
||||
|
||||
return $redisConfig;
|
||||
@@ -989,14 +1211,14 @@ class Setup extends Controller {
|
||||
$changedIndex = false;
|
||||
$addConstraints = [];
|
||||
|
||||
// set (new) column information -------------------------------------------------------
|
||||
// set (new) column information -----------------------------------------------------------
|
||||
$requiredTables[$requiredTableName]['fieldConf'][$columnName]['exists'] = true;
|
||||
$requiredTables[$requiredTableName]['fieldConf'][$columnName]['currentType'] = $currentColType;
|
||||
$requiredTables[$requiredTableName]['fieldConf'][$columnName]['currentNullable'] = $hasNullable;
|
||||
$requiredTables[$requiredTableName]['fieldConf'][$columnName]['currentIndex'] = $hasIndex;
|
||||
$requiredTables[$requiredTableName]['fieldConf'][$columnName]['currentUnique'] = $hasUnique;
|
||||
|
||||
// check constraint -------------------------------------------------------------------
|
||||
// check constraint -----------------------------------------------------------------------
|
||||
if(isset($fieldConf['constraint'])){
|
||||
// add or update constraints
|
||||
foreach((array)$fieldConf['constraint'] as $constraintData){
|
||||
@@ -1022,7 +1244,7 @@ class Setup extends Controller {
|
||||
}
|
||||
}
|
||||
|
||||
// check type changed -----------------------------------------------------------------
|
||||
// check type changed ---------------------------------------------------------------------
|
||||
if(
|
||||
$fieldConf['type'] !== 'JSON' &&
|
||||
!$schema->isCompatible($fieldConf['type'], $currentColType)
|
||||
@@ -1033,14 +1255,14 @@ class Setup extends Controller {
|
||||
$tableStatusCheckCount++;
|
||||
}
|
||||
|
||||
// check if column nullable changed ---------------------------------------------------
|
||||
// check if column nullable changed -------------------------------------------------------
|
||||
if( $currentNullable != $fieldConf['nullable']){
|
||||
$changedNullable = true;
|
||||
$columnStatusCheck = false;
|
||||
$tableStatusCheckCount++;
|
||||
}
|
||||
|
||||
// check if column index changed ------------------------------------------------------
|
||||
// check if column index changed ----------------------------------------------------------
|
||||
$indexUpdate = false;
|
||||
$indexKey = (bool)$hasIndex;
|
||||
$indexUnique = (bool)$hasUnique;
|
||||
@@ -1054,7 +1276,7 @@ class Setup extends Controller {
|
||||
$indexKey = (bool)$fieldConf['index'];
|
||||
}
|
||||
|
||||
// check if column unique changed -----------------------------------------------------
|
||||
// check if column unique changed ---------------------------------------------------------
|
||||
if($currentColIndexData['unique'] != $fieldConf['unique']){
|
||||
$changedUnique = true;
|
||||
$columnStatusCheck = false;
|
||||
@@ -1064,7 +1286,7 @@ class Setup extends Controller {
|
||||
$indexUnique = (bool)$fieldConf['unique'];
|
||||
}
|
||||
|
||||
// build table with changed columns ---------------------------------------------------
|
||||
// build table with changed columns -------------------------------------------------------
|
||||
if(!$columnStatusCheck || !$foreignKeyStatusCheck){
|
||||
|
||||
if(!$columnStatusCheck ){
|
||||
@@ -1106,7 +1328,7 @@ class Setup extends Controller {
|
||||
}
|
||||
}
|
||||
|
||||
// set (new) column information -------------------------------------------------------
|
||||
// set (new) column information -----------------------------------------------------------
|
||||
$requiredTables[$requiredTableName]['fieldConf'][$columnName]['changedType'] = $changedType;
|
||||
$requiredTables[$requiredTableName]['fieldConf'][$columnName]['changedNullable'] = $changedNullable;
|
||||
$requiredTables[$requiredTableName]['fieldConf'][$columnName]['changedUnique'] = $changedUnique;
|
||||
@@ -1360,7 +1582,7 @@ class Setup extends Controller {
|
||||
[
|
||||
'action' => 'clearIndex',
|
||||
'label' => 'Clear',
|
||||
'icon' => 'fa-times',
|
||||
'icon' => 'fa-trash',
|
||||
'btn' => 'btn-danger'
|
||||
],[
|
||||
'action' => 'buildIndex',
|
||||
@@ -1473,39 +1695,96 @@ class Setup extends Controller {
|
||||
}
|
||||
|
||||
/**
|
||||
* get cache folder size as string
|
||||
* get cache folder size
|
||||
* @param \Base $f3
|
||||
* @return array
|
||||
*/
|
||||
protected function getCacheData(\Base $f3){
|
||||
protected function checkDirSize(\Base $f3) : array {
|
||||
// limit shown cache size. Reduce page load on big cache. In Bytes
|
||||
$maxBytes = 10 * 1024 * 1024; // 10MB
|
||||
$dirTemp = (string)$f3->get('TEMP');
|
||||
$cacheDsn = (string)$f3->get('CACHE');
|
||||
Config::parseDSN($cacheDsn, $conf);
|
||||
// if 'CACHE' is e.g. redis=... -> show default dir for cache
|
||||
$dirCache = $conf['type'] == 'folder' ? $conf['folder'] : $dirTemp . 'cache/';
|
||||
|
||||
// get all cache -----------------------------------------------------------------------------------------
|
||||
$cacheFilesAll = Search::getFilesByMTime( $f3->get('TEMP') );
|
||||
$dirAll = [
|
||||
'TEMP' => [
|
||||
'label' => 'Temp dir',
|
||||
'path' => $dirTemp
|
||||
],
|
||||
'CACHE' => [
|
||||
'label' => 'Cache dir',
|
||||
'path' => $dirCache
|
||||
]
|
||||
];
|
||||
|
||||
$maxHitAll = false;
|
||||
$bytesAll = 0;
|
||||
foreach($cacheFilesAll as $filename => $file) {
|
||||
$bytesAll += $file->getSize();
|
||||
}
|
||||
|
||||
// get data cache -----------------------------------------------------------------------------------------
|
||||
$cacheFilesData = Search::getFilesByMTime( $f3->get('TEMP') . 'cache/' );
|
||||
$bytesData = 0;
|
||||
foreach($cacheFilesData as $filename => $file) {
|
||||
$bytesData += $file->getSize();
|
||||
foreach($dirAll as $key => $dirData){
|
||||
$maxHit = false;
|
||||
$bytes = 0;
|
||||
$files = Search::getFilesByMTime($dirData['path']);
|
||||
foreach($files as $filename => $file) {
|
||||
$bytes += $file->getSize();
|
||||
if($bytes > $maxBytes){
|
||||
$maxHit = $maxHitAll = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
$bytesAll += $bytes;
|
||||
|
||||
$dirAll[$key]['size'] = ($maxHit ? '>' : '') . $this->convertBytes($bytes);
|
||||
$dirAll[$key]['task'] = [
|
||||
[
|
||||
'action' => http_build_query([
|
||||
'action' => 'clearFiles',
|
||||
'path' => $dirData['path']
|
||||
]),
|
||||
'label' => 'Delete files',
|
||||
'icon' => 'fa-trash',
|
||||
'btn' => 'btn-danger' . (($bytes > 0) ? '' : ' disabled')
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
'all' => $this->convertBytes($bytesAll),
|
||||
'data' => $this->convertBytes($bytesData),
|
||||
'template' => $this->convertBytes($bytesAll - $bytesData)
|
||||
'sizeAll' => ($maxHitAll ? '>' : '') . $this->convertBytes($bytesAll),
|
||||
'dirAll' => $dirAll
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* clear all cached files
|
||||
* @param \Base $f3
|
||||
* clear directory
|
||||
* @param string $path
|
||||
*/
|
||||
protected function clearCache(\Base $f3){
|
||||
$f3->clear('CACHE');
|
||||
protected function clearFiles(string $path){
|
||||
$files = Search::getFilesByMTime($path);
|
||||
foreach($files as $filename => $file){
|
||||
/**
|
||||
* @var $file \SplFileInfo
|
||||
*/
|
||||
if($file->isFile()){
|
||||
if($file->isWritable()){
|
||||
unlink($file->getRealPath());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* clear all key in a specific Redis database
|
||||
* @param string $host
|
||||
* @param int $port
|
||||
* @param int $db
|
||||
*/
|
||||
protected function flushRedisDb(string $host, int $port, int $db = 0){
|
||||
$client = new \Redis();
|
||||
$client->connect($host, $port, 0.3);
|
||||
$client->select($db);
|
||||
$client->flushDB();
|
||||
$client->close();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -104,13 +104,13 @@ class CcpSystemsUpdate extends AbstractCron {
|
||||
|
||||
// get current jump data --------------------------------------------------------------------------------------
|
||||
$time_start = microtime(true);
|
||||
$jumpData = $f3->ccpClient->getUniverseJumps();
|
||||
$jumpData = $f3->ccpClient()->getUniverseJumps();
|
||||
$time_end = microtime(true);
|
||||
$execTimeGetJumpData = $time_end - $time_start;
|
||||
|
||||
// get current kill data --------------------------------------------------------------------------------------
|
||||
$time_start = microtime(true);
|
||||
$killData = $f3->ccpClient->getUniverseKills();
|
||||
$killData = $f3->ccpClient()->getUniverseKills();
|
||||
$time_end = microtime(true);
|
||||
$execTimeGetKillData = $time_end - $time_start;
|
||||
|
||||
|
||||
@@ -67,8 +67,7 @@ class CharacterUpdate extends AbstractCron {
|
||||
if(is_object($characterLog->characterId)){
|
||||
// force characterLog as "updated" even if no changes were made
|
||||
$characterLog->characterId->updateLog([
|
||||
'markUpdated' => true,
|
||||
'suppressHTTPErrors' => true
|
||||
'markUpdated' => true
|
||||
]);
|
||||
}else{
|
||||
// character_log does not have a character assigned -> delete
|
||||
|
||||
@@ -177,7 +177,7 @@ class Universe extends AbstractCron {
|
||||
switch($type){
|
||||
case 'system':
|
||||
// load systems + dependencies (planets, star, types,...)
|
||||
$ids = $f3->ccpClient->getUniverseSystems();
|
||||
$ids = $f3->ccpClient()->getUniverseSystems();
|
||||
$modelClass = 'SystemModel';
|
||||
$setupModel = function(Model\Universe\SystemModel &$model, int $id){
|
||||
$model->loadById($id);
|
||||
@@ -186,7 +186,7 @@ class Universe extends AbstractCron {
|
||||
break;
|
||||
case 'stargate':
|
||||
// load all stargates. Systems must be present first!
|
||||
$ids = $f3->ccpClient->getUniverseSystems();
|
||||
$ids = $f3->ccpClient()->getUniverseSystems();
|
||||
$modelClass = 'SystemModel';
|
||||
$setupModel = function(Model\Universe\SystemModel &$model, int $id){
|
||||
$model->loadById($id);
|
||||
@@ -195,7 +195,7 @@ class Universe extends AbstractCron {
|
||||
break;
|
||||
case 'index_system':
|
||||
// setup system index, Systems must be present first!
|
||||
$ids = $f3->ccpClient->getUniverseSystems();
|
||||
$ids = $f3->ccpClient()->getUniverseSystems();
|
||||
$modelClass = 'SystemModel';
|
||||
$setupModel = function(Model\Universe\SystemModel &$model, int $id){
|
||||
$model->getById($id); // no loadById() here! would take "forever" when system not exists and build up first...
|
||||
|
||||
323
app/main/lib/api/AbstractClient.php
Normal file
323
app/main/lib/api/AbstractClient.php
Normal file
@@ -0,0 +1,323 @@
|
||||
<?php
|
||||
/**
|
||||
* Created by PhpStorm.
|
||||
* User: Exodus 4D
|
||||
* Date: 26.12.2018
|
||||
* Time: 17:41
|
||||
*/
|
||||
|
||||
namespace lib\api;
|
||||
|
||||
use Cache\Adapter\Filesystem\FilesystemCachePool;
|
||||
use Cache\Adapter\PHPArray\ArrayCachePool;
|
||||
use Cache\Adapter\Redis\RedisCachePool;
|
||||
use Cache\Namespaced\NamespacedCachePool;
|
||||
use League\Flysystem\Adapter\Local;
|
||||
use League\Flysystem\Filesystem;
|
||||
use lib\Config;
|
||||
use lib\Util;
|
||||
use lib\logging;
|
||||
use controller\LogController;
|
||||
use Exodus4D\ESI\Client\ApiInterface;
|
||||
use Psr\Cache\CacheItemPoolInterface;
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
|
||||
abstract class AbstractClient extends \Prefab {
|
||||
|
||||
const ERROR_CLIENT_INVALID = "HTTP API client not found → Check installed Composer packages";
|
||||
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
const CLIENT_NAME = null;
|
||||
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
protected $clientName = null;
|
||||
|
||||
/**
|
||||
* @var ApiInterface|null
|
||||
*/
|
||||
protected $client = null;
|
||||
|
||||
/**
|
||||
* PSR-6 compatible CachePool instance
|
||||
* -> can be Redis, Filesystem or Array cachePool
|
||||
* -> used by e.g. GuzzleCacheMiddleware
|
||||
* @var CacheItemPoolInterface|null
|
||||
*/
|
||||
protected $cachePool = null;
|
||||
|
||||
/**
|
||||
* @param \Base $f3
|
||||
* @return ApiInterface|null
|
||||
*/
|
||||
abstract protected function getClient(\Base $f3) : ?ApiInterface;
|
||||
|
||||
/**
|
||||
* get userAgent
|
||||
* @return string
|
||||
*/
|
||||
protected function getUserAgent() : string {
|
||||
$userAgent = '';
|
||||
$userAgent .= Config::getPathfinderData('name');
|
||||
$userAgent .= ' - ' . Config::getPathfinderData('version');
|
||||
$userAgent .= ' | ' . Config::getPathfinderData('contact');
|
||||
$userAgent .= ' (' . $_SERVER['SERVER_NAME'] . ')';
|
||||
return $userAgent;
|
||||
}
|
||||
|
||||
/**
|
||||
* returns a new Log object used within the Api for logging
|
||||
* @return \Closure
|
||||
*/
|
||||
protected function newLog() : \Closure {
|
||||
return function(string $action, string $level = 'warning') : logging\LogInterface {
|
||||
$log = new logging\ApiLog($action, $level);
|
||||
$log->addHandler('stream', 'json', $this->getStreamConfig($action));
|
||||
return $log;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* returns a new instance of PSR-6 compatible CacheItemPoolInterface
|
||||
* -> this Cache backend will be used across Guzzle Middleware
|
||||
* e.g. GuzzleCacheMiddleware
|
||||
* @see http://www.php-cache.com
|
||||
* @param \Base $f3
|
||||
* @return \Closure
|
||||
*/
|
||||
protected function getCachePool(\Base $f3) : \Closure {
|
||||
// determine cachePool options
|
||||
$poolConfig = $this->getCachePoolConfig($f3);
|
||||
|
||||
return function() use ($poolConfig) : ?CacheItemPoolInterface {
|
||||
// an active CachePool should be re-used
|
||||
// -> no need for e.g. a new Redis->connect()
|
||||
// and/or re-init when it is used the next time
|
||||
if(!is_null($this->cachePool)){
|
||||
return $this->cachePool;
|
||||
}
|
||||
|
||||
// Redis is preferred option (best performance) -----------------------------------------------------------
|
||||
if(
|
||||
$poolConfig['type'] == 'redis' &&
|
||||
extension_loaded('redis') &&
|
||||
class_exists('\Redis') &&
|
||||
class_exists(RedisCachePool::class)
|
||||
){
|
||||
$client = new \Redis();
|
||||
if(
|
||||
$client->connect(
|
||||
$poolConfig['host'],
|
||||
$poolConfig['port'],
|
||||
Config::REDIS_OPT_TIMEOUT,
|
||||
null,
|
||||
Config::REDIS_OPT_RETRY_INTERVAL,
|
||||
Config::REDIS_OPT_READ_TIMEOUT
|
||||
)
|
||||
){
|
||||
if(isset($poolConfig['db'])){
|
||||
$client->select($poolConfig['db']);
|
||||
}
|
||||
$poolRedis = new RedisCachePool($client);
|
||||
|
||||
// RedisCachePool supports "Hierarchy" store slots
|
||||
// -> "Hierarchy" support is required to use it in a NamespacedCachePool
|
||||
// This helps to separate keys by a namespace
|
||||
// @see http://www.php-cache.com/en/latest/
|
||||
$this->cachePool = new NamespacedCachePool($poolRedis, static::CLIENT_NAME);
|
||||
}
|
||||
}
|
||||
|
||||
// Filesystem is second option and fallback for failed Redis pool -----------------------------------------
|
||||
if(
|
||||
is_null($this->cachePool) &&
|
||||
in_array($poolConfig['type'], ['redis', 'folder']) &&
|
||||
class_exists(FilesystemCachePool::class)
|
||||
){
|
||||
$filesystemAdapter = new Local('./');
|
||||
$filesystem = new Filesystem($filesystemAdapter);
|
||||
|
||||
$poolFilesystem = new FilesystemCachePool($filesystem);
|
||||
$poolFilesystem->setFolder($poolConfig['folder']);
|
||||
$this->cachePool = $poolFilesystem;
|
||||
}
|
||||
|
||||
// Array cache pool fallback (not persistent) -------------------------------------------------------------
|
||||
if(
|
||||
is_null($this->cachePool) &&
|
||||
in_array($poolConfig['type'], ['redis', 'folder', 'array']) &&
|
||||
class_exists(ArrayCachePool::class)
|
||||
){
|
||||
$this->cachePool = new ArrayCachePool(2000);
|
||||
}
|
||||
|
||||
return $this->cachePool;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* get cachePool config from [D]ata [S]ource [N]ame string
|
||||
* @param \Base $f3
|
||||
* @return array
|
||||
*/
|
||||
protected function getCachePoolConfig(\Base $f3) : array {
|
||||
$dsn = (string)$f3->get('API_CACHE');
|
||||
|
||||
// fallback
|
||||
$conf = ['type' => 'array'];
|
||||
|
||||
if(!empty($folder = (string)$f3->get('TEMP'))){
|
||||
// filesystem (better than 'array' cache)
|
||||
$conf = [
|
||||
'type' => 'folder',
|
||||
'folder' => $folder . 'cache/'
|
||||
];
|
||||
}
|
||||
|
||||
// redis or filesystem -> overwrites $conf
|
||||
Config::parseDSN($dsn, $conf);
|
||||
|
||||
return $conf;
|
||||
}
|
||||
|
||||
/**
|
||||
* return callback function that expects a $request and checks
|
||||
* whether it should be logged (in case of errors)
|
||||
* @param \Base $f3
|
||||
* @return \Closure
|
||||
*/
|
||||
protected function isLoggable(\Base $f3) : \Closure {
|
||||
return function(RequestInterface $request) use ($f3) : bool {
|
||||
// we need the timestamp for $request that should be checked
|
||||
// -> we assume $request was "recently" send. -> current server time is used for check
|
||||
$requestTime = $f3->get('getDateTime')();
|
||||
// ... "interpolate" time to short interval
|
||||
// -> this might help to re-use sequential calls of this method
|
||||
Util::roundToInterval($requestTime);
|
||||
// check if request was send within ESI downTime range
|
||||
// -> errors during downTime should not be logged
|
||||
$inDowntimeRange = Config::inDownTimeRange($requestTime);
|
||||
|
||||
return !$inDowntimeRange;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* get Logger
|
||||
* @param string $ype
|
||||
* @return \Log
|
||||
*/
|
||||
protected function getLogger(string $ype = 'ERROR') : \Log {
|
||||
return LogController::getLogger($ype);
|
||||
}
|
||||
/**
|
||||
* get error msg for missing $this->client class
|
||||
* @param string $class
|
||||
* @return string
|
||||
*/
|
||||
protected function getMissingClassError(string $class) : string {
|
||||
return sprintf(Config::ERROR_CLASS_NOT_EXISTS_COMPOSER, $class);
|
||||
}
|
||||
|
||||
/**
|
||||
* get error msg for undefined method in $this->client class
|
||||
* @param string $class
|
||||
* @param string $method
|
||||
* @return string
|
||||
*/
|
||||
protected function getMissingMethodError(string $class, string $method) : string {
|
||||
return sprintf(Config::ERROR_METHOD_NOT_EXISTS_COMPOSER, $method, $class);
|
||||
}
|
||||
|
||||
/**
|
||||
* get config for stream logging
|
||||
* @param string $logFileName
|
||||
* @param bool $abs
|
||||
* @return \stdClass
|
||||
*/
|
||||
protected function getStreamConfig(string $logFileName, bool $abs = false) : \stdClass {
|
||||
$f3 = \Base::instance();
|
||||
|
||||
$config = (object) [];
|
||||
$config->stream = '';
|
||||
if( $f3->exists('LOGS', $dir) ){
|
||||
$config->stream .= $abs ? $f3->get('ROOT') . '/' : './';
|
||||
$config->stream .= $dir . $logFileName . '.log';
|
||||
$config->stream = $f3->fixslashes($config->stream);
|
||||
}
|
||||
return $config;
|
||||
}
|
||||
|
||||
/**
|
||||
* call request API data
|
||||
* @param string $name
|
||||
* @param array $arguments
|
||||
* @return array|mixed
|
||||
*/
|
||||
public function __call(string $name, array $arguments = []){
|
||||
$return = [];
|
||||
if(is_object($this->client)){
|
||||
if( method_exists($this->client, $name) ){
|
||||
$return = call_user_func_array([$this->client, $name], $arguments);
|
||||
}else{
|
||||
$errorMsg = $this->getMissingMethodError(get_class($this->client), $name);
|
||||
$this->getLogger('ERROR')->write($errorMsg);
|
||||
\Base::instance()->error(501, $errorMsg);
|
||||
}
|
||||
}else{
|
||||
\Base::instance()->error(501, self::ERROR_CLIENT_INVALID);
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* init web client on __invoke()
|
||||
* -> no need to init client on __construct()
|
||||
* maybe it is nerer used...
|
||||
* @return AbstractClient
|
||||
*/
|
||||
function __invoke() : self {
|
||||
$f3 = \Base::instance();
|
||||
|
||||
if(
|
||||
!($this->client instanceof ApiInterface) &&
|
||||
($this->getClient($f3) instanceof ApiInterface)
|
||||
){
|
||||
// web client not initialized
|
||||
$client = $this->getClient($f3);
|
||||
$client->setTimeout(3);
|
||||
$client->setUserAgent($this->getUserAgent());
|
||||
$client->setDecodeContent('gzip, deflate');
|
||||
|
||||
$client->setDebugLevel($f3->get('DEBUG'));
|
||||
$client->setNewLog($this->newLog());
|
||||
$client->setIsLoggable($this->isLoggable($f3));
|
||||
|
||||
$client->setLogStats(true); // add cURL stats (e.g. transferTime) to logged requests
|
||||
$client->setLogCache(true); // add cache info (e.g. from cached) to logged requests
|
||||
//$client->setLogAllStatus(true); // log all requests regardless of response HTTP status code
|
||||
$client->setLogFile('esi_requests');//
|
||||
|
||||
$client->setRetryLogFile('esi_retry_requests');
|
||||
|
||||
$client->setCacheDebug(true);
|
||||
$client->setCachePool($this->getCachePool($f3));
|
||||
|
||||
// use local proxy server for debugging requests
|
||||
#$client->setProxy('127.0.0.1:8888');
|
||||
|
||||
// disable SSL certificate verification -> allow proxy to decode(view) request
|
||||
#$client->setVerify(false);
|
||||
|
||||
//$client->setDebugRequests(true);
|
||||
|
||||
$this->client = $client;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
37
app/main/lib/api/CcpClient.php
Normal file
37
app/main/lib/api/CcpClient.php
Normal file
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
/**
|
||||
* Created by PhpStorm.
|
||||
* User: Exodus 4D
|
||||
* Date: 26.12.2018
|
||||
* Time: 17:43
|
||||
*/
|
||||
|
||||
namespace lib\api;
|
||||
|
||||
use lib\Config;
|
||||
use Exodus4D\ESI\Client\ESI as Client;
|
||||
use Exodus4D\ESI\Client\ApiInterface;
|
||||
|
||||
class CcpClient extends AbstractClient {
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
const CLIENT_NAME = 'ccpClient';
|
||||
|
||||
/**
|
||||
* @param \Base $f3
|
||||
* @return ApiInterface|null
|
||||
*/
|
||||
protected function getClient(\Base $f3) : ?ApiInterface {
|
||||
$client = null;
|
||||
if(class_exists(Client::class)){
|
||||
$client = new Client(Config::getEnvironmentData('CCP_ESI_URL'));
|
||||
$client->setDataSource(Config::getEnvironmentData('CCP_ESI_DATASOURCE'));
|
||||
}else{
|
||||
$this->getLogger()->write($this->getMissingClassError(Client::class));
|
||||
}
|
||||
|
||||
return $client;
|
||||
}
|
||||
}
|
||||
36
app/main/lib/api/GitHubClient.php
Normal file
36
app/main/lib/api/GitHubClient.php
Normal file
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
/**
|
||||
* Created by PhpStorm.
|
||||
* User: Exodus 4D
|
||||
* Date: 29.01.2019
|
||||
* Time: 22:23
|
||||
*/
|
||||
|
||||
namespace lib\api;
|
||||
|
||||
use lib\Config;
|
||||
use Exodus4D\ESI\Client\Github as Client;
|
||||
use Exodus4D\ESI\Client\ApiInterface;
|
||||
|
||||
class GitHubClient extends AbstractClient {
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
const CLIENT_NAME = 'gitHubClient';
|
||||
|
||||
/**
|
||||
* @param \Base $f3
|
||||
* @return ApiInterface|null
|
||||
*/
|
||||
protected function getClient(\Base $f3) : ?ApiInterface {
|
||||
$client = null;
|
||||
if(class_exists(Client::class)){
|
||||
$client = new Client(Config::getPathfinderData('api.git_hub'));
|
||||
}else{
|
||||
$this->getLogger()->write($this->getMissingClassError(Client::class));
|
||||
}
|
||||
|
||||
return $client;
|
||||
}
|
||||
}
|
||||
36
app/main/lib/api/SsoClient.php
Normal file
36
app/main/lib/api/SsoClient.php
Normal file
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
/**
|
||||
* Created by PhpStorm.
|
||||
* User: Exodus 4D
|
||||
* Date: 26.12.2018
|
||||
* Time: 17:39
|
||||
*/
|
||||
|
||||
namespace lib\api;
|
||||
|
||||
use lib\Config;
|
||||
use Exodus4D\ESI\Client\SSO as Client;
|
||||
use Exodus4D\ESI\Client\ApiInterface;
|
||||
|
||||
class SsoClient extends AbstractClient {
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
const CLIENT_NAME = 'ssoClient';
|
||||
|
||||
/**
|
||||
* @param \Base $f3
|
||||
* @return ApiInterface|null
|
||||
*/
|
||||
protected function getClient(\Base $f3) : ?ApiInterface {
|
||||
$client = null;
|
||||
if(class_exists(Client::class)){
|
||||
$client = new Client(Config::getEnvironmentData('CCP_SSO_URL'));
|
||||
}else{
|
||||
$this->getLogger()->write($this->getMissingClassError(Client::class));
|
||||
}
|
||||
|
||||
return $client;
|
||||
}
|
||||
}
|
||||
@@ -1,90 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Created by PhpStorm.
|
||||
* User: Exodus4D
|
||||
* Date: 26.03.2017
|
||||
* Time: 19:17
|
||||
*/
|
||||
|
||||
namespace lib;
|
||||
|
||||
use controller\LogController;
|
||||
use \Exodus4D\ESI\ESI as ApiClient;
|
||||
|
||||
class CcpClient extends \Prefab {
|
||||
|
||||
private $apiClient;
|
||||
|
||||
public function __construct(\Base $f3){
|
||||
$this->apiClient = $this->getClient($f3);
|
||||
$f3->set('ccpClient', $this);
|
||||
}
|
||||
|
||||
/**
|
||||
* get ApiClient instance
|
||||
* @param \Base $f3
|
||||
* @return ApiClient|null
|
||||
*/
|
||||
protected function getClient(\Base $f3){
|
||||
$client = null;
|
||||
|
||||
if(class_exists(ApiClient::class)){
|
||||
$client = new ApiClient();
|
||||
$client->setUrl( Config::getEnvironmentData('CCP_ESI_URL') );
|
||||
$client->setDatasource( Config::getEnvironmentData('CCP_ESI_DATASOURCE') );
|
||||
$client->setUserAgent($this->getUserAgent());
|
||||
$client->setDebugLevel($f3->get('DEBUG'));
|
||||
//$client->setDebugLogRequests(true);
|
||||
}else{
|
||||
LogController::getLogger('ERROR')->write(sprintf(Config::ERROR_CLASS_NOT_EXISTS_COMPOSER, ApiClient::class));
|
||||
}
|
||||
|
||||
return $client;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
protected function getUserAgent(){
|
||||
$userAgent = '';
|
||||
$userAgent .= Config::getPathfinderData('name');
|
||||
$userAgent .= ' - ' . Config::getPathfinderData('version');
|
||||
$userAgent .= ' | ' . Config::getPathfinderData('contact');
|
||||
$userAgent .= ' (' . $_SERVER['SERVER_NAME'] . ')';
|
||||
|
||||
return $userAgent;
|
||||
}
|
||||
|
||||
/**
|
||||
* get error msg for undefined method in ApiClient() class
|
||||
* @param $method
|
||||
* @return string
|
||||
*/
|
||||
protected function getMissingMethodError($method){
|
||||
return "Method '" . $method . "()' not found in class '" . get_class($this->apiClient) . "'. -> Check installed Composer package version.'";
|
||||
}
|
||||
|
||||
/**
|
||||
* call request API data
|
||||
* @param $name
|
||||
* @param $arguments
|
||||
* @return array|mixed
|
||||
*/
|
||||
public function __call($name, $arguments){
|
||||
$return = [];
|
||||
|
||||
if(is_object($this->apiClient)){
|
||||
if( method_exists($this->apiClient, $name) ){
|
||||
$return = call_user_func_array([$this->apiClient, $name], $arguments);
|
||||
}else{
|
||||
LogController::getLogger('ERROR')->write($this->getMissingMethodError($name));
|
||||
\Base::instance()->error(501, $this->getMissingMethodError($name));
|
||||
}
|
||||
}else{
|
||||
LogController::getLogger('ERROR')->write(sprintf(Config::ERROR_CLASS_NOT_EXISTS_COMPOSER, ApiClient::class));
|
||||
\Base::instance()->error(501, sprintf(Config::ERROR_CLASS_NOT_EXISTS_COMPOSER, ApiClient::class));
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
}
|
||||
@@ -8,8 +8,10 @@
|
||||
|
||||
namespace lib;
|
||||
|
||||
use controller\LogController;
|
||||
use Exception;
|
||||
|
||||
use lib\api\CcpClient;
|
||||
use lib\api\GitHubClient;
|
||||
use lib\api\SsoClient;
|
||||
|
||||
class Config extends \Prefab {
|
||||
|
||||
@@ -20,6 +22,29 @@ class Config extends \Prefab {
|
||||
const CACHE_KEY_SOCKET_VALID = 'CACHED_SOCKET_VALID';
|
||||
const CACHE_TTL_SOCKET_VALID = 60;
|
||||
|
||||
// ================================================================================================================
|
||||
// Redis
|
||||
// ================================================================================================================
|
||||
|
||||
/**
|
||||
* Redis connect timeout (seconds)
|
||||
*/
|
||||
const REDIS_OPT_TIMEOUT = 2;
|
||||
|
||||
/**
|
||||
* Redis read timeout (seconds)
|
||||
*/
|
||||
const REDIS_OPT_READ_TIMEOUT = 10;
|
||||
|
||||
/**
|
||||
* redis retry interval (milliseconds)
|
||||
*/
|
||||
const REDIS_OPT_RETRY_INTERVAL = 200;
|
||||
|
||||
// ================================================================================================================
|
||||
// EVE downtime
|
||||
// ================================================================================================================
|
||||
|
||||
/**
|
||||
* SSO downtime length (estimation), minutes
|
||||
*/
|
||||
@@ -30,7 +55,8 @@ class Config extends \Prefab {
|
||||
*/
|
||||
const DOWNTIME_BUFFER = 1;
|
||||
|
||||
const ERROR_CLASS_NOT_EXISTS_COMPOSER = 'Class "%s" not found. -> Check installed Composer packages';
|
||||
const ERROR_CLASS_NOT_EXISTS_COMPOSER = 'Class "%s" not found. → Check installed Composer packages';
|
||||
const ERROR_METHOD_NOT_EXISTS_COMPOSER = 'Method "%s()" not found in class "%s". → Check installed Composer packages';
|
||||
|
||||
|
||||
/**
|
||||
@@ -59,10 +85,21 @@ class Config extends \Prefab {
|
||||
// -> overwrites default configuration
|
||||
$this->setHiveVariables($f3);
|
||||
|
||||
// set global function for current DateTimeZone()
|
||||
$f3->set('getTimeZone', function() use ($f3){
|
||||
// set global getter for \DateTimeZone
|
||||
$f3->set('getTimeZone', function() use ($f3) : \DateTimeZone {
|
||||
return new \DateTimeZone( $f3->get('TZ') );
|
||||
});
|
||||
|
||||
// set global getter for new \DateTime
|
||||
$f3->set('getDateTime', function(string $time = 'now', ?\DateTimeZone $timeZone = null) use ($f3) : \DateTime {
|
||||
$timeZone = $timeZone ? : $f3->get('getTimeZone')();
|
||||
return new \DateTime($time, $timeZone);
|
||||
});
|
||||
|
||||
// lazy init Web Api clients
|
||||
$f3->set(SsoClient::CLIENT_NAME, SsoClient::instance());
|
||||
$f3->set(CcpClient::CLIENT_NAME, CcpClient::instance());
|
||||
$f3->set(GitHubClient::CLIENT_NAME, GitHubClient::instance());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -410,6 +447,33 @@ class Config extends \Prefab {
|
||||
return $status;
|
||||
}
|
||||
|
||||
/**
|
||||
* parse [D]ata [S]ource [N]ame string from *.ini into $conf parts
|
||||
* -> $dsn = redis=localhost:6379:2
|
||||
* $conf = ['type' => 'redis', 'host' => 'localhost', 'port' => 6379, 'db' => 2]
|
||||
* -> some $conf values might be NULL if not found in $dsn!
|
||||
* -> some missing values become defaults
|
||||
* @param string $dsn
|
||||
* @param array|null $conf
|
||||
* @return bool
|
||||
*/
|
||||
static function parseDSN(string $dsn, ?array &$conf = []) : bool {
|
||||
// reset reference
|
||||
if($matches = (bool)preg_match('/^(\w+)\h*=\h*(.+)/', strtolower(trim($dsn)), $parts)){
|
||||
$conf['type'] = $parts[1];
|
||||
if($conf['type'] == 'redis'){
|
||||
list($conf['host'], $conf['port'], $conf['db']) = explode(':', $parts[2]) + [1 => 6379, 2 => null];
|
||||
}elseif($conf['type'] == 'folder'){
|
||||
$conf['folder'] = $parts[2];
|
||||
}
|
||||
// int cast numeric values
|
||||
$conf = array_map(function($val){
|
||||
return is_numeric($val) ? intval($val) : $val;
|
||||
}, $conf);
|
||||
}
|
||||
return $matches;
|
||||
}
|
||||
|
||||
/**
|
||||
* check if a given DateTime() is within downTime range: downtime + 10m
|
||||
* -> can be used for prevent logging errors during downTime
|
||||
|
||||
@@ -198,7 +198,7 @@ abstract class AbstractLog implements LogInterface {
|
||||
* @param \stdClass|null $handlerParams
|
||||
* @return LogInterface
|
||||
*/
|
||||
public function addHandler(string $handlerKey, string $formatterKey = null, \stdClass $handlerParams = null): LogInterface{
|
||||
public function addHandler(string $handlerKey, string $formatterKey = null, \stdClass $handlerParams = null): LogInterface {
|
||||
if(!$this->hasHandlerKey($handlerKey)){
|
||||
$this->handlerConfig[$handlerKey] = $formatterKey;
|
||||
// add more configuration params for the new handler
|
||||
@@ -214,7 +214,7 @@ abstract class AbstractLog implements LogInterface {
|
||||
* @param string $handlerKey
|
||||
* @return LogInterface
|
||||
*/
|
||||
public function addHandlerGroup(string $handlerKey): LogInterface{
|
||||
public function addHandlerGroup(string $handlerKey) : LogInterface {
|
||||
if(
|
||||
$this->hasHandlerKey($handlerKey) &&
|
||||
!$this->hasHandlerGroupKey($handlerKey)
|
||||
@@ -227,7 +227,7 @@ abstract class AbstractLog implements LogInterface {
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getHandlerConfig(): array{
|
||||
public function getHandlerConfig() : array{
|
||||
return $this->handlerConfig;
|
||||
}
|
||||
|
||||
@@ -237,7 +237,7 @@ abstract class AbstractLog implements LogInterface {
|
||||
* @return array
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function getHandlerParams(string $handlerKey): array{
|
||||
public function getHandlerParams(string $handlerKey) : array {
|
||||
$params = [];
|
||||
|
||||
if($this->hasHandlerKey($handlerKey)){
|
||||
@@ -267,14 +267,14 @@ abstract class AbstractLog implements LogInterface {
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getHandlerParamsConfig(): array{
|
||||
public function getHandlerParamsConfig(): array {
|
||||
return $this->handlerParamsConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getProcessorConfig(): array{
|
||||
public function getProcessorConfig(): array {
|
||||
return $this->processorConfig;
|
||||
}
|
||||
|
||||
|
||||
40
app/main/lib/logging/ApiLog.php
Normal file
40
app/main/lib/logging/ApiLog.php
Normal file
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
/**
|
||||
* Created by PhpStorm.
|
||||
* User: Exodus 4D
|
||||
* Date: 01.01.2019
|
||||
* Time: 16:42
|
||||
*/
|
||||
|
||||
namespace lib\logging;
|
||||
|
||||
|
||||
class ApiLog extends AbstractLog {
|
||||
|
||||
/**
|
||||
* List of possible handlers (tested)
|
||||
* -> final handler will be set dynamic for per instance
|
||||
* @var array
|
||||
*/
|
||||
protected $handlerConfig = [
|
||||
//'stream' => 'json'
|
||||
];
|
||||
|
||||
protected $channelType = 'api';
|
||||
|
||||
public function __construct(string $action, string $level){
|
||||
parent::__construct($action);
|
||||
|
||||
$this->setLevel($level);
|
||||
}
|
||||
|
||||
/**
|
||||
* overwrites parent
|
||||
* -> we need unique channelNames for different $actions within same $channelType
|
||||
* -> otherwise logs would be bundled into the first log file handler
|
||||
* @return string
|
||||
*/
|
||||
public function getChannelName(): string{
|
||||
return $this->getChannelType() . '_' . $this->getAction();
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,8 @@ interface LogInterface {
|
||||
|
||||
public function setLevel(string $level);
|
||||
|
||||
public function setTag(string $tag);
|
||||
|
||||
public function setData(array $data): LogInterface;
|
||||
|
||||
public function setTempData(array $data): LogInterface;
|
||||
@@ -61,4 +63,5 @@ interface LogInterface {
|
||||
|
||||
public function removeHandlerGroup(string $handlerKey);
|
||||
|
||||
public function buffer();
|
||||
}
|
||||
@@ -72,7 +72,7 @@ class RallyLog extends AbstractCharacterLog{
|
||||
}
|
||||
|
||||
// add human readable changes to string ---------------------------------------------------
|
||||
$data['formatted'] =$this->formatData($data);
|
||||
$data['formatted'] = $this->formatData($data);
|
||||
|
||||
return $data;
|
||||
}
|
||||
@@ -89,10 +89,10 @@ class RallyLog extends AbstractCharacterLog{
|
||||
!empty($data['channel'])
|
||||
){
|
||||
$replace = [
|
||||
'{objName}' => $data['object']['objName'],
|
||||
'{objId}' => $data['object']['objId'],
|
||||
'{objName}' => $data['object']['objName'],
|
||||
'{objId}' => $data['object']['objId'],
|
||||
'{channelName}' => $data['channel']['channelName'],
|
||||
'{channelId}' => $data['channel']['channelId']
|
||||
'{channelId}' => $data['channel']['channelId']
|
||||
];
|
||||
$string = str_replace(array_keys($replace), array_values($replace), $this->getMessage());
|
||||
}
|
||||
|
||||
@@ -130,4 +130,54 @@ class Util {
|
||||
sort($scopes);
|
||||
return md5(serialize($scopes));
|
||||
}
|
||||
|
||||
/**
|
||||
* get some information about a $source file/dir
|
||||
* @param string|null $source
|
||||
* @return array
|
||||
*/
|
||||
static function filesystemInfo(?string $source) : array {
|
||||
$info = [];
|
||||
if(is_dir($source)){
|
||||
$info['isDir'] = true;
|
||||
}elseif(is_file($source)){
|
||||
$info['isFile'] = true;
|
||||
}
|
||||
if(!empty($info)){
|
||||
$info['chmod'] = substr(sprintf('%o', fileperms($source)), -4);
|
||||
}
|
||||
return $info;
|
||||
}
|
||||
|
||||
/**
|
||||
* round DateTime to interval
|
||||
* @param \DateTime $dateTime
|
||||
* @param string $type
|
||||
* @param int $interval
|
||||
* @param string $round
|
||||
*/
|
||||
static function roundToInterval(\DateTime &$dateTime, string $type = 'sec', int $interval = 5, string $round = 'floor'){
|
||||
$hours = $minutes = $seconds = 0;
|
||||
|
||||
$roundInterval = function(string $format, int $interval, string $round) : int {
|
||||
return call_user_func($round, $format / $interval) * $interval;
|
||||
};
|
||||
|
||||
switch($type){
|
||||
case 'hour':
|
||||
$hours = $roundInterval($dateTime->format('H'), $interval, $round);
|
||||
break;
|
||||
case 'min':
|
||||
$hours = $dateTime->format('H');
|
||||
$minutes = $roundInterval($dateTime->format('i'), $interval, $round);
|
||||
break;
|
||||
case 'sec':
|
||||
$hours = $dateTime->format('H');
|
||||
$minutes = $dateTime->format('i');
|
||||
$seconds = $roundInterval($dateTime->format('s'), $interval, $round);
|
||||
break;
|
||||
}
|
||||
|
||||
$dateTime->setTime($hours, $minutes, $seconds);
|
||||
}
|
||||
}
|
||||
@@ -1,245 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Created by PhpStorm.
|
||||
* User: Exodus
|
||||
* Date: 12.03.2016
|
||||
* Time: 12:28
|
||||
*/
|
||||
|
||||
namespace lib;
|
||||
|
||||
use controller\LogController;
|
||||
|
||||
class Web extends \Web {
|
||||
|
||||
const ERROR_STATUS_LOG = 'HTTP %s: \'%s\' | url: %s \'%s\'%s';
|
||||
|
||||
/**
|
||||
* max retry attempts (cURL requests) for a single resource until giving up...
|
||||
* -> this is because SSO API is not very stable
|
||||
* 2 == (initial request + 2 retry == max of 3)
|
||||
*/
|
||||
const RETRY_COUNT_MAX = 2;
|
||||
|
||||
/**
|
||||
* end of line
|
||||
* @var string
|
||||
*/
|
||||
private $eol = "\r\n";
|
||||
|
||||
/**
|
||||
* get status code from Header data array
|
||||
* @param array $headers
|
||||
* @return int
|
||||
*/
|
||||
protected function getStatusCodeFromHeaders(array $headers = []) : int {
|
||||
$statusCode = 0;
|
||||
if(
|
||||
preg_match(
|
||||
'/HTTP\/1\.\d (\d{3}?)/',
|
||||
implode($this->eol, $headers),
|
||||
$matches
|
||||
)
|
||||
){
|
||||
$statusCode = (int)$matches[1];
|
||||
}
|
||||
return $statusCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* get cache time in seconds from Header data array
|
||||
* @param array $headers
|
||||
* @return int
|
||||
*/
|
||||
protected function getCacheTimeFromHeaders(array $headers = []) : int {
|
||||
$cacheTime = 0;
|
||||
if(
|
||||
preg_match(
|
||||
'/Cache-Control:(.*?)max-age=([0-9]+)/',
|
||||
implode($this->eol, $headers),
|
||||
$matches
|
||||
)
|
||||
){
|
||||
$cacheTime = (int)$matches[2];
|
||||
}elseif(
|
||||
preg_match(
|
||||
'/Access-Control-Max-Age: ([0-9]+)/',
|
||||
implode($this->eol, $headers),
|
||||
$matches
|
||||
)
|
||||
){
|
||||
$cacheTime = (int)$matches[1];
|
||||
}
|
||||
return $cacheTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* get a unique cache kay for a request
|
||||
* @param $url
|
||||
* @param null $options
|
||||
* @return string
|
||||
*/
|
||||
protected function getCacheKey(string $url, $options = null) : string {
|
||||
$f3 = \Base::instance();
|
||||
|
||||
$headers = isset($options['header']) ? implode($this->eol, (array)$options['header']) : '';
|
||||
|
||||
return $f3->hash(
|
||||
$options['method'] . ' '
|
||||
. $url . ' '
|
||||
. $headers
|
||||
) . 'url';
|
||||
}
|
||||
|
||||
/**
|
||||
* perform curl() request
|
||||
* -> caches response by returned HTTP Cache header data
|
||||
* @param string $url
|
||||
* @param array|null $options
|
||||
* @param array $additionalOptions
|
||||
* @param int $retryCount request counter for failed call
|
||||
* @return array|FALSE|mixed
|
||||
*/
|
||||
public function request($url, array $options = null, array $additionalOptions = [], int $retryCount = 0){
|
||||
$f3 = \Base::instance();
|
||||
|
||||
if( !$f3->exists($hash = $this->getCacheKey($url, $options), $result) ){
|
||||
// set max retry count in case of request error
|
||||
$retryCountMax = isset($additionalOptions['retryCountMax']) ? (int)$additionalOptions['retryCountMax'] : self::RETRY_COUNT_MAX;
|
||||
|
||||
// retry same request until retry limit is reached
|
||||
$retry = false;
|
||||
|
||||
$result = parent::request($url, $options);
|
||||
$result['timeout'] = false;
|
||||
$statusCode = $this->getStatusCodeFromHeaders( $result['headers'] );
|
||||
|
||||
switch($statusCode){
|
||||
case 100:
|
||||
case 200:
|
||||
// request succeeded -> check if response should be cached
|
||||
$ttl = $this->getCacheTimeFromHeaders( $result['headers'] );
|
||||
|
||||
if(
|
||||
$ttl > 0 &&
|
||||
!empty( json_decode( $result['body'], true ) )
|
||||
){
|
||||
$f3->set($hash, $result, $ttl);
|
||||
}
|
||||
break;
|
||||
case 401:
|
||||
case 415:
|
||||
// unauthorized
|
||||
$errorMsg = $this->getErrorMessageFromJsonResponse(
|
||||
$statusCode,
|
||||
$options['method'],
|
||||
$url,
|
||||
json_decode($result['body'])
|
||||
);
|
||||
|
||||
// if request not within downTime time range -> log error
|
||||
if( !Config::inDownTimeRange() ){
|
||||
LogController::getLogger('ERROR')->write($errorMsg);
|
||||
}
|
||||
break;
|
||||
case 500:
|
||||
case 501:
|
||||
case 502:
|
||||
case 503:
|
||||
case 505:
|
||||
$retry = true;
|
||||
|
||||
if($retryCount == $retryCountMax){
|
||||
$errorMsg = $this->getErrorMessageFromJsonResponse(
|
||||
$statusCode,
|
||||
$options['method'],
|
||||
$url,
|
||||
json_decode($result['body'])
|
||||
);
|
||||
|
||||
// if request not within downTime time range -> log error
|
||||
if( !Config::inDownTimeRange() ){
|
||||
LogController::getLogger('ERROR')->write($errorMsg);
|
||||
}
|
||||
|
||||
// trigger error
|
||||
if($additionalOptions['suppressHTTPErrors'] !== true){
|
||||
$f3->error($statusCode, $errorMsg);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 504:
|
||||
case 0:
|
||||
$retry = true;
|
||||
|
||||
if($retryCount == $retryCountMax){
|
||||
// timeout -> response should not be cached
|
||||
$result['timeout'] = true;
|
||||
|
||||
$errorMsg = $this->getErrorMessageFromJsonResponse(
|
||||
504,
|
||||
$options['method'],
|
||||
$url,
|
||||
json_decode($result['body'])
|
||||
);
|
||||
|
||||
// if request not within downTime time range -> log error
|
||||
if( !Config::inDownTimeRange() ){
|
||||
LogController::getLogger('ERROR')->write($errorMsg);
|
||||
}
|
||||
|
||||
if($additionalOptions['suppressHTTPErrors'] !== true){
|
||||
$f3->error(504, $errorMsg);
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
// unknown status
|
||||
$errorMsg = $this->getErrorMessageFromJsonResponse(
|
||||
$statusCode,
|
||||
$options['method'],
|
||||
$url
|
||||
);
|
||||
|
||||
if( !Config::inDownTimeRange() ){
|
||||
LogController::getLogger('ERROR')->write($errorMsg);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if(
|
||||
$retry &&
|
||||
$retryCount < $retryCountMax
|
||||
){
|
||||
$retryCount++;
|
||||
$this->request($url, $options, $additionalOptions, $retryCount);
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* get error message from response object
|
||||
* @param int $code
|
||||
* @param string $method
|
||||
* @param string $url
|
||||
* @param null|\stdClass $responseBody
|
||||
* @return string
|
||||
*/
|
||||
protected function getErrorMessageFromJsonResponse($code, $method, $url, $responseBody = null){
|
||||
if( empty($responseBody->message) ){
|
||||
$message = @constant('Base::HTTP_' . $code);
|
||||
}else{
|
||||
$message = $responseBody->message;
|
||||
}
|
||||
|
||||
$body = '';
|
||||
if( !is_null($responseBody) ){
|
||||
$body = ' | body: ' . print_r($responseBody, true);
|
||||
}
|
||||
|
||||
return sprintf(self::ERROR_STATUS_LOG, $code, $message, $method, $url, $body);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -141,7 +141,7 @@ class AllianceModel extends BasicModel {
|
||||
$alliance = parent::getById($id, $ttl, $isActive);
|
||||
if($alliance->isOutdated()){
|
||||
// request alliance data
|
||||
$allianceData = self::getF3()->ccpClient->getAllianceData($id);
|
||||
$allianceData = self::getF3()->ccpClient()->getAllianceData($id);
|
||||
if( !empty($allianceData) ){
|
||||
$alliance->copyfrom($allianceData, ['id', 'name', 'ticker']);
|
||||
$alliance->save();
|
||||
|
||||
@@ -11,7 +11,6 @@ namespace Model;
|
||||
use Controller\Ccp\Sso as Sso;
|
||||
use Controller\Api\User as User;
|
||||
use DB\SQL\Schema;
|
||||
use lib\Util;
|
||||
use lib\Config;
|
||||
use lib\Socket;
|
||||
use Model\Universe;
|
||||
@@ -512,15 +511,8 @@ class CharacterModel extends BasicModel {
|
||||
$refreshToken &&
|
||||
!empty($this->esiRefreshToken)
|
||||
){
|
||||
$additionalOptions = [];
|
||||
if($accessToken){
|
||||
// ... close to expire token exists -> moderate failover settings
|
||||
$additionalOptions['suppressHTTPErrors'] = true;
|
||||
$additionalOptions['retryCountMax'] = 0;
|
||||
}
|
||||
|
||||
$ssoController = new Sso();
|
||||
$accessData = $ssoController->refreshAccessToken($this->esiRefreshToken, $additionalOptions);
|
||||
$accessData = $ssoController->refreshAccessToken($this->esiRefreshToken);
|
||||
|
||||
if(isset($accessData->accessToken, $accessData->esiAccessTokenExpires, $accessData->refreshToken)){
|
||||
$this->esiAccessToken = $accessData->accessToken;
|
||||
@@ -758,12 +750,12 @@ class CharacterModel extends BasicModel {
|
||||
){
|
||||
// Try to pull data from API
|
||||
if( $accessToken = $this->getAccessToken() ){
|
||||
$onlineData = self::getF3()->ccpClient->getCharacterOnlineData($this->_id, $accessToken, $additionalOptions);
|
||||
$onlineData = self::getF3()->ccpClient()->getCharacterOnlineData($this->_id, $accessToken);
|
||||
|
||||
// check whether character is currently ingame online
|
||||
if(is_bool($onlineData['online'])){
|
||||
if($onlineData['online'] === true){
|
||||
$locationData = self::getF3()->ccpClient->getCharacterLocationData($this->_id, $accessToken, $additionalOptions);
|
||||
$locationData = self::getF3()->ccpClient()->getCharacterLocationData($this->_id, $accessToken);
|
||||
|
||||
if( !empty($locationData['system']['id']) ){
|
||||
// character is currently in-game
|
||||
@@ -807,7 +799,7 @@ class CharacterModel extends BasicModel {
|
||||
// get "more" data for systemId and/or stationId -----------------------------------------
|
||||
if( !empty($lookupUniverseIds) ){
|
||||
// get "more" information for some Ids (e.g. name)
|
||||
$universeData = self::getF3()->ccpClient->getUniverseNamesData($lookupUniverseIds, $additionalOptions);
|
||||
$universeData = self::getF3()->ccpClient()->getUniverseNamesData($lookupUniverseIds);
|
||||
|
||||
if( !empty($universeData) && !isset($universeData['error']) ){
|
||||
// We expect max ONE system AND/OR station data, not an array of e.g. systems
|
||||
@@ -860,7 +852,7 @@ class CharacterModel extends BasicModel {
|
||||
|
||||
// check ship data for changes ------------------------------------------------------------
|
||||
if( !$deleteLog ){
|
||||
$shipData = self::getF3()->ccpClient->getCharacterShipData($this->_id, $accessToken, $additionalOptions);
|
||||
$shipData = self::getF3()->ccpClient()->getCharacterShipData($this->_id, $accessToken);
|
||||
|
||||
// IDs for "shipTypeId" that require more data
|
||||
$lookupShipTypeId = 0;
|
||||
@@ -989,14 +981,14 @@ class CharacterModel extends BasicModel {
|
||||
// -> the "id" check is just for security and should NEVER fail!
|
||||
$ssoController = new Sso();
|
||||
if(
|
||||
!is_null( $verificationCharacterData = $ssoController->verifyCharacterData($accessToken) ) &&
|
||||
$verificationCharacterData->CharacterID === $this->_id
|
||||
!empty( $verificationCharacterData = $ssoController->verifyCharacterData($accessToken) ) &&
|
||||
$verificationCharacterData['characterId'] === $this->_id
|
||||
){
|
||||
// get character data from API
|
||||
$characterData = $ssoController->getCharacterData($this->_id);
|
||||
if( !empty($characterData->character) ){
|
||||
$characterData->character['ownerHash'] = $verificationCharacterData->CharacterOwnerHash;
|
||||
$characterData->character['esiScopes'] = Util::convertScopesString($verificationCharacterData->Scopes);
|
||||
$characterData->character['ownerHash'] = $verificationCharacterData['characterOwnerHash'];
|
||||
$characterData->character['esiScopes'] = $verificationCharacterData['scopes'];
|
||||
|
||||
$this->copyfrom($characterData->character, ['ownerHash', 'esiScopes', 'securityStatus']);
|
||||
$this->corporationId = $characterData->corporation;
|
||||
|
||||
@@ -284,7 +284,7 @@ class CorporationModel extends BasicModel {
|
||||
!empty($accessToken) &&
|
||||
!$this->isNPC
|
||||
){
|
||||
$response = self::getF3()->ccpClient->getCorporationRoles($this->_id, $accessToken);
|
||||
$response = self::getF3()->ccpClient()->getCorporationRoles($this->_id, $accessToken);
|
||||
if( !empty($response['roles']) ){
|
||||
$characterRolesData = (array)$response['roles'];
|
||||
}
|
||||
@@ -351,10 +351,10 @@ class CorporationModel extends BasicModel {
|
||||
$corporation = parent::getById($id, $ttl, $isActive);
|
||||
if($corporation->isOutdated()){
|
||||
// request corporation data
|
||||
$corporationData = self::getF3()->ccpClient->getCorporationData($id);
|
||||
$corporationData = self::getF3()->ccpClient()->getCorporationData($id);
|
||||
if( !empty($corporationData) ){
|
||||
// check for NPC corporation
|
||||
$corporationData['isNPC'] = self::getF3()->ccpClient->isNpcCorporation($id);
|
||||
$corporationData['isNPC'] = self::getF3()->ccpClient()->isNpcCorporation($id);
|
||||
|
||||
$corporation->copyfrom($corporationData, ['id', 'name', 'ticker', 'memberCount', 'isNPC']);
|
||||
$corporation->save();
|
||||
|
||||
@@ -1020,7 +1020,7 @@ class MapModel extends AbstractMapTrackingModel {
|
||||
* get object relevant data for model log channel
|
||||
* @return array
|
||||
*/
|
||||
public function getLogChannelData() : array{
|
||||
public function getLogChannelData() : array {
|
||||
return [
|
||||
'channelId' => $this->_id,
|
||||
'channelName' => $this->name
|
||||
@@ -1030,7 +1030,7 @@ class MapModel extends AbstractMapTrackingModel {
|
||||
* get object relevant data for model log object
|
||||
* @return array
|
||||
*/
|
||||
public function getLogObjectData() : array{
|
||||
public function getLogObjectData() : array {
|
||||
return [
|
||||
'objId' => $this->_id,
|
||||
'objName' => $this->name
|
||||
|
||||
@@ -665,7 +665,7 @@ class SystemModel extends AbstractMapTrackingModel {
|
||||
public function sendRallyPoke(array $rallyData, CharacterModel $characterModel){
|
||||
// rally log needs at least one handler to be valid
|
||||
$isValidLog = false;
|
||||
$log = new Logging\RallyLog('rallySet', $this->getMap()->getLogChannelData());
|
||||
$log = new logging\RallyLog('rallySet', $this->getMap()->getLogChannelData());
|
||||
|
||||
// Slack poke -----------------------------------------------------------------------------
|
||||
$slackChannelKey = 'slackChannelRally';
|
||||
|
||||
@@ -109,7 +109,7 @@ class CategoryModel extends BasicUniverseModel {
|
||||
* @param array $additionalOptions
|
||||
*/
|
||||
protected function loadData(int $id, string $accessToken = '', array $additionalOptions = []){
|
||||
$data = self::getF3()->ccpClient->getUniverseCategoryData($id);
|
||||
$data = self::getF3()->ccpClient()->getUniverseCategoryData($id);
|
||||
if(!empty($data)){
|
||||
$this->copyfrom($data, ['id', 'name', 'published']);
|
||||
$this->save();
|
||||
@@ -125,7 +125,7 @@ class CategoryModel extends BasicUniverseModel {
|
||||
public function loadGroupsData(int $offset = 0, int $length = 0) : array {
|
||||
$groupIds = [];
|
||||
if( !$this->dry() ){
|
||||
$data = self::getF3()->ccpClient->getUniverseCategoryData($this->_id);
|
||||
$data = self::getF3()->ccpClient()->getUniverseCategoryData($this->_id);
|
||||
if(!empty($data)){
|
||||
array_multisort($data['groups'], SORT_ASC, SORT_NUMERIC);
|
||||
if($length){
|
||||
|
||||
@@ -86,7 +86,7 @@ class ConstellationModel extends BasicUniverseModel {
|
||||
* @param array $additionalOptions
|
||||
*/
|
||||
protected function loadData(int $id, string $accessToken = '', array $additionalOptions = []){
|
||||
$data = self::getF3()->ccpClient->getUniverseConstellationData($id);
|
||||
$data = self::getF3()->ccpClient()->getUniverseConstellationData($id);
|
||||
if(!empty($data)){
|
||||
/**
|
||||
* @var $region RegionModel
|
||||
@@ -105,7 +105,7 @@ class ConstellationModel extends BasicUniverseModel {
|
||||
*/
|
||||
public function loadSystemsData(){
|
||||
if( !$this->dry() ){
|
||||
$data = self::getF3()->ccpClient->getUniverseConstellationData($this->_id);
|
||||
$data = self::getF3()->ccpClient()->getUniverseConstellationData($this->_id);
|
||||
if(!empty($data)){
|
||||
foreach((array)$data['systems'] as $systemId){
|
||||
/**
|
||||
|
||||
@@ -111,7 +111,7 @@ class GroupModel extends BasicUniverseModel {
|
||||
* @param array $additionalOptions
|
||||
*/
|
||||
protected function loadData(int $id, string $accessToken = '', array $additionalOptions = []){
|
||||
$data = self::getF3()->ccpClient->getUniverseGroupData($id);
|
||||
$data = self::getF3()->ccpClient()->getUniverseGroupData($id);
|
||||
if(!empty($data)){
|
||||
/**
|
||||
* @var $category CategoryModel
|
||||
@@ -132,7 +132,7 @@ class GroupModel extends BasicUniverseModel {
|
||||
public function loadTypesData(){
|
||||
$count = 0;
|
||||
if( !$this->dry() ){
|
||||
$data = self::getF3()->ccpClient->getUniverseGroupData($this->_id);
|
||||
$data = self::getF3()->ccpClient()->getUniverseGroupData($this->_id);
|
||||
if(!empty($data)){
|
||||
foreach((array)$data['types'] as $typeId){
|
||||
/**
|
||||
|
||||
@@ -96,7 +96,7 @@ class PlanetModel extends BasicUniverseModel {
|
||||
* @param array $additionalOptions
|
||||
*/
|
||||
protected function loadData(int $id, string $accessToken = '', array $additionalOptions = []){
|
||||
$data = self::getF3()->ccpClient->getUniversePlanetData($id);
|
||||
$data = self::getF3()->ccpClient()->getUniversePlanetData($id);
|
||||
if(!empty($data)){
|
||||
/**
|
||||
* @var $system SystemModel
|
||||
|
||||
@@ -46,7 +46,7 @@ class RegionModel extends BasicUniverseModel {
|
||||
* @param array $additionalOptions
|
||||
*/
|
||||
protected function loadData(int $id, string $accessToken = '', array $additionalOptions = []){
|
||||
$data = self::getF3()->ccpClient->getUniverseRegionData($id);
|
||||
$data = self::getF3()->ccpClient()->getUniverseRegionData($id);
|
||||
if(!empty($data)){
|
||||
$this->copyfrom($data, ['id', 'name', 'description']);
|
||||
$this->save();
|
||||
@@ -58,7 +58,7 @@ class RegionModel extends BasicUniverseModel {
|
||||
*/
|
||||
public function loadConstellationsData(){
|
||||
if( !$this->dry() ){
|
||||
$data = self::getF3()->ccpClient->getUniverseRegionData($this->_id);
|
||||
$data = self::getF3()->ccpClient()->getUniverseRegionData($this->_id);
|
||||
if(!empty($data)){
|
||||
foreach((array)$data['constellations'] as $constellationsId){
|
||||
/**
|
||||
|
||||
@@ -104,7 +104,7 @@ class StargateModel extends BasicUniverseModel {
|
||||
* @param array $additionalOptions
|
||||
*/
|
||||
protected function loadData(int $id, string $accessToken = '', array $additionalOptions = []){
|
||||
$data = self::getF3()->ccpClient->getUniverseStargateData($id);
|
||||
$data = self::getF3()->ccpClient()->getUniverseStargateData($id);
|
||||
|
||||
if(!empty($data)){
|
||||
|
||||
|
||||
@@ -80,7 +80,7 @@ class StarModel extends BasicUniverseModel {
|
||||
* @param array $additionalOptions
|
||||
*/
|
||||
protected function loadData(int $id, string $accessToken = '', array $additionalOptions = []){
|
||||
$data = self::getF3()->ccpClient->getUniverseStarData($id);
|
||||
$data = self::getF3()->ccpClient()->getUniverseStarData($id);
|
||||
if(!empty($data)){
|
||||
/**
|
||||
* @var $type TypeModel
|
||||
|
||||
@@ -77,7 +77,7 @@ class StructureModel extends BasicUniverseModel {
|
||||
* @param array $additionalOptions
|
||||
*/
|
||||
protected function loadData(int $id, string $accessToken = '', array $additionalOptions = []){
|
||||
$data = self::getF3()->ccpClient->getUniverseStructureData($id, $accessToken, $additionalOptions);
|
||||
$data = self::getF3()->ccpClient()->getUniverseStructureData($id, $accessToken);
|
||||
if(!empty($data)){
|
||||
/**
|
||||
* @var $type TypeModel
|
||||
|
||||
@@ -333,7 +333,7 @@ class SystemModel extends BasicUniverseModel {
|
||||
* @param array $additionalOptions
|
||||
*/
|
||||
protected function loadData(int $id, string $accessToken = '', array $additionalOptions = []){
|
||||
$data = self::getF3()->ccpClient->getUniverseSystemData($id);
|
||||
$data = self::getF3()->ccpClient()->getUniverseSystemData($id);
|
||||
|
||||
if(!empty($data)){
|
||||
/**
|
||||
@@ -363,7 +363,7 @@ class SystemModel extends BasicUniverseModel {
|
||||
*/
|
||||
public function loadPlanetsData(){
|
||||
if( !$this->dry() ){
|
||||
$data = self::getF3()->ccpClient->getUniverseSystemData($this->_id);
|
||||
$data = self::getF3()->ccpClient()->getUniverseSystemData($this->_id);
|
||||
if($data['planets']){
|
||||
// planets are optional since ESI v4 (e.g. Abyssal systems)
|
||||
foreach((array)$data['planets'] as $planetData){
|
||||
@@ -384,7 +384,7 @@ class SystemModel extends BasicUniverseModel {
|
||||
*/
|
||||
public function loadStargatesData(){
|
||||
if( !$this->dry() ){
|
||||
$data = self::getF3()->ccpClient->getUniverseSystemData($this->_id);
|
||||
$data = self::getF3()->ccpClient()->getUniverseSystemData($this->_id);
|
||||
if(!empty($data)){
|
||||
foreach((array)$data['stargates'] as $stargateId){
|
||||
/**
|
||||
|
||||
@@ -136,7 +136,7 @@ class TypeModel extends BasicUniverseModel {
|
||||
* @param array $additionalOptions
|
||||
*/
|
||||
protected function loadData(int $id, string $accessToken = '', array $additionalOptions = []){
|
||||
$data = self::getF3()->ccpClient->getUniverseTypesData($id, $additionalOptions);
|
||||
$data = self::getF3()->ccpClient()->getUniverseTypesData($id);
|
||||
if(!empty($data)){
|
||||
$group = $this->rel('groupId');
|
||||
$group->loadById($data['groupId'], $accessToken, $additionalOptions);
|
||||
|
||||
@@ -13,8 +13,8 @@ NAME = Pathfinder
|
||||
; Version is used for CSS/JS cache busting and is part of the URL for static resources:
|
||||
; e.g. public/js/vX.X.X/app.js
|
||||
; Syntax: String (current version)
|
||||
; Default: v1.4.4
|
||||
VERSION = v1.4.4
|
||||
; Default: v1.5.0
|
||||
VERSION = v1.5.0
|
||||
|
||||
; Contact information [optional]
|
||||
; Shown on 'licence', 'contact' page.
|
||||
@@ -229,6 +229,7 @@ RALLY_SET =
|
||||
; =================================================================================================
|
||||
[PATHFINDER.TIMER]
|
||||
; Login time for characters. Users get logged out after X minutes
|
||||
; Hint: Set to 0 disables login time and characters stay logged in until Cookie data expires
|
||||
; Syntax: Integer (minutes)
|
||||
; Default: 480
|
||||
LOGGED = 480
|
||||
|
||||
@@ -9,7 +9,7 @@ APACHE.VERSION = 2.5
|
||||
NGINX.VERSION = 1.9
|
||||
|
||||
[REQUIREMENTS.PHP]
|
||||
VERSION = 7.0
|
||||
VERSION = 7.1
|
||||
|
||||
; 64-bit version of PHP (4 = 32-bit, 8 = 64-bit)
|
||||
PHP_INT_SIZE = 8
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
"url": "../pathfinder_esi"
|
||||
}],
|
||||
"require": {
|
||||
"php-64bit": ">=7.0",
|
||||
"php-64bit": ">=7.1",
|
||||
"ext-pdo": "*",
|
||||
"ext-openssl": "*",
|
||||
"ext-curl": "*",
|
||||
@@ -33,6 +33,14 @@
|
||||
"websoftwares/monolog-zmq-handler": "0.2.*",
|
||||
"swiftmailer/swiftmailer": "^6.0",
|
||||
"league/html-to-markdown": "4.8.*",
|
||||
"cache/redis-adapter": "1.0.*",
|
||||
"cache/filesystem-adapter": "1.0.*",
|
||||
"cache/array-adapter": "1.0.*",
|
||||
"cache/void-adapter": "1.0.*",
|
||||
"cache/namespaced-cache": "1.0.*",
|
||||
"exodus4d/pathfinder_esi": "dev-develop as 0.0.x-dev"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-redis": "Redis can be used as cache backend."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
"url": "https://github.com/exodus4d/pathfinder_esi"
|
||||
}],
|
||||
"require": {
|
||||
"php-64bit": ">=7.0",
|
||||
"php-64bit": ">=7.1",
|
||||
"ext-pdo": "*",
|
||||
"ext-openssl": "*",
|
||||
"ext-curl": "*",
|
||||
@@ -33,6 +33,14 @@
|
||||
"websoftwares/monolog-zmq-handler": "0.2.*",
|
||||
"swiftmailer/swiftmailer": "^6.0",
|
||||
"league/html-to-markdown": "4.8.*",
|
||||
"exodus4d/pathfinder_esi": "dev-master#v1.2.5"
|
||||
"cache/redis-adapter": "1.0.*",
|
||||
"cache/filesystem-adapter": "1.0.*",
|
||||
"cache/array-adapter": "1.0.*",
|
||||
"cache/void-adapter": "1.0.*",
|
||||
"cache/namespaced-cache": "1.0.*",
|
||||
"exodus4d/pathfinder_esi": "dev-master#v1.3.0"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-redis": "Redis can be used as cache backend."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,9 +14,6 @@ $f3->config('app/config.ini', true);
|
||||
// load environment dependent config
|
||||
lib\Config::instance($f3);
|
||||
|
||||
// initiate CCP API Client (ESI)
|
||||
lib\CcpClient::instance($f3);
|
||||
|
||||
// initiate cron-jobs
|
||||
Cron::instance();
|
||||
|
||||
|
||||
@@ -486,6 +486,36 @@ define(['jquery'], ($) => {
|
||||
7: 'Q003 - 0.0',
|
||||
8: 'A009 - C13'
|
||||
},
|
||||
10: { // High Sec
|
||||
1: 'E004 - C1',
|
||||
2: 'L005 - C2',
|
||||
3: 'Z006 - C3',
|
||||
4: 'M001 - C4',
|
||||
5: 'C008 - C5',
|
||||
6: 'G008 - C6',
|
||||
7: 'Q003 - 0.0',
|
||||
8: 'A009 - C13'
|
||||
},
|
||||
11: { // Low Sec
|
||||
1: 'E004 - C1',
|
||||
2: 'L005 - C2',
|
||||
3: 'Z006 - C3',
|
||||
4: 'M001 - C4',
|
||||
5: 'C008 - C5',
|
||||
6: 'G008 - C6',
|
||||
7: 'Q003 - 0.0',
|
||||
8: 'A009 - C13'
|
||||
},
|
||||
12: { // 0.0
|
||||
1: 'E004 - C1',
|
||||
2: 'L005 - C2',
|
||||
3: 'Z006 - C3',
|
||||
4: 'M001 - C4',
|
||||
5: 'C008 - C5',
|
||||
6: 'G008 - C6',
|
||||
7: 'Q003 - 0.0',
|
||||
8: 'A009 - C13'
|
||||
},
|
||||
13: { // Shattered Wormholes (some of them are static)
|
||||
1: 'E004 - C1',
|
||||
2: 'L005 - C2',
|
||||
|
||||
@@ -17,7 +17,8 @@ define([
|
||||
'dialog/notification',
|
||||
'dialog/manual',
|
||||
'dialog/changelog',
|
||||
'dialog/credit'
|
||||
'dialog/credit',
|
||||
'dialog/api_status',
|
||||
], ($, Init, Util, Render, Gallery, bootbox) => {
|
||||
|
||||
'use strict';
|
||||
@@ -70,6 +71,8 @@ define([
|
||||
stickyPanelServerId: 'pf-landing-server-panel', // id for EVE Online server status panel
|
||||
stickyPanelAdminId: 'pf-landing-admin-panel', // id for admin login panel
|
||||
|
||||
apiStatusTriggerClass: 'pf-api-status-trigger', // class for "api status" dialog trigger elements
|
||||
|
||||
// animation
|
||||
animateElementClass: 'pf-animate-on-visible', // class for elements that will be animated to show
|
||||
|
||||
@@ -463,30 +466,40 @@ define([
|
||||
dataType: 'json'
|
||||
}).done(function(responseData, textStatus, request){
|
||||
|
||||
if(responseData.hasOwnProperty('status')){
|
||||
let data = responseData.status;
|
||||
data.stickyPanelServerId = config.stickyPanelServerId;
|
||||
data.stickyPanelClass = config.stickyPanelClass;
|
||||
|
||||
let statusClass = '';
|
||||
switch(data.serviceStatus.toLowerCase()){
|
||||
case 'online': statusClass = 'txt-color-green'; break;
|
||||
case 'vip': statusClass = 'txt-color-orange'; break;
|
||||
case 'offline': statusClass = 'txt-color-redDarker'; break;
|
||||
let data = {
|
||||
stickyPanelServerId: config.stickyPanelServerId,
|
||||
stickyPanelClass: config.stickyPanelClass,
|
||||
apiStatusTriggerClass: config.apiStatusTriggerClass,
|
||||
server: responseData.server,
|
||||
api: responseData.api,
|
||||
statusFormat: () => {
|
||||
return (val, render) => {
|
||||
switch(render(val)){
|
||||
case 'online':
|
||||
case 'green': return 'txt-color-green';
|
||||
case 'vip':
|
||||
case 'yellow': return 'txt-color-orange';
|
||||
case 'offline':
|
||||
case 'red': return 'txt-color-red';
|
||||
default: return '';
|
||||
}
|
||||
};
|
||||
}
|
||||
data.serviceStatus = {
|
||||
eve: data.serviceStatus,
|
||||
style: statusClass
|
||||
};
|
||||
};
|
||||
|
||||
requirejs(['text!templates/ui/server_panel.html', 'mustache'], function(template, Mustache){
|
||||
let content = Mustache.render(template, data);
|
||||
$('#' + config.headerId).prepend(content);
|
||||
$('#' + config.stickyPanelServerId).velocity('transition.slideLeftBigIn', {
|
||||
duration: 240
|
||||
});
|
||||
requirejs(['text!templates/ui/server_panel.html', 'mustache'], function(template, Mustache){
|
||||
let content = Mustache.render(template, data);
|
||||
$('#' + config.headerId).prepend(content);
|
||||
let stickyPanelServer = $('#' + config.stickyPanelServerId);
|
||||
stickyPanelServer.velocity('transition.slideLeftBigIn', {
|
||||
duration: 240
|
||||
});
|
||||
}
|
||||
|
||||
// set observer for api status dialog
|
||||
stickyPanelServer.on('click', '.' + config.apiStatusTriggerClass, function(){
|
||||
$.fn.apiStatusDialog(data.api);
|
||||
});
|
||||
});
|
||||
|
||||
}).fail(handleAjaxErrorResponse);
|
||||
};
|
||||
|
||||
76
js/app/ui/dialog/api_status.js
Normal file
76
js/app/ui/dialog/api_status.js
Normal file
@@ -0,0 +1,76 @@
|
||||
/**
|
||||
* changelog dialog (GitHub API repository information)
|
||||
*/
|
||||
|
||||
define([
|
||||
'jquery',
|
||||
'app/init',
|
||||
'app/util',
|
||||
'app/render',
|
||||
'bootbox'
|
||||
], ($, Init, Util, Render, bootbox) => {
|
||||
'use strict';
|
||||
|
||||
let config = {
|
||||
apiStatusDialogClass: 'pf-api-status-dialog' // class for "api status" dialog
|
||||
};
|
||||
|
||||
/**
|
||||
* show api status dialog
|
||||
* @param apiData
|
||||
*/
|
||||
$.fn.apiStatusDialog = function(apiData){
|
||||
|
||||
let data = {
|
||||
apiData: apiData,
|
||||
methodFormat: () => {
|
||||
return (val, render) => {
|
||||
switch(render(val)){
|
||||
case 'get': return 'txt-color-blue';
|
||||
case 'post': return 'txt-color-green';
|
||||
case 'put': return 'txt-color-yellow';
|
||||
case 'delete': return 'txt-color-red';
|
||||
default: return '';
|
||||
}
|
||||
};
|
||||
},
|
||||
statusTitle: () => {
|
||||
return (val, render) => {
|
||||
switch(render(val)){
|
||||
case 'green': return 'ok';
|
||||
case 'yellow': return 'degraded: Slow or potentially dropping requests';
|
||||
case 'red': return 'bad: Most requests are not succeeding and/or are very slow (5s+) on average';
|
||||
default: return 'unknown';
|
||||
}
|
||||
};
|
||||
},
|
||||
secondsFormat: () => {
|
||||
return (val, render) => {
|
||||
return parseFloat(render(val)).toFixed(2) + 's';
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
requirejs(['text!templates/dialog/api_status.html', 'mustache'], (template, Mustache) => {
|
||||
let apiStatusDialog = bootbox.dialog({
|
||||
className: config.apiStatusDialogClass,
|
||||
title: 'API status',
|
||||
message: Mustache.render(template, data),
|
||||
show: false,
|
||||
buttons: {
|
||||
close: {
|
||||
label: 'cancel',
|
||||
className: 'btn-default'
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
apiStatusDialog.initTooltips();
|
||||
|
||||
// show dialog
|
||||
apiStatusDialog.modal('show');
|
||||
|
||||
});
|
||||
};
|
||||
|
||||
});
|
||||
@@ -88,7 +88,7 @@ define([
|
||||
let data = {
|
||||
isFirst: (i === 0),
|
||||
isOdd: (i % 2 !== 0),
|
||||
releaseDate: releaseData.published_at.substr(0, 10),
|
||||
releaseDate: releaseData.publishedAt.substr(0, 10),
|
||||
releaseData: releaseData
|
||||
};
|
||||
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
7
public/css/v1.5.0/pathfinder.css.map
Normal file
7
public/css/v1.5.0/pathfinder.css.map
Normal file
File diff suppressed because one or more lines are too long
@@ -486,6 +486,36 @@ define(['jquery'], ($) => {
|
||||
7: 'Q003 - 0.0',
|
||||
8: 'A009 - C13'
|
||||
},
|
||||
10: { // High Sec
|
||||
1: 'E004 - C1',
|
||||
2: 'L005 - C2',
|
||||
3: 'Z006 - C3',
|
||||
4: 'M001 - C4',
|
||||
5: 'C008 - C5',
|
||||
6: 'G008 - C6',
|
||||
7: 'Q003 - 0.0',
|
||||
8: 'A009 - C13'
|
||||
},
|
||||
11: { // Low Sec
|
||||
1: 'E004 - C1',
|
||||
2: 'L005 - C2',
|
||||
3: 'Z006 - C3',
|
||||
4: 'M001 - C4',
|
||||
5: 'C008 - C5',
|
||||
6: 'G008 - C6',
|
||||
7: 'Q003 - 0.0',
|
||||
8: 'A009 - C13'
|
||||
},
|
||||
12: { // 0.0
|
||||
1: 'E004 - C1',
|
||||
2: 'L005 - C2',
|
||||
3: 'Z006 - C3',
|
||||
4: 'M001 - C4',
|
||||
5: 'C008 - C5',
|
||||
6: 'G008 - C6',
|
||||
7: 'Q003 - 0.0',
|
||||
8: 'A009 - C13'
|
||||
},
|
||||
13: { // Shattered Wormholes (some of them are static)
|
||||
1: 'E004 - C1',
|
||||
2: 'L005 - C2',
|
||||
@@ -17,7 +17,8 @@ define([
|
||||
'dialog/notification',
|
||||
'dialog/manual',
|
||||
'dialog/changelog',
|
||||
'dialog/credit'
|
||||
'dialog/credit',
|
||||
'dialog/api_status',
|
||||
], ($, Init, Util, Render, Gallery, bootbox) => {
|
||||
|
||||
'use strict';
|
||||
@@ -70,6 +71,8 @@ define([
|
||||
stickyPanelServerId: 'pf-landing-server-panel', // id for EVE Online server status panel
|
||||
stickyPanelAdminId: 'pf-landing-admin-panel', // id for admin login panel
|
||||
|
||||
apiStatusTriggerClass: 'pf-api-status-trigger', // class for "api status" dialog trigger elements
|
||||
|
||||
// animation
|
||||
animateElementClass: 'pf-animate-on-visible', // class for elements that will be animated to show
|
||||
|
||||
@@ -463,30 +466,40 @@ define([
|
||||
dataType: 'json'
|
||||
}).done(function(responseData, textStatus, request){
|
||||
|
||||
if(responseData.hasOwnProperty('status')){
|
||||
let data = responseData.status;
|
||||
data.stickyPanelServerId = config.stickyPanelServerId;
|
||||
data.stickyPanelClass = config.stickyPanelClass;
|
||||
|
||||
let statusClass = '';
|
||||
switch(data.serviceStatus.toLowerCase()){
|
||||
case 'online': statusClass = 'txt-color-green'; break;
|
||||
case 'vip': statusClass = 'txt-color-orange'; break;
|
||||
case 'offline': statusClass = 'txt-color-redDarker'; break;
|
||||
let data = {
|
||||
stickyPanelServerId: config.stickyPanelServerId,
|
||||
stickyPanelClass: config.stickyPanelClass,
|
||||
apiStatusTriggerClass: config.apiStatusTriggerClass,
|
||||
server: responseData.server,
|
||||
api: responseData.api,
|
||||
statusFormat: () => {
|
||||
return (val, render) => {
|
||||
switch(render(val)){
|
||||
case 'online':
|
||||
case 'green': return 'txt-color-green';
|
||||
case 'vip':
|
||||
case 'yellow': return 'txt-color-orange';
|
||||
case 'offline':
|
||||
case 'red': return 'txt-color-red';
|
||||
default: return '';
|
||||
}
|
||||
};
|
||||
}
|
||||
data.serviceStatus = {
|
||||
eve: data.serviceStatus,
|
||||
style: statusClass
|
||||
};
|
||||
};
|
||||
|
||||
requirejs(['text!templates/ui/server_panel.html', 'mustache'], function(template, Mustache){
|
||||
let content = Mustache.render(template, data);
|
||||
$('#' + config.headerId).prepend(content);
|
||||
$('#' + config.stickyPanelServerId).velocity('transition.slideLeftBigIn', {
|
||||
duration: 240
|
||||
});
|
||||
requirejs(['text!templates/ui/server_panel.html', 'mustache'], function(template, Mustache){
|
||||
let content = Mustache.render(template, data);
|
||||
$('#' + config.headerId).prepend(content);
|
||||
let stickyPanelServer = $('#' + config.stickyPanelServerId);
|
||||
stickyPanelServer.velocity('transition.slideLeftBigIn', {
|
||||
duration: 240
|
||||
});
|
||||
}
|
||||
|
||||
// set observer for api status dialog
|
||||
stickyPanelServer.on('click', '.' + config.apiStatusTriggerClass, function(){
|
||||
$.fn.apiStatusDialog(data.api);
|
||||
});
|
||||
});
|
||||
|
||||
}).fail(handleAjaxErrorResponse);
|
||||
};
|
||||
76
public/js/v1.5.0/app/ui/dialog/api_status.js
Normal file
76
public/js/v1.5.0/app/ui/dialog/api_status.js
Normal file
@@ -0,0 +1,76 @@
|
||||
/**
|
||||
* changelog dialog (GitHub API repository information)
|
||||
*/
|
||||
|
||||
define([
|
||||
'jquery',
|
||||
'app/init',
|
||||
'app/util',
|
||||
'app/render',
|
||||
'bootbox'
|
||||
], ($, Init, Util, Render, bootbox) => {
|
||||
'use strict';
|
||||
|
||||
let config = {
|
||||
apiStatusDialogClass: 'pf-api-status-dialog' // class for "api status" dialog
|
||||
};
|
||||
|
||||
/**
|
||||
* show api status dialog
|
||||
* @param apiData
|
||||
*/
|
||||
$.fn.apiStatusDialog = function(apiData){
|
||||
|
||||
let data = {
|
||||
apiData: apiData,
|
||||
methodFormat: () => {
|
||||
return (val, render) => {
|
||||
switch(render(val)){
|
||||
case 'get': return 'txt-color-blue';
|
||||
case 'post': return 'txt-color-green';
|
||||
case 'put': return 'txt-color-yellow';
|
||||
case 'delete': return 'txt-color-red';
|
||||
default: return '';
|
||||
}
|
||||
};
|
||||
},
|
||||
statusTitle: () => {
|
||||
return (val, render) => {
|
||||
switch(render(val)){
|
||||
case 'green': return 'ok';
|
||||
case 'yellow': return 'degraded: Slow or potentially dropping requests';
|
||||
case 'red': return 'bad: Most requests are not succeeding and/or are very slow (5s+) on average';
|
||||
default: return 'unknown';
|
||||
}
|
||||
};
|
||||
},
|
||||
secondsFormat: () => {
|
||||
return (val, render) => {
|
||||
return parseFloat(render(val)).toFixed(2) + 's';
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
requirejs(['text!templates/dialog/api_status.html', 'mustache'], (template, Mustache) => {
|
||||
let apiStatusDialog = bootbox.dialog({
|
||||
className: config.apiStatusDialogClass,
|
||||
title: 'API status',
|
||||
message: Mustache.render(template, data),
|
||||
show: false,
|
||||
buttons: {
|
||||
close: {
|
||||
label: 'cancel',
|
||||
className: 'btn-default'
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
apiStatusDialog.initTooltips();
|
||||
|
||||
// show dialog
|
||||
apiStatusDialog.modal('show');
|
||||
|
||||
});
|
||||
};
|
||||
|
||||
});
|
||||
@@ -88,7 +88,7 @@ define([
|
||||
let data = {
|
||||
isFirst: (i === 0),
|
||||
isOdd: (i % 2 !== 0),
|
||||
releaseDate: releaseData.published_at.substr(0, 10),
|
||||
releaseDate: releaseData.publishedAt.substr(0, 10),
|
||||
releaseData: releaseData
|
||||
};
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user