done. going to bed now.

This commit is contained in:
2025-05-05 19:27:09 -04:00
commit f7567db1b9
40 changed files with 6775 additions and 0 deletions

View File

@@ -0,0 +1,47 @@
<?php
declare(strict_types=1);
namespace Siteworxpro\App\Controllers;
use League\Route\Http\Exception\NotFoundException;
use Nyholm\Psr7\ServerRequest;
use Psr\Http\Message\ResponseInterface;
abstract class Controller implements ControllerInterface
{
/**
* @param ServerRequest $request
* @return ResponseInterface
* @throws NotFoundException
*/
public function get(ServerRequest $request): ResponseInterface
{
throw new NotFoundException("not found");
}
/**
* @throws NotFoundException
*/
public function post(ServerRequest $request): ResponseInterface
{
throw new NotFoundException("not found");
}
/**
* @throws NotFoundException
*/
public function delete(ServerRequest $request): ResponseInterface
{
throw new NotFoundException("not found");
}
/**
* @throws NotFoundException
*/
public function patch(ServerRequest $request): ResponseInterface
{
throw new NotFoundException("not found");
}
}

View File

@@ -0,0 +1,43 @@
<?php
declare(strict_types=1);
namespace Siteworxpro\App\Controllers;
use Nyholm\Psr7\ServerRequest;
use Psr\Http\Message\ResponseInterface;
interface ControllerInterface
{
/**
* Handle the request and return a response.
*
* @param ServerRequest $request The request data.
* @return mixed The response data.
*/
public function get(ServerRequest $request): ResponseInterface;
/**
* Handle the request and return a response.
*
* @param ServerRequest $request The request data.
* @return mixed The response data.
*/
public function post(ServerRequest $request): ResponseInterface;
/**
* Handle the request and return a response.
*
* @param ServerRequest $request The request data.
* @return mixed The response data.
*/
public function delete(ServerRequest $request): ResponseInterface;
/**
* Handle the request and return a response.
*
* @param ServerRequest $request The request data.
* @return mixed The response data.
*/
public function patch(ServerRequest $request): ResponseInterface;
}

View File

@@ -0,0 +1,63 @@
<?php
declare(strict_types=1);
namespace Siteworxpro\App\Controllers;
use Nyholm\Psr7\ServerRequest;
use Psr\Http\Message\ResponseInterface;
use Siteworxpro\App\Http\JsonResponseFactory;
use Siteworxpro\App\Traefik\EntityEnum;
use Siteworxpro\App\Traefik\RedisClient;
class MiddlewaresController extends Controller
{
/**
* @throws \JsonException
*/
public function get(ServerRequest $request): ResponseInterface
{
if ($request->getAttribute('id') !== null) {
$name = $request->getAttribute('id');
return JsonResponseFactory::createJsonResponse(RedisClient::getMiddleware($name));
}
return JsonResponseFactory::createJsonResponse(RedisClient::getAllMiddlewares());
}
/**
* @throws \JsonException
*/
public function post(ServerRequest $request): ResponseInterface
{
$name = $request->getAttribute('id');
$data = $request->getParsedBody();
if (empty($data)) {
return JsonResponseFactory::createJsonResponse(['error' => 'Middleware is invalid'], 400);
}
RedisClient::createOrReplace($name, $data, EntityEnum::MIDDLEWARE);
return JsonResponseFactory::createJsonResponse(['message' => 'Middleware added successfully']);
}
/**
* @throws \JsonException
*/
public function delete(ServerRequest $request): ResponseInterface
{
$name = $request->getAttribute('id');
if (empty($name)) {
return JsonResponseFactory::createJsonResponse(['error' => 'Middleware is invalid'], 400);
}
RedisClient::deleteAllKeys($name, EntityEnum::MIDDLEWARE);
return JsonResponseFactory::createJsonResponse(['message' => 'Middleware deleted successfully']);
}
}

View File

