Implemented Tamer & Cache Drivers (WIP)

This commit is contained in:
Netkas 2023-06-18 21:12:42 -04:00
parent 26f0f31cc6
commit d346c4d23d
No known key found for this signature in database
GPG key ID: 5DAF58535614062B
39 changed files with 2211 additions and 913 deletions

View file

@ -4,146 +4,220 @@
namespace FederationCLI;
use Exception;
use FederationLib\Classes\Configuration;
use FederationLib\Enums\Standard\Methods;
use FederationLib\Exceptions\DatabaseException;
use FederationLib\Exceptions\Standard\AccessDeniedException;
use FederationLib\Exceptions\Standard\ClientNotFoundException;
use FederationLib\Exceptions\Standard\InternalServerException;
use FederationLib\Exceptions\Standard\InvalidClientDescriptionException;
use FederationLib\Exceptions\Standard\InvalidClientNameException;
use FederationLib\FederationLib;
use JsonException;
use OptsLib\Parse;
use TamerLib\Enums\TamerMode;
use TamerLib\Exceptions\ServerException;
use TamerLib\Exceptions\WorkerFailedException;
use TamerLib\tm;
class InteractiveMode
{
/**
* The current menu the user is in
*
* @var string
* @var FederationLib
*/
private static $current_menu = 'Main';
/**
* An array of menu pointers to functions
*
* @var string[]
*/
private static $menu_pointers = [
'ClientManager' => 'FederationCLI\InteractiveMode\ClientManager::processCommand',
'ConfigManager' => 'FederationCLI\InteractiveMode\ConfigurationManager::processCommand'
];
private static $help_pointers =[
'ClientManager' => 'FederationCLI\InteractiveMode\ClientManager::help',
'ConfigManager' => 'FederationCLI\InteractiveMode\ConfigurationManager::help'
];
/**
* @var FederationLib|null
*/
private static $federation_lib = null;
private static $federation_lib;
/**
* Main entry point for the interactive mode
*
* @param array $args
* @return void
* @throws ServerException
* @throws WorkerFailedException
*/
public static function main(array $args=[]): void
{
tm::initialize(TamerMode::CLIENT, Configuration::getTamerLibConfiguration()->getServerConfiguration());
tm::createWorker(Configuration::getTamerLibConfiguration()->getCliWorkers(), FederationLib::getSubprocessorPath());
self::$federation_lib = new FederationLib();
while(true)
{
print(sprintf('federation@%s:~$ ', self::$current_menu));
print(sprintf('%s@%s:~$ ', 'root', Configuration::getHostName()));
$input = trim(fgets(STDIN));
self::processCommand($input);
try
{
switch(strtolower(explode(' ', $input)[0]))
{
case Methods::PING:
self::ping();
break;
case Methods::WHOAMI:
self::whoami();
break;
case Methods::CREATE_CLIENT:
self::createClient($input);
break;
case Methods::GET_CLIENT:
self::getClient($input);
break;
case Methods::CHANGE_CLIENT_NAME:
self::changeClientName($input);
break;
default:
print(sprintf('Command \'%s\' not found' . PHP_EOL, $input));
break;
}
}
catch(Exception $e)
{
print(sprintf('Error: %s' . PHP_EOL, $e->getMessage()));
}
}
}
/**
* Processes a command from the user
* Invokes the ping method
*
* @return void
* @throws AccessDeniedException
* @throws ClientNotFoundException
* @throws InternalServerException
*/
private static function ping(): void
{
if(self::$federation_lib->ping(null))
{
print('OK' . PHP_EOL);
return;
}
print('ERROR' . PHP_EOL);
}
/**
* Invokes the whoami method and prints the result
*
* @return void
* @throws AccessDeniedException
* @throws ClientNotFoundException
* @throws InternalServerException
*/
private static function whoami(): void
{
print(self::$federation_lib->whoami(null) . PHP_EOL);
}
/**
* @param string $input
* @return void
* @throws AccessDeniedException
* @throws ClientNotFoundException
* @throws DatabaseException
* @throws InternalServerException
* @throws InvalidClientDescriptionException
* @throws InvalidClientNameException
*/
private static function createClient(string $input): void
{
$parsed_arguments = Parse::parseArgument($input);
$name = $parsed_arguments['name'] ?? $parsed_arguments['n'] ?? null;
$description = $parsed_arguments['description'] ?? $parsed_arguments['d'] ?? null;
print(self::$federation_lib->createClient(null, $name, $description) . PHP_EOL);
}
/**
* @param string $input
* @return void
* @throws AccessDeniedException
* @throws DatabaseException
* @throws InternalServerException
* @throws JsonException
* @noinspection PhpMultipleClassDeclarationsInspection
*/
private static function getClient(string $input): void
{
$parsed_arguments = Parse::parseArgument($input);
$uuid = $parsed_arguments['uuid'] ?? $parsed_arguments['u'] ?? null;
$raw = $parsed_arguments['raw'] ?? $parsed_arguments['r'] ?? false;
if($uuid === null | $uuid === '')
{
print('Missing required argument \'uuid\'' . PHP_EOL);
return;
}
try
{
$client = self::$federation_lib->getClient(null, $uuid);
if($raw)
{
print(json_encode($client->toArray(), JSON_THROW_ON_ERROR | JSON_PRETTY_PRINT) . PHP_EOL);
return;
}
Utilities::printData('CLIENT LOOKUP RESULTS', [
'UUID' => $client->getUuid(),
'NAME' => $client->getName(),
'DESCRIPTION' => $client->getDescription() ?? 'N/A',
'PERMISSION_ROLE' => $client->getPermissionRole(),
'CREATED_AT' => date('Y-m-d H:i:s', $client->getCreatedTimestamp()),
'UPDATED_AT' => date('Y-m-d H:i:s', $client->getUpdatedTimestamp()),
'SEEN_AT' => date('Y-m-d H:i:s', $client->getSeenTimestamp())
]);
}
catch(ClientNotFoundException)
{
print(sprintf('Client with UUID \'%s\' not found' . PHP_EOL, $uuid));
}
}
/**
* Changes the name of a client with the given UUID
*
* @param string $input
* @return void
* @throws AccessDeniedException
* @throws DatabaseException
* @throws InternalServerException
* @throws InvalidClientNameException
*/
private static function processCommand(string $input): void
private static function changeClientName(string $input): void
{
$parsed_input = Utilities::parseShellInput($input);
$parsed_arguments = Parse::parseArgument($input);
switch(strtolower($parsed_input['command']))
$uuid = $parsed_arguments['uuid'] ?? $parsed_arguments['u'] ?? null;
$name = $parsed_arguments['name'] ?? $parsed_arguments['n'] ?? null;
if($uuid === null | $uuid === '')
{
case 'help':
print('Available commands:' . PHP_EOL);
print(' help - displays the help menu for the current menu and global commands' . PHP_EOL);
print(' client_manager - enter client manager mode' . PHP_EOL);
print(' config_manager - enter config manager mode' . PHP_EOL);
print(' clear - clears the screen' . PHP_EOL);
print(' exit - exits the current menu, if on the main menu, exits the program' . PHP_EOL);
if(isset(self::$help_pointers[self::$current_menu]))
{
call_user_func(self::$help_pointers[self::$current_menu]);
}
break;
case 'client_manager':
self::$current_menu = 'ClientManager';
break;
case 'config_manager':
self::$current_menu = 'ConfigManager';
break;
case 'clear':
print(chr(27) . "[2J" . chr(27) . "[;H");
break;
case 'exit':
if(self::$current_menu != 'Main')
{
self::$current_menu = 'Main';
break;
}
exit(0);
default:
if(!isset(self::$menu_pointers[self::$current_menu]))
{
print(sprintf('Unknown command: %s', $parsed_input['command']) . PHP_EOL);
break;
}
call_user_func(self::$menu_pointers[self::$current_menu], $input);
break;
}
}
/**
* Returns the current menu
*
* @return string
*/
public static function getCurrentMenu(): string
{
return self::$current_menu;
}
/**
* Sets the current menu to the specified value
*
* @param string $current_menu
*/
public static function setCurrentMenu(string $current_menu): void
{
self::$current_menu = $current_menu;
}
/**
* Returns the FederationLib instance
*
* @return FederationLib
*/
public static function getFederationLib(): FederationLib
{
if(self::$federation_lib == null)
{
self::$federation_lib = new FederationLib();
print('Missing required argument \'uuid\'' . PHP_EOL);
return;
}
return self::$federation_lib;
if($name === null | $name === '')
{
print('Missing required argument \'name\'' . PHP_EOL);
return;
}
try
{
self::$federation_lib->changeClientName(null, $uuid, $name);
print('OK' . PHP_EOL);
}
catch(ClientNotFoundException)
{
print(sprintf('Client with UUID \'%s\' not found' . PHP_EOL, $uuid));
}
}
}

