weitere Bearibeitung
This commit is contained in:
@@ -6,6 +6,7 @@ use DateTimeImmutable;
|
||||
use DateTimeZone;
|
||||
use PDO;
|
||||
use RuntimeException;
|
||||
use Throwable;
|
||||
|
||||
final class RateLimiterService
|
||||
{
|
||||
@@ -43,45 +44,69 @@ final class RateLimiterService
|
||||
$nowTs = time();
|
||||
$now = $this->formatTimestamp($nowTs);
|
||||
|
||||
$isSqlite = $this->isSqlite();
|
||||
$this->pdo->beginTransaction();
|
||||
|
||||
try {
|
||||
$bootstrapStatement = $this->pdo->prepare(
|
||||
'INSERT INTO rate_limits (
|
||||
action_key,
|
||||
bucket_key,
|
||||
attempts,
|
||||
window_start,
|
||||
last_attempt_at,
|
||||
blocked_until,
|
||||
created_at
|
||||
) VALUES (
|
||||
:action_key,
|
||||
:bucket_key,
|
||||
0,
|
||||
:window_start,
|
||||
:last_attempt_at,
|
||||
NULL,
|
||||
:created_at
|
||||
)
|
||||
ON DUPLICATE KEY UPDATE
|
||||
action_key = action_key'
|
||||
);
|
||||
$bootstrapStatement->execute([
|
||||
'action_key' => $policy['action'],
|
||||
'bucket_key' => $bucketKey,
|
||||
'window_start' => $now,
|
||||
'last_attempt_at' => $now,
|
||||
'created_at' => $now,
|
||||
]);
|
||||
if ($isSqlite) {
|
||||
$existingStmt = $this->pdo->prepare(
|
||||
'SELECT id FROM rate_limits WHERE action_key = :action_key AND bucket_key = :bucket_key LIMIT 1'
|
||||
);
|
||||
$existingStmt->execute([
|
||||
'action_key' => $policy['action'],
|
||||
'bucket_key' => $bucketKey,
|
||||
]);
|
||||
|
||||
if (!$existingStmt->fetchColumn()) {
|
||||
$this->pdo->prepare(
|
||||
'INSERT INTO rate_limits (action_key, bucket_key, attempts, window_start, last_attempt_at, created_at)
|
||||
VALUES (:action_key, :bucket_key, 0, :window_start, :last_attempt_at, :created_at)'
|
||||
)->execute([
|
||||
'action_key' => $policy['action'],
|
||||
'bucket_key' => $bucketKey,
|
||||
'window_start' => $now,
|
||||
'last_attempt_at' => $now,
|
||||
'created_at' => $now,
|
||||
]);
|
||||
}
|
||||
} else {
|
||||
$bootstrapStatement = $this->pdo->prepare(
|
||||
'INSERT INTO rate_limits (
|
||||
action_key,
|
||||
bucket_key,
|
||||
attempts,
|
||||
window_start,
|
||||
last_attempt_at,
|
||||
blocked_until,
|
||||
created_at
|
||||
) VALUES (
|
||||
:action_key,
|
||||
:bucket_key,
|
||||
0,
|
||||
:window_start,
|
||||
:last_attempt_at,
|
||||
NULL,
|
||||
:created_at
|
||||
)
|
||||
ON DUPLICATE KEY UPDATE
|
||||
action_key = action_key'
|
||||
);
|
||||
$bootstrapStatement->execute([
|
||||
'action_key' => $policy['action'],
|
||||
'bucket_key' => $bucketKey,
|
||||
'window_start' => $now,
|
||||
'last_attempt_at' => $now,
|
||||
'created_at' => $now,
|
||||
]);
|
||||
}
|
||||
|
||||
$forUpdate = $isSqlite ? '' : ' FOR UPDATE';
|
||||
$selectStatement = $this->pdo->prepare(
|
||||
'SELECT attempts, window_start, last_attempt_at, blocked_until
|
||||
FROM rate_limits
|
||||
WHERE action_key = :action_key
|
||||
AND bucket_key = :bucket_key
|
||||
LIMIT 1
|
||||
FOR UPDATE'
|
||||
LIMIT 1' . $forUpdate
|
||||
);
|
||||
$selectStatement->execute([
|
||||
'action_key' => $policy['action'],
|
||||
@@ -136,7 +161,7 @@ final class RateLimiterService
|
||||
}
|
||||
|
||||
$this->pdo->commit();
|
||||
} catch (\Throwable $throwable) {
|
||||
} catch (Throwable $throwable) {
|
||||
if ($this->pdo->inTransaction()) {
|
||||
$this->pdo->rollBack();
|
||||
}
|
||||
@@ -239,6 +264,15 @@ final class RateLimiterService
|
||||
];
|
||||
}
|
||||
|
||||
private function isSqlite(): bool
|
||||
{
|
||||
try {
|
||||
return $this->pdo->getAttribute(PDO::ATTR_DRIVER_NAME) === 'sqlite';
|
||||
} catch (\Throwable) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private function bucketKey(string $scope): string
|
||||
{
|
||||
$scope = trim($scope);
|
||||
|
||||
Reference in New Issue
Block a user