@@ -0,0 +1,81 @@
<?php
declare(strict_types=1);
namespace Siteworxpro\App\Controllers;
use Nyholm\Psr7\ServerRequest;
use Psr\Http\Message\ResponseInterface;
use Siteworxpro\App\Http\JsonResponseFactory;
use Siteworxpro\App\Traefik\EntityEnum;
use Siteworxpro\App\Traefik\RedisClient;
class RoutesController extends Controller
{
/**
* @throws \JsonException
*/
public function get(ServerRequest $request): ResponseInterface
{
if ($request->getAttribute('id') !== null) {
$name = $request->getAttribute('id');
return JsonResponseFactory::createJsonResponse(RedisClient::getRouter($name));
}
return JsonResponseFactory::createJsonResponse(RedisClient::getAllRouters());
}
/**
* @throws \JsonException
*/
public function post(ServerRequest $request): ResponseInterface
{
$data = $request->getParsedBody();
$name = $request->getAttribute('id');
if (empty($data['service'])) {
return JsonResponseFactory::createJsonResponse(['error' => 'Service is required'], 400);
}
if (empty($data['rule'])) {
return JsonResponseFactory::createJsonResponse(['error' => 'Rule is required'], 400);
}
RedisClient::createOrReplace($name, $data, EntityEnum::ROUTER);
return JsonResponseFactory::createJsonResponse(['message' => 'Router created successfully']);
}
/**
* @throws \JsonException
*/
public function delete(ServerRequest $request): ResponseInterface
{
$name = $request->getAttribute('id');
RedisClient::deleteAllKeys($name, EntityEnum::ROUTER);
return JsonResponseFactory::createJsonResponse(['message' => 'Router deleted successfully']);
}
/**
* @throws \JsonException
*/
public function patch(ServerRequest $request): ResponseInterface
{
$name = $request->getAttribute('id');
$data = $request->getParsedBody();
try {
RedisClient::patchEntity($name, $data, EntityEnum::ROUTER);
} catch (\InvalidArgumentException) {
return JsonResponseFactory::createJsonResponse(['error' => 'Router not found'], 404);
}
return JsonResponseFactory::createJsonResponse(['message' => 'Router updated successfully']);
}
}

View File

@@ -0,0 +1,63 @@
<?php
declare(strict_types=1);
namespace Siteworxpro\App\Controllers;
use Nyholm\Psr7\ServerRequest;
use Psr\Http\Message\ResponseInterface;
use Siteworxpro\App\Http\JsonResponseFactory;
use Siteworxpro\App\Traefik\EntityEnum;
use Siteworxpro\App\Traefik\RedisClient;
class ServicesController extends Controller
{
/**
* @throws \JsonException
*/
public function get(ServerRequest $request): ResponseInterface
{
if ($request->getAttribute('id') !== null) {
$name = $request->getAttribute('id');
return JsonResponseFactory::createJsonResponse(RedisClient::getService($name));
}
return JsonResponseFactory::createJsonResponse(RedisClient::getAllServices());
}
/**
* @throws \JsonException
*/
public function post(ServerRequest $request): ResponseInterface
{
$data = $request->getParsedBody();
$name = $request->getAttribute('id');
if (empty($data['loadbalancer'])) {
return JsonResponseFactory::createJsonResponse(['error' => 'loadbalancer is required'], 400);
}
RedisClient::createOrReplace($name, $data, EntityEnum::SERVICE);
return JsonResponseFactory::createJsonResponse(['message' => 'Service updated successfully']);
}
/**
* @throws \JsonException
*/
public function delete(ServerRequest $request): ResponseInterface
{
$name = $request->getAttribute('id');
if (empty($name)) {
return JsonResponseFactory::createJsonResponse(['error' => 'Service name is required'], 400);
}
RedisClient::deleteAllKeys($name, EntityEnum::SERVICE);
return JsonResponseFactory::createJsonResponse(['message' => 'Service deleted successfully']);
}
}

57
src/Facades/Config.php Normal file
View File

