mirror of
				https://github.com/invoiceninja/invoiceninja.git
				synced 2025-11-03 18:57:34 -05: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();
 | 
						|
    }
 | 
						|
}
 |