leeds_backend/src/Middleware/RateLimitMiddleware.php
2026-02-08 17:41:26 -03:00

71 lines
1.5 KiB
PHP

<?php
declare(strict_types=1);
namespace Bass\Webclient\Middleware;
use Bass\Webclient\Libs\ResponseLib;
use Psr\Http\Message\ServerRequestInterface;
use React\Http\Message\Response;
class RateLimitMiddleware
{
private int $maxRequests;
private int $windowSeconds;
private array $limits = [];
public function __construct(int $maxRequests = 30, int $windowSeconds = 60)
{
$this->maxRequests = $maxRequests;
$this->windowSeconds = $windowSeconds;
}
public function __invoke(
ServerRequestInterface $request,
callable $next
): Response {
$apiKey = $request->getHeaderLine('X-API-KEY');
if (!$apiKey) {
return ResponseLib::sendFail(
'Missing API key',
401,
['code' => 'MISSING_API_KEY']
);
}
// 🔒 rate-limit por API key (em memória)
if (!$this->allowRequest($apiKey)) {
return ResponseLib::sendFail(
'Rate limit exceeded',
429,
[
'code' => 'RATE_LIMIT_EXCEEDED',
'retry_after' => $this->windowSeconds
]
);
}
return $next($request);
}
/**
* In-memory rate-limit control
* (per process, per API key)
*/
private function allowRequest(string $key): bool
{
$now = time();
if (!isset($this->limits[$key]) || $now > $this->limits[$key]['reset']) {
$this->limits[$key] = [
'count' => 0,
'reset' => $now + $this->windowSeconds
];
}
$this->limits[$key]['count']++;
return $this->limits[$key]['count'] <= $this->maxRequests;
}
}