From c09fb31e3421d5ac96b51da12c710c4e287cd0d5 Mon Sep 17 00:00:00 2001 From: Ron Rise Date: Tue, 6 May 2025 10:36:07 -0400 Subject: [PATCH] FONDLED THE CODE --- README.md | 96 ++++++++++++++++++++--- docker-compose.yml | 32 +++----- src/Controllers/Controller.php | 16 ++++ src/Controllers/MiddlewaresController.php | 12 ++- src/Controllers/RoutesController.php | 23 ++++-- src/Controllers/ServicesController.php | 12 ++- src/Server.php | 9 ++- src/Traefik/ProtocolEnum.php | 10 +++ 8 files changed, 162 insertions(+), 48 deletions(-) diff --git a/README.md b/README.md index bed88b4..48fc432 100644 --- a/README.md +++ b/README.md @@ -1,28 +1,104 @@ # Traefik Redis Provider API +[Traefik](https://traefik.io/traefik/) is a modern HTTP reverse proxy and load balancer that makes deploying microservices easy. This project provides +an api manager for the [Redis provider](https://doc.traefik.io/traefik/providers/redis/) for Traefik, allowing you to use Redis as a dynamic configuration backend. + +## Running the Project +### Prerequisites +- Docker +- Traefik +- Redis + +### Environment Variables +- `REDIS_HOST`: The URL of the Redis server. Default is `localhost`. +- `REDIS_PORT`: The port of the Redis server. Default is `6379`. +- `REDIS_DATABASE`: The database number to use. Default is `0`. +- `HTTP_PORT`: The port to run the HTTP server on. Default is `9501`. +```shell + docker run --rm -it -p 9501:9501 scr.siteworxpro.com/traefik-api:v1.1.0 +``` + +## Usage + +### Get All Routes +```http request +GET http://localhost:9501/http/routes +Accept: application/json +``` +```json +[ + "route-1", + "route-2" +] +``` + +### Get a Route +```http request +GET http://localhost:9501/http/routes/route-1 +Accept: application/json +``` +```json +{ + "id": "route-1", + "rule": "Host(`example.com`)", + "service": "service-1", + "middlewares": [ + "middleware-1" + ] +} +``` + ## Dev Environment +### Prerequisites +- Docker +- Docker Compose + +### Starting the Runtime ```shell -export PHP_IDE_CONFIG=serverName=localhost +docker-compose up -d +``` +### Start the server +```shell +docker exec -it traefik-redis-api-dev-runtime-1 rr serve ``` +You can access the api at `http://localhost:9501/` + +### Xdebug + +xdebug needs to be built into the container before it will work +```shell + docker exec -it traefik-redis-api-dev-runtime-1 bin/xdebug.sh +``` + +### Install the dependencies ```shell docker run --rm -v $(PWD):/app siteworxpro/composer install --ignore-platform-reqs ``` +### Running all tests ```shell docker run --rm -v $(PWD):/app siteworxpro/composer run tests:all ``` -### migrations -create a new migration -```shell -docker run --rm -v $(PWD):/app siteworxpro/migrate:v4.18.3 create -ext sql -dir /app/db/migrations -seq create_users_table -``` + + +## License ```text -postgres://siteworxpro:password@localhost:5432/siteworxpro?sslmode=disable -``` +Copyright (c)2025 Siteworx Professionals, LLC -```shell -docker run --rm -v $(PWD):/app siteworxpro/migrate:v4.18.3 -database "postgres://siteworxpro:password@localhost:5432/siteworxpro?sslmode=disable" -path /app/db/migrations up +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated +documentation +files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom +the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the +Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ``` \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index 8d23be1..0fedb43 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,10 +1,10 @@ -#volumes: -# pgdata: {} +volumes: + redisdata: {} services: dev-runtime: ports: - - "8080:8080" + - "9501:9501" volumes: - .:/app build: @@ -14,24 +14,12 @@ services: environment: PHP_IDE_CONFIG: serverName=localhost WORKERS: 1 - HTTP_PORT: 8080 DEBUG: 1 - REDIS_HOST: 192.168.1.30 + REDIS_HOST: redis -# migrations: -# image: siteworxpro/migrate:v4.18.3 -# restart: no -# volumes: -# - .:/app -# command: "-database 'postgres://${DB_DATABASE-siteworxpro}:${DB_PASSWORD-password}@${DB_HOST-postgres}:5432/siteworxpro?sslmode=disable' -path /app/db/migrations up" -# -# postgres: -# image: postgres:latest -# environment: -# POSTGRES_USER: ${DB_USERNAME:-siteworxpro} -# POSTGRES_PASSWORD: ${DB_PASSWORD:-password} -# POSTGRES_DB: ${DB_DATABASE:-siteworxpro} -# ports: -# - "5432:5432" -# volumes: -# - pgdata:/var/lib/postgresql/data \ No newline at end of file + redis: + image: redis:latest + ports: + - "6379:6379" + volumes: + - redisdata:/data \ No newline at end of file diff --git a/src/Controllers/Controller.php b/src/Controllers/Controller.php index 641f6d8..aec682f 100644 --- a/src/Controllers/Controller.php +++ b/src/Controllers/Controller.php @@ -4,12 +4,28 @@ declare(strict_types=1); namespace Siteworxpro\App\Controllers; +use Illuminate\Support\Facades\Log; use League\Route\Http\Exception\NotFoundException; use Nyholm\Psr7\ServerRequest; use Psr\Http\Message\ResponseInterface; +use Siteworxpro\App\Traefik\ProtocolEnum; abstract class Controller implements ControllerInterface { + + protected function protocolEnumFromRequest(ServerRequest $request): ProtocolEnum + { + $protocol = $request->getAttribute('protocol'); + + try { + return ProtocolEnum::fromString($protocol); + } catch (\InvalidArgumentException $e) { + Log::error($e->getMessage()); + + return ProtocolEnum::HTTP; + } + } + /** * @param ServerRequest $request * @return ResponseInterface diff --git a/src/Controllers/MiddlewaresController.php b/src/Controllers/MiddlewaresController.php index e30f4b9..7d0a3cd 100644 --- a/src/Controllers/MiddlewaresController.php +++ b/src/Controllers/MiddlewaresController.php @@ -20,10 +20,14 @@ class MiddlewaresController extends Controller if ($request->getAttribute('id') !== null) { $name = $request->getAttribute('id'); - return JsonResponseFactory::createJsonResponse(RedisClient::getMiddleware($name)); + return JsonResponseFactory::createJsonResponse( + RedisClient::getMiddleware($name, $this->protocolEnumFromRequest($request)) + ); } - return JsonResponseFactory::createJsonResponse(RedisClient::getAllMiddlewares()); + return JsonResponseFactory::createJsonResponse( + RedisClient::getAllMiddlewares($this->protocolEnumFromRequest($request)) + ); } /** @@ -39,7 +43,7 @@ class MiddlewaresController extends Controller return JsonResponseFactory::createJsonResponse(['error' => 'Middleware is invalid'], 400); } - RedisClient::createOrReplace($name, $data, EntityEnum::MIDDLEWARE); + RedisClient::createOrReplace($name, $data, EntityEnum::MIDDLEWARE, $this->protocolEnumFromRequest($request)); return JsonResponseFactory::createJsonResponse(['message' => 'Middleware added successfully']); } @@ -55,7 +59,7 @@ class MiddlewaresController extends Controller return JsonResponseFactory::createJsonResponse(['error' => 'Middleware is invalid'], 400); } - RedisClient::deleteAllKeys($name, EntityEnum::MIDDLEWARE); + RedisClient::deleteAllKeys($name, EntityEnum::MIDDLEWARE , $this->protocolEnumFromRequest($request)); return JsonResponseFactory::createJsonResponse(['message' => 'Middleware deleted successfully']); } diff --git a/src/Controllers/RoutesController.php b/src/Controllers/RoutesController.php index 4b49949..998ec78 100644 --- a/src/Controllers/RoutesController.php +++ b/src/Controllers/RoutesController.php @@ -20,10 +20,14 @@ class RoutesController extends Controller if ($request->getAttribute('id') !== null) { $name = $request->getAttribute('id'); - return JsonResponseFactory::createJsonResponse(RedisClient::getRouter($name)); + return JsonResponseFactory::createJsonResponse( + RedisClient::getRouter($name, $this->protocolEnumFromRequest($request)) + ); } - return JsonResponseFactory::createJsonResponse(RedisClient::getAllRouters()); + return JsonResponseFactory::createJsonResponse( + RedisClient::getAllRouters($this->protocolEnumFromRequest($request)) + ); } /** @@ -43,7 +47,12 @@ class RoutesController extends Controller return JsonResponseFactory::createJsonResponse(['error' => 'Rule is required'], 400); } - RedisClient::createOrReplace($name, $data, EntityEnum::ROUTER); + RedisClient::createOrReplace( + $name, + $data, + EntityEnum::ROUTER, + $this->protocolEnumFromRequest($request) + ); return JsonResponseFactory::createJsonResponse(['message' => 'Router created successfully']); } @@ -55,7 +64,11 @@ class RoutesController extends Controller { $name = $request->getAttribute('id'); - RedisClient::deleteAllKeys($name, EntityEnum::ROUTER); + RedisClient::deleteAllKeys( + $name, + EntityEnum::ROUTER, + $this->protocolEnumFromRequest($request) + ); return JsonResponseFactory::createJsonResponse(['message' => 'Router deleted successfully']); } @@ -70,7 +83,7 @@ class RoutesController extends Controller $data = $request->getParsedBody(); try { - RedisClient::patchEntity($name, $data, EntityEnum::ROUTER); + RedisClient::patchEntity($name, $data, EntityEnum::ROUTER, $this->protocolEnumFromRequest($request)); } catch (\InvalidArgumentException) { return JsonResponseFactory::createJsonResponse(['error' => 'Router not found'], 404); } diff --git a/src/Controllers/ServicesController.php b/src/Controllers/ServicesController.php index 1eb5a51..4767d35 100644 --- a/src/Controllers/ServicesController.php +++ b/src/Controllers/ServicesController.php @@ -20,10 +20,14 @@ class ServicesController extends Controller if ($request->getAttribute('id') !== null) { $name = $request->getAttribute('id'); - return JsonResponseFactory::createJsonResponse(RedisClient::getService($name)); + return JsonResponseFactory::createJsonResponse( + RedisClient::getService($name, $this->protocolEnumFromRequest($request)) + ); } - return JsonResponseFactory::createJsonResponse(RedisClient::getAllServices()); + return JsonResponseFactory::createJsonResponse( + RedisClient::getAllServices($this->protocolEnumFromRequest($request)) + ); } /** @@ -39,7 +43,7 @@ class ServicesController extends Controller return JsonResponseFactory::createJsonResponse(['error' => 'loadbalancer is required'], 400); } - RedisClient::createOrReplace($name, $data, EntityEnum::SERVICE); + RedisClient::createOrReplace($name, $data, EntityEnum::SERVICE, $this->protocolEnumFromRequest($request)); return JsonResponseFactory::createJsonResponse(['message' => 'Service updated successfully']); } @@ -55,7 +59,7 @@ class ServicesController extends Controller return JsonResponseFactory::createJsonResponse(['error' => 'Service name is required'], 400); } - RedisClient::deleteAllKeys($name, EntityEnum::SERVICE); + RedisClient::deleteAllKeys($name, EntityEnum::SERVICE, $this->protocolEnumFromRequest($request)); return JsonResponseFactory::createJsonResponse(['message' => 'Service deleted successfully']); } diff --git a/src/Server.php b/src/Server.php index 84cd8eb..a4a5de4 100644 --- a/src/Server.php +++ b/src/Server.php @@ -120,7 +120,10 @@ class Server */ protected function registerRoutes(): void { - $this->router->group('/http/routes', function (RouteGroup $router) { + $this->router->addPatternMatcher('protocolEnums', 'http|tcp|udp'); + $this->router->addPatternMatcher('protocolEnumsMiddleware', 'http|tcp'); + + $this->router->group('/{protocol:protocolEnums}/routes', function (RouteGroup $router) { $router->get('/', RoutesController::class . '::get'); $router->get('/{id}', RoutesController::class . '::get'); $router->post('/{id}', RoutesController::class . '::post'); @@ -128,14 +131,14 @@ class Server $router->delete('/{id}', RoutesController::class . '::delete'); }); - $this->router->group('/http/services', function (RouteGroup $router) { + $this->router->group('/{protocol:protocolEnums}/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) { + $this->router->group('/{protocol:protocolEnumsMiddleware}/middlewares', function (RouteGroup $router) { $router->get('/', MiddlewaresController::class . '::get'); $router->get('/{id}', MiddlewaresController::class . '::get'); $router->post('/{id}', MiddlewaresController::class . '::post'); diff --git a/src/Traefik/ProtocolEnum.php b/src/Traefik/ProtocolEnum.php index 6d811bc..d76060f 100644 --- a/src/Traefik/ProtocolEnum.php +++ b/src/Traefik/ProtocolEnum.php @@ -18,4 +18,14 @@ enum ProtocolEnum self::UDP => 'udp', }; } + + public static function fromString(string $value): self + { + return match ($value) { + 'http' => self::HTTP, + 'tcp' => self::TCP, + 'udp' => self::UDP, + default => throw new \InvalidArgumentException("Invalid protocol: $value"), + }; + } }