- move elixForms API logic to specific folder
- move everything else under Api folder
This commit is contained in:
2026-07-13 17:34:43 +02:00
parent 1766057cb9
commit 661e2db8b1
27 changed files with 294 additions and 129 deletions
+6 -4
View File
@@ -11,7 +11,7 @@ use Core\RateLimiter\FileRateLimiter;
use Core\RateLimiter\InMemoryRateLimiter;
use Psr\Log\LoggerInterface;
use Core\LoggerFactory;
use Services\ExternalApiService;
use ElixForms\ElixFormsClient;
$container = new Container();
@@ -51,9 +51,11 @@ $container->singleton(LoggerInterface::class, function($c) {
return LoggerFactory::create($config->get('log_ident', 'api'));
});
// External API service binding
$container->singleton(ExternalApiService::class, function($c) {
return new ExternalApiService();
// ElixForms client binding
$container->singleton(ElixFormsClient::class, function($c) {
$config = $c->make(Config::class);
$baseUrl = $config->get('elixforms_api_base_url');
return new ElixFormsClient($baseUrl);
});
// If you have other services, bind them here, for example:
+6 -4
View File
@@ -10,10 +10,12 @@
},
"autoload": {
"psr-4": {
"Core\\": "src/Core/",
"Controllers\\": "src/Controllers/",
"Services\\": "src/Services/",
"Helpers\\": "src/Helpers/"
"Core\\": "src/Api/Core/",
"Controllers\\": "src/Api/Controllers/",
"Services\\": "src/Api/Services/",
"Helpers\\": "src/Api/Helpers/",
"Api\\": "src/Api/",
"ElixForms\\": "src/ElixForms/"
}
},
"autoload-dev": {
+1 -1
View File
@@ -1,6 +1,6 @@
<?php
return [
'external_api_base_url' => 'https://api.example.com',
'elixforms_api_base_url' => 'https://api.example.com',
'rate_limiter_driver' => 'file', // o 'memory'
// configuration for file-based rate limiter
'rate_limit_storage_dir' => sys_get_temp_dir() . '/api_rate_limit',
+5 -3
View File
@@ -1,6 +1,8 @@
<?php
return [
'external_api_username' => 'myUser',
'external_api_password' => 'myPass',
'external_api_token' => 'mySecretToken',
// elixforms API credentials
'elixforms_api_username' => 'myUser',
'elixforms_api_password' => 'myPass',
'elixforms_api_token' => 'mySecretToken',
'api_access_token' => 'myApiAccessToken',
];
+18 -2
View File
@@ -1,6 +1,7 @@
<?php
$container = require __DIR__ . '/../bootstrap.php';
use Api\Auth\ApiTokenAuthenticator;
use Core\Router;
use Core\Request;
use Core\Response;
@@ -28,13 +29,28 @@ $limiter = $container->make(RateLimiterInterface::class);
$router = new Router($container); // vedi nota: router può ricevere container
$request = new Request();
$response = new Response();
// Autenticazione separata per le API esterne
if (strpos($request->path(), '/api/') === 0) {
try {
$authenticator = $container->make(ApiTokenAuthenticator::class);
$authenticator->authenticate($request);
} catch (\Throwable $e) {
$logger->warning('External API auth failed', ['path' => $request->path(), 'error' => $e->getMessage()]);
$response->json(['error' => 'Unauthorized'], 401);
}
}
// register routes (path without version prefix)
$router->get('/users', 'UsersController@index');
$router->post('/users', 'UsersController@create');
$router->get('/example', 'ExampleController@test');
$request = new Request();
$response = new Response();
// external API routes
$router->get('/api/elixforms/status', 'Api\\Controllers\\ExternalElixFormsController@status');
$router->post('/api/elixforms/login', 'Api\\Controllers\\ExternalElixFormsController@login');
// Rate limiting by IP address
$key = $_SERVER['REMOTE_ADDR'] ?? 'unknown';
+34
View File
@@ -0,0 +1,34 @@
<?php
namespace Api\Auth;
use Core\Config;
use Core\Request;
class ApiTokenAuthenticator
{
private Config $config;
public function __construct(Config $config)
{
$this->config = $config;
}
public function authenticate(Request $request): void
{
$authorization = $_SERVER['HTTP_AUTHORIZATION'] ?? $_SERVER['REDIRECT_HTTP_AUTHORIZATION'] ?? '';
if (!$authorization) {
throw new \Exception('Missing Authorization header');
}
if (!preg_match('/^Bearer\s+(.*)$/i', trim($authorization), $matches)) {
throw new \Exception('Invalid Authorization header format');
}
$token = $matches[1];
$expected = $this->config->secret('api_access_token');
if (empty($expected) || !hash_equals((string) $expected, (string) $token)) {
throw new \Exception('Invalid API access token');
}
}
}
+26
View File
@@ -0,0 +1,26 @@
<?php
namespace Controllers;
use Core\Request;
use Core\Response;
use ElixForms\ElixFormsClient;
class ExampleController
{
private ElixFormsClient $elixFormsClient;
public function __construct(ElixFormsClient $elixFormsClient)
{
$this->elixFormsClient = $elixFormsClient;
}
public function test(Request $req, Response $res)
{
$response = $this->elixFormsClient->request('GET', '/status');
return $res->json([
'external_api_response' => $response['json'] ?? $response['body'],
'status' => $response['status']
]);
}
}
@@ -0,0 +1,44 @@
<?php
namespace Api\Controllers;
use Core\Request;
use Core\Response;
use ElixForms\ElixFormsClient;
use ElixForms\Exceptions\ElixFormsException;
class ExternalElixFormsController
{
private ElixFormsClient $elixFormsClient;
public function __construct(ElixFormsClient $elixFormsClient)
{
$this->elixFormsClient = $elixFormsClient;
}
public function status(Request $req, Response $res)
{
try {
$response = $this->elixFormsClient->request('GET', '/status');
return $res->json([
'status' => $response['status'],
'body' => $response['json'] ?? $response['body']
]);
} catch (ElixFormsException $e) {
return $res->json(['error' => $e->getMessage()], 500);
}
}
public function login(Request $req, Response $res)
{
$data = $req->body();
$username = $data['username'] ?? '';
$password = $data['password'] ?? '';
try {
$token = $this->elixFormsClient->auth()->login($username, $password);
return $res->json(['authToken' => $token]);
} catch (ElixFormsException $e) {
return $res->json(['error' => $e->getMessage()], 401);
}
}
}
@@ -17,4 +17,10 @@ class Request {
public function query() {
return $_GET;
}
public function header(string $name): ?string
{
$key = 'HTTP_' . strtoupper(str_replace('-', '_', $name));
return $_SERVER[$key] ?? null;
}
}
@@ -17,8 +17,8 @@ class ExternalApiService
public function getStatus()
{
$url = rtrim($this->config->get('external_api_base_url'), '/') . '/status';
$token = $this->config->secret('external_api_token');
$url = rtrim($this->config->get('elixforms_api_base_url'), '/') . '/status';
$token = $this->config->secret('elixforms_api_token');
$headers = ['Authorization' => 'Bearer ' . $token];
return $this->httpClient->get($url, $headers);
}
-33
View File
@@ -1,33 +0,0 @@
<?php
namespace Controllers;
use Core\Request;
use Core\Response;
use Core\Config;
use Core\HttpClient;
class ExampleController
{
private $config;
private $httpClient;
public function __construct(Config $config, HttpClient $httpClient)
{
$this->config = $config;
$this->httpClient = $httpClient;
}
public function test(Request $req, Response $res)
{
$url = $this->config->get('external_api_base_url') . '/status';
$response = $this->httpClient->get($url, [
'Authorization' => 'Bearer ' . $this->config->secret('external_api_token')
]);
return $res->json([
'external_api_response' => $response
]);
}
}
@@ -0,0 +1,87 @@
<?php
namespace ElixForms\Auth;
use ElixForms\Exceptions\ElixFormsException;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\GuzzleException;
class ElixFormsAuthenticationClient {
private $baseUrl;
private $httpClient;
public function __construct(string $baseUrl, Client $httpClient = null) {
$this->baseUrl = rtrim($baseUrl, '/');
$this->httpClient = $httpClient ?? new Client(['timeout' => 10.0, 'http_errors' => false]);
}
/**
* Esegue il login verso l'API di elixForms.
*
* @param string $username
* @param string $password
* @return string Il token di autenticazione (authToken)
* @throws ElixFormsException Se le credenziali sono errate o c'è un errore server
*/
public function login(string $username, string $password): string {
$url = $this->baseUrl . '/eF/services/api/authentication/login/v1';
try {
$response = $this->httpClient->post($url, [
'form_params' => [
'username' => $username,
'password' => $password
]
]);
$status = $response->getStatusCode();
if ($status !== 200) {
throw new ElixFormsException("Errore durante il login elixForms. HTTP Status: " . $status);
}
$body = $response->getBody()->getContents();
$json = json_decode($body, true);
if (json_last_error() !== JSON_ERROR_NONE || empty($json)) {
throw new ElixFormsException("Risposta non valida dal server elixForms: atteso JSON.");
}
if (isset($json['value']['authToken'])) {
return $json['value']['authToken'];
}
throw new ElixFormsException("authToken non trovato nella risposta del login elixForms.");
} catch (GuzzleException $e) {
throw new ElixFormsException("Errore di connessione al server elixForms: " . $e->getMessage(), 0, $e);
}
}
/**
* Effettua il logout invalidando il token sul server elixForms.
*
* @param string $username
* @param string $token
* @return bool True se il logout ha successo
* @throws ElixFormsException Se c'è un errore durante il logout
*/
public function logout(string $username, string $token): bool {
$url = $this->baseUrl . '/eF/services/api/authentication/' . urlencode($username) . '/logout/v1';
try {
$response = $this->httpClient->post($url, [
'headers' => [
'Authorization' => 'Bearer ' . $token,
'Content-Type' => 'application/x-www-form-urlencoded'
]
]);
$status = $response->getStatusCode();
if ($status !== 200) {
throw new ElixFormsException("Errore durante il logout elixForms. HTTP Status: " . $status);
}
return true;
} catch (GuzzleException $e) {
throw new ElixFormsException("Errore di connessione al server elixForms: " . $e->getMessage(), 0, $e);
}
}
}
+52
View File
@@ -0,0 +1,52 @@
<?php
namespace ElixForms;
use ElixForms<Auth\ElixFormsAuthenticationClient as AuthClient;
use ElixForms\Exceptions\ElixFormsException;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\GuzzleException;
class ElixFormsClient
{
private string $baseUrl;
private Client $httpClient;
public function __construct(string $baseUrl, Client $httpClient = null)
{
$this->baseUrl = rtrim($baseUrl, '/');
$this->httpClient = $httpClient ?? new Client(['timeout' => 10.0, 'http_errors' => false]);
}
public function auth(): AuthClient
{
return new AuthClient($this->baseUrl, $this->httpClient);
}
public function request(string $method, string $path, ?array $body = null, array $headers = []): array
{
$url = $this->baseUrl . '/' . ltrim($path, '/');
$options = ['headers' => $headers];
if ($body !== null) {
$options['json'] = $body;
}
try {
$response = $this->httpClient->request($method, $url, $options);
$status = $response->getStatusCode();
$bodyRaw = $response->getBody()->getContents();
$contentType = $response->getHeaderLine('Content-Type');
$isJson = stripos($contentType, 'application/json') !== false;
return [
'status' => $status,
'headers' => $response->getHeaders(),
'body' => $bodyRaw,
'is_json' => $isJson,
'json' => $isJson ? json_decode($bodyRaw, true) : null,
];
} catch (GuzzleException $e) {
throw new ElixFormsException('Errore di connessione al server elixForms: ' . $e->getMessage(), 0, $e);
}
}
}
@@ -0,0 +1,7 @@
<?php
namespace ElixForms\Exceptions;
use Exception;
class ElixFormsException extends Exception {
}
@@ -1,80 +0,0 @@
<?php
namespace Services;
use Core\Config;
use Core\HttpClient;
use Core\ElixFormsException;
class ElixFormsAuthenticationClient {
private $config;
private $httpClient;
public function __construct(Config $config, HttpClient $httpClient) {
$this->config = $config;
$this->httpClient = $httpClient;
}
/**
* Esegue il login verso l'API di elixForms.
*
* @param string $username
* @param string $password
* @return string Il token di autenticazione (authToken)
* @throws ElixFormsException Se le credenziali sono errate o c'è un errore server
*/
public function login(string $username, string $password): string {
$baseUrl = rtrim($this->config->get('elixforms_api_base_url'), '/');
$url = $baseUrl . '/eF/services/api/authentication/login/v1';
$data = [
'username' => $username,
'password' => $password
];
// L'API supporta application/json
$response = $this->httpClient->post($url, $data);
if ($response['status'] !== 200) {
throw new ElixFormsException("Errore durante il login elixForms. HTTP Status: " . $response['status']);
}
if (!$response['is_json'] || empty($response['json'])) {
throw new ElixFormsException("Risposta non valida dal server elixForms: atteso JSON.");
}
$json = $response['json'];
// Estraiamo il token dalla struttura complessa
if (isset($json['value']['authToken'])) {
return $json['value']['authToken'];
}
throw new ElixFormsException("authToken non trovato nella risposta del login elixForms.");
}
/**
* Effettua il logout invalidando il token sul server elixForms.
*
* @param string $username
* @param string $token
* @return bool True se il logout ha successo
* @throws ElixFormsException Se c'è un errore durante il logout
*/
public function logout(string $username, string $token): bool {
$baseUrl = rtrim($this->config->get('elixforms_api_base_url'), '/');
$url = $baseUrl . '/eF/services/api/authentication/' . urlencode($username) . '/logout/v1';
$headers = [
'Authorization' => 'Bearer ' . $token,
'Content-Type' => 'application/x-www-form-urlencoded'
];
$response = $this->httpClient->post($url, null, $headers);
if ($response['status'] !== 200) {
throw new ElixFormsException("Errore durante il logout elixForms. HTTP Status: " . $response['status']);
}
return true;
}
}