View file

@ -1,190 +0,0 @@
<?php
namespace FederationCLI\InteractiveMode;
use AsciiTable\Builder;
use Exception;
use FederationCLI\InteractiveMode;
use FederationCLI\Utilities;
use FederationLib\Enums\FilterOrder;
use FederationLib\Enums\Filters\ListClientsFilter;
use FederationLib\Exceptions\DatabaseException;
use FederationLib\Objects\Client;
class ClientManager
{
/**
* @param string $input
* @return void
*/
public static function processCommand(string $input): void
{
$parsed_input = Utilities::parseShellInput($input);
try
{
switch(strtolower($parsed_input['command']))
{
case 'register':
self::registerClient();
break;
case 'list':
// list [optional: page] [optional: filter] [optional: order] [optional: max_items]
self::listClients($parsed_input['args']);
break;
case 'total':
self::totalClients();
break;
case 'total_pages':
self::totalPages($parsed_input['args']);
break;
case 'get':
self::getClient($parsed_input['args']);
break;
default:
print(sprintf('Unknown command: %s', $parsed_input['command']) . PHP_EOL);
break;
}
}
catch(Exception $e)
{
Utilities::printExceptionStack($e);
}
}
/**
* Displays the help message for the client manager
*
* @return void
*/
public static function help(): void
{
print('Client manager commands:' . PHP_EOL);
print(' register - registers a new client with the federation' . PHP_EOL);
print(' list [optional: page (default 1)] [optional: filter (default created_timestamp)] [optional: order (default asc)] [optional: max_items (default 100)] - lists clients' . PHP_EOL);
print(' total - gets the total number of clients' . PHP_EOL);
print(' total_pages [optional: max_items (default 100)] - gets the total number of pages of clients' . PHP_EOL);
print(' get [client uuid] - gets a client by UUID' . PHP_EOL);
}
/**
* Registers a new client with the federation. prints the UUID of the client if successful.
*
* @return void
*/
private static function registerClient(): void
{
$client = new Client();
$client->setName(Utilities::promptInput('Client name (default: Random): '));
$client->setDescription(Utilities::promptInput('Client description (default: N/A): '));
$client->setQueryPermission((int)Utilities::parseBoolean(Utilities::promptInput('Query permission (default: 1): ')));
$client->setUpdatePermission((int)Utilities::parseBoolean(Utilities::promptInput('Update permission (default: 0): ')));
try
{
$client_uuid = InteractiveMode::getFederationLib()->getClientManager()->registerClient($client);
}
catch(Exception $e)
{
print('Failed to register client: ' . $e->getMessage() . PHP_EOL);
Utilities::printExceptionStack($e);
return;
}
print(sprintf('Client registered successfully, UUID: %s', $client_uuid) . PHP_EOL);
}
/**
* @param array $args
* @return void
* @throws DatabaseException
* @throws \Doctrine\DBAL\Exception
* @throws \RedisException
*/
private static function listClients(array $args): void
{
$page = $args[0] ?? 1;
$filter = $args[1] ?? ListClientsFilter::CREATED_TIMESTAMP;
$order = $args[2] ?? FilterOrder::ASCENDING;
$max_items = $args[3] ?? 100;
$clients = InteractiveMode::getFederationLib()->getClientManager()->listClients($page, $filter, $order, $max_items);
if(count($clients) === 0)
{
print('No clients found' . PHP_EOL);
}
else
{
$builder = new Builder();
foreach($clients as $client)
{
$builder->addRow($client->toArray());
}
$builder->setTitle(sprintf('Clients (page %d, filter %s, order %s, max items %d)', $page, $filter, $order, $max_items));
print($builder->renderTable() . PHP_EOL);
}
}
private static function getClient(mixed $args)
{
$client_uuid = $args[0] ?? null;
if(is_null($client_uuid))
{
print('Client UUID required' . PHP_EOL);
return;
}
try
{
$client = InteractiveMode::getFederationLib()->getClientManager()->getClient($client_uuid);
}
catch(Exception $e)
{
print('Failed to get client: ' . $e->getMessage() . PHP_EOL);
Utilities::printExceptionStack($e);
return;
}
foreach($client->toArray() as $key => $value)
{
print match ($key)
{
'id' => (sprintf(' UUID: %s', $value) . PHP_EOL),
'enabled' => (sprintf(' Enabled: %s', (Utilities::parseBoolean($value) ? 'Yes' : 'No')) . PHP_EOL),
'name' => (sprintf(' Name: %s', $value ?? 'N/A') . PHP_EOL),
'description' => (sprintf(' Description: %s', $value ?? 'N/A') . PHP_EOL),
'secret_totp' => (sprintf(' Secret TOTP: %s', $value ?? 'N/A') . PHP_EOL),
'query_permission' => (sprintf(' Query permission Level: %s', $value) . PHP_EOL),
'update_permission' => (sprintf(' Update permission Level: %s', $value) . PHP_EOL),
'flags' => (sprintf(' Flags: %s', $value) . PHP_EOL),
'created_timestamp' => (sprintf(' Created: %s', date('Y-m-d H:i:s', $value)) . PHP_EOL),
'updated_timestamp' => (sprintf(' Updated: %s', date('Y-m-d H:i:s', $value)) . PHP_EOL),
'seen_timestamp' => (sprintf(' Last seen: %s', date('Y-m-d H:i:s', $value)) . PHP_EOL),
default => (sprintf(' %s: %s', $key, $value) . PHP_EOL),
};
}
}
private static function totalClients()
{
print(sprintf('Total clients: %d', InteractiveMode::getFederationLib()->getClientManager()->getTotalClients()) . PHP_EOL);
}
private static function totalPages(mixed $args)
{
$max_items = $args[0] ?? 100;
print(sprintf('Total pages: %d', InteractiveMode::getFederationLib()->getClientManager()->getTotalPages($max_items)) . PHP_EOL);
}
}

