weitere Bearibeitung

This commit is contained in:
2026-06-17 16:45:14 +02:00
parent 06645f1e9c
commit 1891ec0a51
38 changed files with 2720 additions and 109 deletions
+65 -31
View File
@@ -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);