83 lines
3.1 KiB
PHP
83 lines
3.1 KiB
PHP
<?php
|
|
// ============================================================
|
|
// core/Auth/RateLimiter.php
|
|
// Sliding Window Rate Limiting باستخدام Redis
|
|
// ============================================================
|
|
|
|
class RateLimiter
|
|
{
|
|
private ?Redis $redis;
|
|
|
|
// حدود مختلفة لكل نوع endpoint
|
|
private const LIMITS = [
|
|
'login' => ['requests' => 5, 'window' => 60], // 5 محاولات / دقيقة
|
|
'otp' => ['requests' => 3, 'window' => 300], // 3 محاولات / 5 دقائق
|
|
'register' => ['requests' => 3, 'window' => 3600], // 3 محاولات / ساعة
|
|
'api' => ['requests' => 120, 'window' => 60], // 120 طلب / دقيقة
|
|
'ride' => ['requests' => 30, 'window' => 60], // 30 طلب / دقيقة
|
|
'upload' => ['requests' => 10, 'window' => 300], // 10 رفع / 5 دقائق
|
|
];
|
|
|
|
public function __construct(?Redis $redis)
|
|
{
|
|
$this->redis = $redis;
|
|
}
|
|
|
|
// ── فحص الحد ─────────────────────────────────────────────
|
|
// $identifier: IP:userId أو IP فقط
|
|
// $type: login | otp | api | ride | upload
|
|
public function check(string $identifier, string $type = 'api'): bool
|
|
{
|
|
if (!$this->redis) {
|
|
return true; // بدون Redis نمرر (fallback)
|
|
}
|
|
|
|
$limit = self::LIMITS[$type] ?? self::LIMITS['api'];
|
|
$window = $limit['window'];
|
|
$max = $limit['requests'];
|
|
|
|
$key = "rate:{$type}:{$identifier}";
|
|
$current = $this->redis->incr($key);
|
|
|
|
if ($current === 1) {
|
|
$this->redis->expire($key, $window);
|
|
}
|
|
|
|
return $current <= $max;
|
|
}
|
|
|
|
// ── تطبيق الحد وإيقاف الطلب إن تجاوز ─────────────────────
|
|
public function enforce(string $identifier, string $type = 'api'): void
|
|
{
|
|
if (!$this->check($identifier, $type)) {
|
|
$limit = self::LIMITS[$type] ?? self::LIMITS['api'];
|
|
$window = $limit['window'];
|
|
|
|
error_log("[RATE_LIMIT] Blocked: $identifier | type: $type");
|
|
|
|
http_response_code(429);
|
|
header("Retry-After: $window");
|
|
echo json_encode([
|
|
'error' => 'Too many requests. Please slow down.',
|
|
'retry_after' => $window,
|
|
]);
|
|
exit;
|
|
}
|
|
}
|
|
|
|
// ── بناء معرّف المستخدم ────────────────────────────────────
|
|
public static function identifier(?string $userId = null): string
|
|
{
|
|
$ip = $_SERVER['REMOTE_ADDR'] ?? 'unknown';
|
|
return $userId ? "{$ip}:{$userId}" : $ip;
|
|
}
|
|
|
|
// ── إعادة تعيين عداد (مثلاً بعد تسجيل دخول ناجح) ───────────
|
|
public function reset(string $identifier, string $type = 'login'): void
|
|
{
|
|
if ($this->redis) {
|
|
$this->redis->del("rate:{$type}:{$identifier}");
|
|
}
|
|
}
|
|
}
|