mirror of
				https://github.com/invoiceninja/invoiceninja.git
				synced 2025-10-31 17:57:33 -04:00 
			
		
		
		
	
		
			
				
	
	
		
			125 lines
		
	
	
		
			3.2 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
			
		
		
	
	
			125 lines
		
	
	
		
			3.2 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
| <?php
 | |
| 
 | |
| namespace App\Http\Middleware;
 | |
| 
 | |
| use Closure;
 | |
| use Illuminate\Cache\RateLimiter;
 | |
| use Illuminate\Redis\Limiters\DurationLimiter;
 | |
| use Illuminate\Routing\Middleware\ThrottleRequests;
 | |
| 
 | |
| class ThrottleRequestsWithPredis extends ThrottleRequests
 | |
| {
 | |
|     /**
 | |
|      * The Redis factory implementation.
 | |
|      *
 | |
|      * @var \Illuminate\Contracts\Redis\Factory
 | |
|      */
 | |
|     protected $redis;
 | |
| 
 | |
|     /**
 | |
|      * The timestamp of the end of the current duration by key.
 | |
|      *
 | |
|      * @var array
 | |
|      */
 | |
|     public $decaysAt = [];
 | |
| 
 | |
|     /**
 | |
|      * The number of remaining slots by key.
 | |
|      *
 | |
|      * @var array
 | |
|      */
 | |
|     public $remaining = [];
 | |
| 
 | |
|     /**
 | |
|      * Create a new request throttler.
 | |
|      *
 | |
|      * @param  \Illuminate\Cache\RateLimiter  $limiter
 | |
|      * @return void
 | |
|      */
 | |
|     public function __construct(RateLimiter $limiter)
 | |
|     {
 | |
|         parent::__construct($limiter);
 | |
| 
 | |
|         $this->redis = \Illuminate\Support\Facades\Redis::connection('sentinel-cache');
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Handle an incoming request.
 | |
|      *
 | |
|      * @param  \Illuminate\Http\Request  $request
 | |
|      * @param  \Closure  $next
 | |
|      * @param  array  $limits
 | |
|      * @return \Symfony\Component\HttpFoundation\Response
 | |
|      *
 | |
|      * @throws \Illuminate\Http\Exceptions\ThrottleRequestsException
 | |
|      */
 | |
|     protected function handleRequest($request, Closure $next, array $limits)
 | |
|     {
 | |
|         foreach ($limits as $limit) {
 | |
|             if ($this->tooManyAttempts($limit->key, $limit->maxAttempts, $limit->decayMinutes)) {
 | |
|                 throw $this->buildException($request, $limit->key, $limit->maxAttempts, $limit->responseCallback);
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         $response = $next($request);
 | |
| 
 | |
|         foreach ($limits as $limit) {
 | |
|             $response = $this->addHeaders(
 | |
|                 $response,
 | |
|                 $limit->maxAttempts,
 | |
|                 $this->calculateRemainingAttempts($limit->key, $limit->maxAttempts)
 | |
|             );
 | |
|         }
 | |
| 
 | |
|         return $response;
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Determine if the given key has been "accessed" too many times.
 | |
|      *
 | |
|      * @param  string  $key
 | |
|      * @param  int  $maxAttempts
 | |
|      * @param  int  $decayMinutes
 | |
|      * @return mixed
 | |
|      */
 | |
|     protected function tooManyAttempts($key, $maxAttempts, $decayMinutes)
 | |
|     {
 | |
|         $limiter = new DurationLimiter(
 | |
|             $this->redis,
 | |
|             $key,
 | |
|             $maxAttempts,
 | |
|             $decayMinutes * 60
 | |
|         );
 | |
| 
 | |
|         return tap(! $limiter->acquire(), function () use ($key, $limiter) {
 | |
|             [$this->decaysAt[$key], $this->remaining[$key]] = [
 | |
|                 $limiter->decaysAt, $limiter->remaining,
 | |
|             ];
 | |
|         });
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Calculate the number of remaining attempts.
 | |
|      *
 | |
|      * @param  string  $key
 | |
|      * @param  int  $maxAttempts
 | |
|      * @param  int|null  $retryAfter
 | |
|      * @return int
 | |
|      */
 | |
|     protected function calculateRemainingAttempts($key, $maxAttempts, $retryAfter = null)
 | |
|     {
 | |
|         return is_null($retryAfter) ? $this->remaining[$key] : 0;
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Get the number of seconds until the lock is released.
 | |
|      *
 | |
|      * @param  string  $key
 | |
|      * @return int
 | |
|      */
 | |
|     protected function getTimeUntilNextRetry($key)
 | |
|     {
 | |
|         return $this->decaysAt[$key] - $this->currentTime();
 | |
|     }
 | |
| }
 |