feat/cli-framework (#12)
All checks were successful
🧪✨ Tests Workflow / 🛡️ 🔒 Library Audit (push) Successful in 1m37s
🧪✨ Tests Workflow / 📝 ✨ Code Lint (push) Successful in 1m32s
🧪✨ Tests Workflow / 🧪 ✨ Database Migrations (push) Successful in 1m54s
🧪✨ Tests Workflow / 🛡️ 🔒 License Check (push) Successful in 1m46s
🧪✨ Tests Workflow / 🐙 🔍 Code Sniffer (push) Successful in 1m49s
🧪✨ Tests Workflow / 🧪 ✅ Unit Tests (push) Successful in 1m18s

Reviewed-on: #12
Co-authored-by: Ron Rise <ron@siteworxpro.com>
Co-committed-by: Ron Rise <ron@siteworxpro.com>
This commit was merged in pull request #12.
This commit is contained in:
2025-11-11 14:52:29 +00:00
committed by Siteworx Pro Gitea
parent 13445a0719
commit 7d0b00fb89
13 changed files with 357 additions and 141 deletions

133
src/Api.php Normal file
View File

@@ -0,0 +1,133 @@
<?php
declare(strict_types=1);
namespace Siteworxpro\App;
use League\Route\Http\Exception\MethodNotAllowedException;
use League\Route\Http\Exception\NotFoundException;
use League\Route\Router;
use Nyholm\Psr7\Factory\Psr17Factory;
use Siteworxpro\App\Controllers\HealthcheckController;
use Siteworxpro\App\Controllers\IndexController;
use Siteworxpro\App\Http\JsonResponseFactory;
use Siteworxpro\App\Http\Middleware\CorsMiddleware;
use Siteworxpro\App\Http\Middleware\JwtMiddleware;
use Siteworxpro\App\Http\Middleware\ScopeMiddleware;
use Siteworxpro\App\Services\Facades\Config;
use Siteworxpro\App\Services\Facades\Logger;
use Siteworxpro\HttpStatus\CodesEnum;
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 Api
{
/**
* @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;
/**
* @throws \ReflectionException
*/
public function __construct()
{
Kernel::boot();
$this->registerRoutes();
}
/**
* 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
*/
public function registerRoutes(): void
{
$this->worker = new PSR7Worker(
Worker::create(),
new Psr17Factory(),
new Psr17Factory(),
new Psr17Factory()
);
$this->router = new Router();
$this->router->get('/', IndexController::class . '::get');
$this->router->get('/healthz', HealthcheckController::class . '::get');
$this->router->middleware(new CorsMiddleware());
$this->router->middleware(new JwtMiddleware());
$this->router->middleware(new ScopeMiddleware());
}
/**
* 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'],
CodesEnum::NOT_FOUND
)
);
} 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, CodesEnum::INTERNAL_SERVER_ERROR)
);
}
}
}
}