Update: 2026-06-16 02:14:34
This commit is contained in:
@@ -29,7 +29,8 @@ class RateLimiter
|
||||
public function check(string $identifier, string $type = 'api'): bool
|
||||
{
|
||||
if (!$this->redis) {
|
||||
return true; // بدون Redis نمرر (fallback)
|
||||
// HIGH-01 FIX: fallback مع ملف بدلاً من تمرير كل الطلبات
|
||||
return $this->fileBasedCheck($identifier, $type);
|
||||
}
|
||||
|
||||
$limit = self::LIMITS[$type] ?? self::LIMITS['api'];
|
||||
@@ -77,6 +78,47 @@ class RateLimiter
|
||||
{
|
||||
if ($this->redis) {
|
||||
$this->redis->del("rate:{$type}:{$identifier}");
|
||||
} else {
|
||||
// HIGH-01: مسح ملف الفل باك عند إعادة التعيين
|
||||
$key = self::sanitizeKey("rate:{$type}:{$identifier}");
|
||||
$tmpFile = sys_get_temp_dir() . "/rate_{$key}.json";
|
||||
if (file_exists($tmpFile)) {
|
||||
@unlink($tmpFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ── Fallback باستخدام ملفات مؤقتة عند تعطل Redis ───────────
|
||||
private function fileBasedCheck(string $identifier, string $type): bool
|
||||
{
|
||||
$limit = self::LIMITS[$type] ?? self::LIMITS['api'];
|
||||
$window = $limit['window'];
|
||||
$max = $limit['requests'];
|
||||
|
||||
$key = self::sanitizeKey("rate:{$type}:{$identifier}");
|
||||
$tmpFile = sys_get_temp_dir() . "/rate_{$key}.json";
|
||||
$now = time();
|
||||
|
||||
$data = [];
|
||||
if (file_exists($tmpFile)) {
|
||||
$data = json_decode(file_get_contents($tmpFile), true) ?: [];
|
||||
}
|
||||
|
||||
// تنظيف النوافذ القديمة
|
||||
$data = array_filter($data, fn($ts) => $ts > ($now - $window));
|
||||
|
||||
if (count($data) >= $max) {
|
||||
error_log("[RATE_LIMIT_FB] File-based block: $identifier | type: $type");
|
||||
return false;
|
||||
}
|
||||
|
||||
$data[] = $now;
|
||||
file_put_contents($tmpFile, json_encode($data));
|
||||
return true;
|
||||
}
|
||||
|
||||
private static function sanitizeKey(string $key): string
|
||||
{
|
||||
return preg_replace('/[^a-zA-Z0-9_\-:]/', '_', $key);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user