@@ -0,0 +1,57 @@
<?php
declare(strict_types=1);
namespace Siteworxpro\App\Facades;
use Illuminate\Contracts\Container\BindingResolutionException;
use Illuminate\Support\Facades\Facade;
use Siteworx\Config\Exception\EmptyDirectoryException;
use Siteworx\Config\Exception\FileNotFoundException;
use Siteworx\Config\Exception\UnsupportedFormatException;
/**
* Class Config
*
* This class serves as a facade for the configuration settings of the application.
* It extends the Facade class from the Illuminate\Support\Facades namespace.
*
* @method static bool | string | int get(string $key) Retrieve the configuration value for the given key.
*
* @package Siteworx\App\Facades
*/
class Config extends Facade
{
protected static $cached = false;
/**
* @throws UnsupportedFormatException
* @throws FileNotFoundException
* @throws EmptyDirectoryException
*/
public static function getFacadeRoot(): \Siteworx\Config\Config
{
if (self::$resolvedInstance !== null) {
try {
$config = self::resolveFacadeInstance(self::getFacadeAccessor());
if ($config instanceof \Siteworx\Config\Config) {
return $config;
}
} catch (BindingResolutionException) {
}
}
return \Siteworx\Config\Config::load(__DIR__ . '/../../config.php');
}
/**
* Get the registered name of the component.
*
* @return string The name of the component.
*/
protected static function getFacadeAccessor(): string
{
return \Siteworx\Config\Config::class;
}
}

51
src/Facades/Logger.php Normal file
View File

@@ -0,0 +1,51 @@
<?php
declare(strict_types=1);
namespace Siteworxpro\App\Facades;
use Illuminate\Support\Facades\Facade;
use RoadRunner\Logger\Logger as RRLogger;
use Spiral\Goridge\RPC\RPC;
/**
* Class Logger
*
* This class serves as a facade for the Monolog logger.
* It extends the Facade class from the Illuminate\Support\Facades namespace.
*
* @method static debug(string $message, array $context = []) Log an informational message.
* @method static info(string $message, array $context = []) Log an informational message.
* @method static error(string $message, array $context = []) Log an error message.
* @method static warning(string $message, array $context = []) Log a warning message.
*
* @package Siteworxpro\App\Facades
*/
class Logger extends Facade
{
public static function getFacadeRoot(): RRLogger
{
if (self::$resolvedInstance !== null) {
$logger = self::resolveFacadeInstance(self::getFacadeAccessor());
if ($logger instanceof RRLogger) {
return $logger;
}
}
$rpc = RPC::create('tcp://127.0.0.1:6001');
return new RRLogger($rpc);
}
/**
* Get the registered name of the component.
*
* @return string The name of the component.
*/
protected static function getFacadeAccessor(): string
{
return RRLogger::class;
}
}

53
src/Facades/Redis.php Normal file
View File

@@ -0,0 +1,53 @@
<?php
declare(strict_types=1);
namespace Siteworxpro\App\Facades;
use Illuminate\Support\Facades\Facade;
use Predis\Client;
use Predis\Response\Status;
/**
* Facade for the Redis client.
*
* This class provides a static interface to interact with the Redis client.
*
* @method static array scan($cursor, ?array $options = null)
* @method static string|null get(string $key)
* @method static Status|null set(string $key, $value, $expireResolution = null, $expireTTL = null, $flag = null)
* @method static array keys(string $pattern)
* @method static int del(string $key)
*/
class Redis extends Facade
{
public static function getFacadeRoot(): Client
{
if (self::$resolvedInstance !== null) {
$redis = self::resolveFacadeInstance(self::getFacadeAccessor());
if ($redis instanceof Client) {
return $redis;
}
}
// Create a new Redis client instance if not already resolved
return new Client([
'scheme' => 'tcp',
'host' => Config::get('redis.host'),
'port' => Config::get('redis.port'),
'database' => Config::get('redis.database'),
]);
}
/**
* Get the registered name of the component.
*
* @return string The name of the component.
*/
protected static function getFacadeAccessor(): string
{
return Client::class;
}
}

26
src/Helpers/Env.php Normal file
View File