View file

@ -1,113 +0,0 @@
<?php
namespace FederationCLI\InteractiveMode;
use Exception;
use FederationCLI\Utilities;
use FederationLib\Classes\Configuration;
class ConfigurationManager
{
/**
* @param string $input
* @return void
*/
public static function processCommand(string $input): void
{
$parsed_input = Utilities::parseShellInput($input);
switch(strtolower($parsed_input['command']))
{
case 'read':
self::read($parsed_input['args'][0] ?? null);
break;
case 'write':
self::write($parsed_input['args'][0] ?? null, $parsed_input['args'][1] ?? null);
break;
case 'save':
self::save();
break;
default:
print(sprintf('Unknown command: %s', $parsed_input['command']) . PHP_EOL);
break;
}
}
/**
* Displays the help message for the client manager
*
* @return void
*/
public static function help(): void
{
print('Configuration manager commands:' . PHP_EOL);
print(' read - reads the current configuration' . PHP_EOL);
print(' read <key> - reads the value of the specified configuration key' . PHP_EOL);
print(' write <key> <value> - writes the value of the specified configuration key' . PHP_EOL);
print(' save - saves the current configuration to disk' . PHP_EOL);
}
/**
* Reads the current configuration or the value of a specific key
*
* @param string|null $key
* @return void
*/
private static function read(?string $key=null): void
{
if($key === null)
{
$value = Configuration::getConfiguration();
}
else
{
$value = Configuration::getConfigurationObject()->get($key);
}
if(is_null($value))
{
print('No value found for key: ' . $key . PHP_EOL);
}
elseif(is_array($value))
{
print(json_encode($value, JSON_PRETTY_PRINT) . PHP_EOL);
}
else
{
print($value . PHP_EOL);
}
}
/**
* Writes the value of a specific configuration key
*
* @param string $key
* @param string $value
* @return void
*/
private static function write(string $key, string $value): void
{
Configuration::getConfigurationObject()->set($key, $value);
}
/**
* Writes the current configuration to disk
*
* @return void
*/
private static function save(): void
{
try
{
Configuration::getConfigurationObject()->save();
}
catch(Exception $e)
{
print('Failed to save configuration: ' . $e->getMessage() . PHP_EOL);
Utilities::printExceptionStack($e);
}
}
}