@@ -0,0 +1,26 @@
<?php
declare(strict_types=1);
namespace Siteworxpro\App\Helpers;
abstract class Env
{
/**
* @param string $key
* @param null $default
* @param string $castTo
* @return float|bool|int|string
*/
public static function get(string $key, $default = null, string $castTo = 'string'): float | bool | int | string
{
$env = getenv($key) !== false ? getenv($key) : $default;
return match ($castTo) {
'bool', 'boolean' => (bool) $env,
'int', 'integer' => (int) $env,
'float' => (float) $env,
default => (string) $env,
};
}
}

View File

@@ -0,0 +1,34 @@
<?php
declare(strict_types=1);
namespace Siteworxpro\App\Http;
use Nyholm\Psr7\Response;
/**
* Class JsonResponseFactory
*
* A factory class for creating JSON responses.
*/
class JsonResponseFactory
{
/**
* Create a JSON response with the given data and status code.
*
* @param array $data The data to include in the response.
* @param int $statusCode The HTTP status code for the response.
* @return Response The JSON response.
* @throws \JsonException
*/
public static function createJsonResponse(array $data, int $statusCode = 200): Response
{
return new Response(
status: $statusCode,
headers: [
'Content-Type' => 'application/json',
],
body: json_encode($data, JSON_THROW_ON_ERROR)
);
}
}

View File

@@ -0,0 +1,72 @@
<?php
declare(strict_types=1);
namespace Siteworxpro\App\Http\Middleware;
use Nyholm\Psr7\Response;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;
use Siteworxpro\App\Facades\Config;
/**
* Class CorsMiddleware
*
* Middleware to handle CORS (Cross-Origin Resource Sharing) requests.
* It checks the origin of the request and sets appropriate CORS headers
* in the response.
*/
class CorsMiddleware implements MiddlewareInterface
{
/**
* Process the incoming request and add CORS headers to the response.
*
* @param ServerRequestInterface $request The incoming request.
* @param RequestHandlerInterface $handler The request handler.
* @return ResponseInterface The response with CORS headers.
*/
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
{
$origin = $request->getHeaderLine('Origin');
$allowedOrigins = array_map(
'trim',
explode(
',',
Config::get('cors.allowed_origins')
)
);
$allowOrigin = in_array($origin, $allowedOrigins, true)
? $origin
: null;
if ($request->getMethod() === 'OPTIONS') {
$response = new Response(204);
} else {
$response = $handler->handle($request);
}
if ($allowOrigin === null) {
return $response; // Do not add CORS headers if origin is not allowed.
}
$response = $response
->withHeader('Access-Control-Allow-Origin', $allowOrigin)
->withHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, PATCH, DELETE, OPTIONS')
->withHeader(
'Access-Control-Allow-Headers',
$request->getHeaderLine('Access-Control-Request-Headers')
?: 'Content-Type, Authorization'
);
if (Config::get('cors.allow_credentials') === true) {
$response = $response->withHeader('Access-Control-Allow-Credentials', 'true');
}
$maxAge = Config::get('cors.max_age') ?: '86400'; // Use correct configuration key.
return $response->withHeader('Access-Control-Max-Age', $maxAge);
}
}

11
src/Models/Model.php Normal file
View File

@@ -0,0 +1,11 @@
<?php
declare(strict_types=1);
namespace Siteworxpro\App\Models;
use Illuminate\Database\Eloquent\Model as ORM;
abstract class Model extends ORM
{
}

198
src/Server.php Normal file
View File

@@ -0,0 +1,198 @@
<?php
declare(strict_types=1);
namespace Siteworxpro\App;
use Illuminate\Container\Container;
use Illuminate\Database\Capsule\Manager;
use Illuminate\Support\Facades\Facade;
use League\Route\Http\Exception\MethodNotAllowedException;
use League\Route\Http\Exception\NotFoundException;
use League\Route\RouteGroup;
use League\Route\Router;
use Nyholm\Psr7\Factory\Psr17Factory;
use Siteworxpro\App\Controllers\MiddlewaresController;
use Siteworxpro\App\Controllers\RoutesController;
use Siteworxpro\App\Controllers\ServicesController;
use Siteworxpro\App\Facades\Config;
use Siteworxpro\App\Facades\Logger;
use Siteworxpro\App\Http\JsonResponseFactory;
use Siteworxpro\App\Http\Middleware\CorsMiddleware;
use Spiral\RoadRunner\Http\PSR7Worker;
use Spiral\RoadRunner\Worker;
/**
* Class Server
*
* This class represents the main server application.
* It handles incoming HTTP requests, routes them to the appropriate handlers,
* and manages the server lifecycle.
*
* @package Siteworxpro\App
*/
class Server
{
/**
* @var Router The router instance for handling routes.
*/
protected Router $router;
/**
* @var PSR7Worker The PSR-7 worker instance for handling HTTP requests.
*/
protected PSR7Worker $worker;
/**
* Server constructor.
*
* Initializes the server by booting the PSR-7 worker and router.
*/
public function __construct()
{
$this->boot();
}
/**
* Bootstraps the server by initializing the PSR-7 worker and router.
*
* This method sets up the PSR-7 worker and router instances, and registers
* the routes for the server. It should be called in the constructor of
* subclasses to ensure proper initialization.
*
* @return void
*/
private function boot(): void
{
$container = new Container();
Facade::setFacadeApplication($container);
$this->worker = new PSR7Worker(
Worker::create(),
new Psr17Factory(),
new Psr17Factory(),
new Psr17Factory()
);
$this->router = new Router();
$this->registerRoutes();
// $this->bootModelCapsule(); // no db
}
/**
* Bootstraps the model capsule for database connections.
*
* This method sets up the database connection using the Eloquent ORM.
* It retrieves the database configuration from the Config facade and
* initializes the Eloquent capsule manager.
*
* @return void
*/
public function bootModelCapsule(): void
{
$capsule = new Manager();
$capsule->addConnection([
'driver' => Config::get('db.driver'),
'host' => Config::get('db.host'),
'database' => Config::get('db.database'),
'username' => Config::get('db.username'),
'password' => Config::get('db.password'),
'charset' => 'utf8',
'collation' => 'utf8_unicode_ci',
'prefix' => '',
]);
$capsule->setAsGlobal();
$capsule->bootEloquent();
}
/**
* Registers the routes for the server.
*
* This method is responsible for defining the routes that the server will handle.
* It should be implemented in subclasses to provide specific route definitions.
*
* @return void
*/
protected function registerRoutes(): void
{
$this->router->group('/http/routes', function (RouteGroup $router) {
$router->get('/', RoutesController::class . '::get');
$router->get('/{id}', RoutesController::class . '::get');
$router->post('/{id}', RoutesController::class . '::post');
$router->patch('/{id}', RoutesController::class . '::patch');
$router->delete('/{id}', RoutesController::class . '::delete');
});
$this->router->group('/http/services', function (RouteGroup $router) {
$router->get('/', ServicesController::class . '::get');
$router->get('/{id}', ServicesController::class . '::get');
$router->post('/{id}', ServicesController::class . '::post');
$router->delete('/{id}', ServicesController::class . '::delete');
});
$this->router->group('/http/middlewares', function (RouteGroup $router) {
$router->get('/', MiddlewaresController::class . '::get');
$router->get('/{id}', MiddlewaresController::class . '::get');
$router->post('/{id}', MiddlewaresController::class . '::post');
$router->delete('/{id}', MiddlewaresController::class . '::delete');
});
$this->router->middleware(new CorsMiddleware());
}
/**
* Starts the server and handles incoming requests.
*
* This method enters an infinite loop to continuously handle incoming HTTP requests.
* It decodes the request body, routes the request, and sends the response. It also handles
* exceptions and ensures proper cleanup after each request.
*
* @throws \JsonException If there is an error decoding the JSON request body.
*/
public function startServer(): void
{
Logger::info(sprintf('Server started: %s', microtime(true)));
Logger::info(sprintf('Server PID: %s', getmypid()));
Logger::info(sprintf('Server Listening on: 0.0.0.0:%s', Config::get('server.port')));
while (true) {
try {
$request = $this->worker->waitRequest();
if ($request === null) {
break;
}
$request = $request->withParsedBody(json_decode($request->getBody()->getContents(), true));
$response = $this->router->handle($request);
$this->worker->respond($response);
} catch (MethodNotAllowedException | NotFoundException) {
$this->worker->respond(
JsonResponseFactory::createJsonResponse(
['status_code' => 404, 'reason_phrase' => 'Not Found'],
404
)
);
} catch (\Throwable $e) {
Logger::error($e->getMessage());
Logger::error($e->getTraceAsString());
$json = ['status_code' => 500, 'reason_phrase' => 'Server Error'];
if (Config::get("server.dev_mode")) {
$json = [
'status_code' => 500,
'reason_phrase' => 'Server Error',
'message' => $e->getMessage(),
'trace' => $e->getTraceAsString(),
];
}
$this->worker->respond(JsonResponseFactory::createJsonResponse($json, 500));
}
}
}
}