View file

@ -98,4 +98,45 @@
return self::parseBoolean($input);
}
/**
* Prints data in a formatted manner
*
* @param $banner_text
* @param $data
* @param int $body_width
* @return void
*/
public static function printData($banner_text, $data, int $body_width = 70)
{
// Padding and wrap for the longest key
$max_key_len = max(array_map('strlen', array_keys($data)));
// Adjust padding for body_width
$padding = $body_width - ($max_key_len + 4); // Additional 2 spaces for initial padding
// Banner
$banner_width = $body_width + 2;
echo str_repeat("*", $banner_width) . PHP_EOL;
echo "* " . str_pad($banner_text, $banner_width - 4, ' ', STR_PAD_RIGHT) . " *" . PHP_EOL;
echo str_repeat("*", $banner_width) . PHP_EOL;
// Print data
foreach ($data as $key => $value) {
// Split value into lines if it's too long
$lines = str_split($value, $padding);
// Print lines
foreach ($lines as $i => $line) {
if ($i == 0) {
// First line - print key and value
echo ' ' . str_pad(strtoupper($key), $max_key_len, ' ', STR_PAD_RIGHT) . ' ' . $line . PHP_EOL;
} else {
// Additional lines - only value
echo str_repeat(' ', $max_key_len + 4) . $line . PHP_EOL;
}
}
}
}
}