View File

@@ -0,0 +1,21 @@
<?php
declare(strict_types=1);
namespace Siteworxpro\App\Traefik;
enum EntityEnum
{
case ROUTER;
case SERVICE;
case MIDDLEWARE;
public function getValue(): string
{
return match ($this) {
self::ROUTER => 'routers',
self::SERVICE => 'services',
self::MIDDLEWARE => 'middlewares',
};
}
}

View File

@@ -0,0 +1,21 @@
<?php
declare(strict_types=1);
namespace Siteworxpro\App\Traefik;
enum ProtocolEnum
{
case HTTP;
case TCP;
case UDP;
public function getValue(): string
{
return match ($this) {
self::HTTP => 'http',
self::TCP => 'tcp',
self::UDP => 'udp',
};
}
}

217
src/Traefik/RedisClient.php Normal file
View File

@@ -0,0 +1,217 @@
<?php
declare(strict_types=1);
namespace Siteworxpro\App\Traefik;
use Illuminate\Support\Collection;
use Predis\Collection\Iterator\Keyspace;
use Siteworxpro\App\Facades\Redis;
/**
* Class RedisClient
*/
class RedisClient
{
/**
* @param string $name
* @param array $data
* @param EntityEnum $entity
* @param ProtocolEnum $type
* @return void
*/
public static function createOrReplace(string $name, array $data, EntityEnum $entity, ProtocolEnum $type = ProtocolEnum::HTTP): void
{
self::deleteAllKeys($name, $entity, $type);
$collection = self::flattenToDotArray($data);
foreach ($collection as $key => $value) {
$redisKey = 'traefik/' . $type->getValue() . '/' . $entity->getValue() . "/$name/" . str_replace('.', '/', $key);
Redis::set($redisKey, $value);
}
}
/**
* @param string $name
* @param array $data
* @param EntityEnum $entity
* @param ProtocolEnum $type
* @return void
*/
public static function patchEntity(string $name, array $data, EntityEnum $entity, ProtocolEnum $type = ProtocolEnum::HTTP): void
{
$collection = self::flattenToDotArray($data);
$checkKey = 'traefik/' . $type->getValue() . '/' . $entity->getValue() . "/$name";
$keys = Redis::keys($checkKey . '/*');
if (empty($keys)) {
throw new \InvalidArgumentException("The key $checkKey does not exist.");
}
$cleanedUpKeys = [];
foreach ($collection as $key => $value) {
// regex if key matches [key].[digit]
if (preg_match('/\.[0-9]+$/', $key)) {
$arrayKey = preg_replace('/\.[0-9]+$/', '', $key);
if (in_array($arrayKey, $cleanedUpKeys)) {
$redisKey = 'traefik/' . $type->getValue() . '/' . $entity->getValue() . "/$name/" . str_replace('.', '/', $key);
Redis::set($redisKey, $value);
continue;
}
// key is an array, delete keys under array
$arrayKeys = Redis::keys($checkKey . '/' . str_replace('.', '/', $arrayKey) . '/*');
foreach ($arrayKeys as $k) {
Redis::del($k);
}
$cleanedUpKeys[] = $arrayKey;
}
$redisKey = 'traefik/' . $type->getValue() . '/' . $entity->getValue() . "/$name/" . str_replace('.', '/', $key);
Redis::set($redisKey, $value);
}
}
/**
* @param string $name
* @param EntityEnum $entity
* @param ProtocolEnum $protocol
* @return bool
*/
public static function deleteAllKeys(string $name, EntityEnum $entity, ProtocolEnum $protocol = ProtocolEnum::HTTP): bool
{
$pattern = 'traefik/' . $protocol->getValue() . '/' . $entity->getValue() . "/$name/*";
foreach (new Keyspace(Redis::getFacadeRoot(), $pattern) as $key) {
Redis::del($key);
}
return true;
}
/**
* @param string $name
* @param ProtocolEnum $type
* @return array
*/
public static function getMiddleware(string $name, ProtocolEnum $type = ProtocolEnum::HTTP): array
{
$pattern = 'traefik/' . $type->getValue() . '/' . EntityEnum::MIDDLEWARE->getValue() . "/$name/*";
return self::fetchValuesToArray($pattern);
}
/**
* @param ProtocolEnum $type
* @return array
*/
public static function getAllMiddlewares(ProtocolEnum $type = ProtocolEnum::HTTP): array
{
$pattern = 'traefik/' . $type->getValue() . '/' . EntityEnum::MIDDLEWARE->getValue() . '/*';
return self::getUniqueKeys($pattern, 3);
}
/**
* @param ProtocolEnum $type
* @return array
*/
public static function getAllServices(ProtocolEnum $type = ProtocolEnum::HTTP): array
{
$pattern = 'traefik/' . $type->getValue() . '/' . EntityEnum::SERVICE->getValue() . '/*';
return self::getUniqueKeys($pattern, 3);
}
/**
* @param string $serviceName
* @param ProtocolEnum $type
* @return array
*/
public static function getService(string $serviceName, ProtocolEnum $type = ProtocolEnum::HTTP): array
{
$pattern = 'traefik/' . $type->getValue() . '/' . EntityEnum::SERVICE->getValue() . "/$serviceName/*";
return self::fetchValuesToArray($pattern);
}
/**
* @param ProtocolEnum $type
* @return array
*/
public static function getAllRouters(ProtocolEnum $type = ProtocolEnum::HTTP): array
{
$pattern = 'traefik/' . $type->getValue() . '/' . EntityEnum::ROUTER->getValue() . '/*';
return self::getUniqueKeys($pattern, 3);
}
/**
* @param string $name
* @param ProtocolEnum $type
* @return array
*/
public static function getRouter(string $name, ProtocolEnum $type = ProtocolEnum::HTTP): array
{
$pattern = 'traefik/' . $type->getValue() . '/' . EntityEnum::ROUTER->getValue() . "/$name/*";
return self::fetchValuesToArray($pattern);
}
/**
* @param string $pattern
* @param int $position
* @return array
*/
private static function getUniqueKeys(string $pattern, int $position): array
{
$values = new Collection();
$redis = Redis::getFacadeRoot();
foreach (new Keyspace($redis, $pattern) as $key) {
$parts = explode('/', $key);
if (isset($parts[$position])) {
$values->push($parts[$position]);
}
}
return $values->unique()->values()->toArray();
}
/**
* @param string $pattern
* @return array
*/
private static function fetchValuesToArray(string $pattern): array
{
$redis = Redis::getFacadeRoot();
$values = new Collection();
foreach (new Keyspace($redis, $pattern) as $key) {
$parts = explode('/', $key);
$arrayPath = array_slice($parts, 4);
$keyPath = implode('.', $arrayPath);
$values->put($keyPath, $redis->get($key));
}
return $values->undot()->toArray();
}
/**
* @param array $data
* @return array
*/
public static function flattenToDotArray(array $data): array
{
$collection = new Collection($data);
return $collection->dot()->toArray();
}
}