Allow Redis to be disabled in favor of flatfile cache.
This commit is contained in:
parent
3e8f90151f
commit
edb1839cbc
|
@ -54,6 +54,10 @@ ENV LANG="en_US.UTF-8" \
|
|||
MYSQL_USER="azuracast" \
|
||||
MYSQL_PASSWORD="azur4c457" \
|
||||
MYSQL_DATABASE="azuracast" \
|
||||
ENABLE_REDIS="true" \
|
||||
REDIS_HOST="redis" \
|
||||
REDIS_PORT=6379 \
|
||||
REDIS_DB=1 \
|
||||
PREFER_RELEASE_BUILDS="false" \
|
||||
COMPOSER_PLUGIN_MODE="false" \
|
||||
ADDITIONAL_MEDIA_SYNC_WORKER_COUNT=0 \
|
||||
|
|
|
@ -85,6 +85,10 @@ MYSQL_MAX_CONNECTIONS=100
|
|||
# Do not modify these fields if you are using the standard AzuraCast Redis host.
|
||||
#
|
||||
|
||||
# Whether to use the Redis cache; if set to false, will disable Redis and use flatfile cache instead.
|
||||
# Default: true
|
||||
# ENABLE_REDIS=true
|
||||
|
||||
# Name of the Redis host.
|
||||
# Default: redis
|
||||
# REDIS_HOST=redis
|
||||
|
|
|
@ -116,10 +116,16 @@ class UptimeWait
|
|||
|
||||
protected function checkRedis(): bool
|
||||
{
|
||||
$enableRedis = $this->envToBool($_ENV['ENABLE_REDIS'] ?? true);
|
||||
$redisHost = $_ENV['REDIS_HOST'] ?? 'redis';
|
||||
$redisPort = (int)($_ENV['REDIS_PORT'] ?? 6379);
|
||||
$redisDb = (int)($_ENV['REDIS_DB'] ?? 1);
|
||||
|
||||
if (!$enableRedis) {
|
||||
$this->println('Redis disabled; skipping Redis check...');
|
||||
return true;
|
||||
}
|
||||
|
||||
try {
|
||||
$redis = new Redis();
|
||||
$redis->connect($redisHost, $redisPort, 15);
|
||||
|
@ -137,6 +143,17 @@ class UptimeWait
|
|||
}
|
||||
}
|
||||
|
||||
protected function envToBool(string|bool $value): bool
|
||||
{
|
||||
if (is_bool($value)) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
return str_starts_with(strtolower($value), 'y')
|
||||
|| 'true' === strtolower($value)
|
||||
|| '1' === $value;
|
||||
}
|
||||
|
||||
protected function println(string $line): void
|
||||
{
|
||||
echo $line . "\n";
|
||||
|
|
|
@ -72,6 +72,7 @@
|
|||
"symfony/messenger": "^5",
|
||||
"symfony/process": "^5",
|
||||
"symfony/property-access": "^5",
|
||||
"symfony/rate-limiter": "^5.3",
|
||||
"symfony/redis-messenger": "^5",
|
||||
"symfony/serializer": "^5",
|
||||
"symfony/validator": "^5",
|
||||
|
|
153
composer.lock
generated
153
composer.lock
generated
|
@ -4,7 +4,7 @@
|
|||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "c1a93d3233ba7369caefc4d1a25d4118",
|
||||
"content-hash": "ff6df4489010493b9a43e5f2f41a7124",
|
||||
"packages": [
|
||||
{
|
||||
"name": "aws/aws-sdk-php",
|
||||
|
@ -7335,6 +7335,75 @@
|
|||
],
|
||||
"time": "2021-05-26T17:43:10+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/options-resolver",
|
||||
"version": "v5.3.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/options-resolver.git",
|
||||
"reference": "162e886ca035869866d233a2bfef70cc28f9bbe5"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/options-resolver/zipball/162e886ca035869866d233a2bfef70cc28f9bbe5",
|
||||
"reference": "162e886ca035869866d233a2bfef70cc28f9bbe5",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.2.5",
|
||||
"symfony/deprecation-contracts": "^2.1",
|
||||
"symfony/polyfill-php73": "~1.0",
|
||||
"symfony/polyfill-php80": "^1.15"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Symfony\\Component\\OptionsResolver\\": ""
|
||||
},
|
||||
"exclude-from-classmap": [
|
||||
"/Tests/"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Fabien Potencier",
|
||||
"email": "fabien@symfony.com"
|
||||
},
|
||||
{
|
||||
"name": "Symfony Community",
|
||||
"homepage": "https://symfony.com/contributors"
|
||||
}
|
||||
],
|
||||
"description": "Provides an improved replacement for the array_replace PHP function",
|
||||
"homepage": "https://symfony.com",
|
||||
"keywords": [
|
||||
"config",
|
||||
"configuration",
|
||||
"options"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/options-resolver/tree/v5.3.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://symfony.com/sponsor",
|
||||
"type": "custom"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/fabpot",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2021-05-26T17:43:10+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/polyfill-ctype",
|
||||
"version": "v1.23.0",
|
||||
|
@ -7734,6 +7803,76 @@
|
|||
],
|
||||
"time": "2021-05-31T12:40:48+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/rate-limiter",
|
||||
"version": "v5.3.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/rate-limiter.git",
|
||||
"reference": "e9226c91163495ff0b655cdae0fff682e869640b"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/rate-limiter/zipball/e9226c91163495ff0b655cdae0fff682e869640b",
|
||||
"reference": "e9226c91163495ff0b655cdae0fff682e869640b",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.2.5",
|
||||
"symfony/lock": "^5.2",
|
||||
"symfony/options-resolver": "^5.1"
|
||||
},
|
||||
"require-dev": {
|
||||
"psr/cache": "^1.0|^2.0|^3.0"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Symfony\\Component\\RateLimiter\\": ""
|
||||
},
|
||||
"exclude-from-classmap": [
|
||||
"/Tests/"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Wouter de Jong",
|
||||
"email": "wouter@wouterj.nl"
|
||||
},
|
||||
{
|
||||
"name": "Symfony Community",
|
||||
"homepage": "https://symfony.com/contributors"
|
||||
}
|
||||
],
|
||||
"description": "Provides a Token Bucket implementation to rate limit input and output in your application",
|
||||
"homepage": "https://symfony.com",
|
||||
"keywords": [
|
||||
"limiter",
|
||||
"rate-limiter"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/rate-limiter/tree/v5.3.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://symfony.com/sponsor",
|
||||
"type": "custom"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/fabpot",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2021-05-26T17:43:10+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/redis-messenger",
|
||||
"version": "v5.3.0",
|
||||
|
@ -10650,16 +10789,16 @@
|
|||
},
|
||||
{
|
||||
"name": "phpstan/phpstan",
|
||||
"version": "0.12.89",
|
||||
"version": "0.12.90",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/phpstan/phpstan.git",
|
||||
"reference": "54c0f5a6c30511b77128d58b6369f718df250542"
|
||||
"reference": "f0e4b56630fc3d4eb5be86606d07212ac212ede4"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/54c0f5a6c30511b77128d58b6369f718df250542",
|
||||
"reference": "54c0f5a6c30511b77128d58b6369f718df250542",
|
||||
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/f0e4b56630fc3d4eb5be86606d07212ac212ede4",
|
||||
"reference": "f0e4b56630fc3d4eb5be86606d07212ac212ede4",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -10690,7 +10829,7 @@
|
|||
"description": "PHPStan - PHP Static Analysis Tool",
|
||||
"support": {
|
||||
"issues": "https://github.com/phpstan/phpstan/issues",
|
||||
"source": "https://github.com/phpstan/phpstan/tree/0.12.89"
|
||||
"source": "https://github.com/phpstan/phpstan/tree/0.12.90"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
@ -10710,7 +10849,7 @@
|
|||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2021-06-09T20:23:49+00:00"
|
||||
"time": "2021-06-18T07:15:38+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpstan/phpstan-doctrine",
|
||||
|
|
|
@ -134,6 +134,10 @@ return [
|
|||
|
||||
// Redis cache
|
||||
Redis::class => function (Environment $environment) {
|
||||
if (!$environment->enableRedis()) {
|
||||
throw new App\Exception\BootstrapException('Redis is disabled on this installation.');
|
||||
}
|
||||
|
||||
$settings = $environment->getRedisSettings();
|
||||
|
||||
$redis = new Redis();
|
||||
|
@ -148,15 +152,20 @@ return [
|
|||
Psr\Log\LoggerInterface $logger,
|
||||
ContainerInterface $di
|
||||
) {
|
||||
/** @var Symfony\Contracts\Cache\CacheInterface $cacheInterface */
|
||||
if ($environment->isTesting()) {
|
||||
$arrayAdapter = new Symfony\Component\Cache\Adapter\ArrayAdapter();
|
||||
$arrayAdapter->setLogger($logger);
|
||||
return $arrayAdapter;
|
||||
$cacheInterface = new Symfony\Component\Cache\Adapter\ArrayAdapter();
|
||||
} elseif (!$environment->enableRedis()) {
|
||||
$tempDir = $environment->getTempDirectory() . DIRECTORY_SEPARATOR . 'cache';
|
||||
$cacheInterface = new Symfony\Component\Cache\Adapter\FilesystemAdapter(
|
||||
directory: $tempDir
|
||||
);
|
||||
} else {
|
||||
$cacheInterface = new Symfony\Component\Cache\Adapter\RedisAdapter($di->get(Redis::class));
|
||||
}
|
||||
|
||||
$redisAdapter = new Symfony\Component\Cache\Adapter\RedisAdapter($di->get(Redis::class));
|
||||
$redisAdapter->setLogger($logger);
|
||||
return $redisAdapter;
|
||||
$cacheInterface->setLogger($logger);
|
||||
return $cacheInterface;
|
||||
},
|
||||
|
||||
Symfony\Component\Cache\Adapter\AdapterInterface::class => DI\get(
|
||||
|
@ -178,9 +187,23 @@ return [
|
|||
$psr6Cache = new Symfony\Component\Cache\Adapter\ArrayAdapter();
|
||||
}
|
||||
|
||||
$doctrineCache = Doctrine\Common\Cache\Psr6\DoctrineProvider::wrap($psr6Cache);
|
||||
$doctrineCache->setNamespace('doctrine.');
|
||||
return $doctrineCache;
|
||||
$proxyCache = new Symfony\Component\Cache\Adapter\ProxyAdapter($psr6Cache, 'doctrine.');
|
||||
return Doctrine\Common\Cache\Psr6\DoctrineProvider::wrap($proxyCache);
|
||||
},
|
||||
|
||||
// Symfony Lock adapter
|
||||
Symfony\Component\Lock\PersistingStoreInterface::class => function (
|
||||
ContainerInterface $di,
|
||||
Environment $environment
|
||||
) {
|
||||
if ($environment->enableRedis()) {
|
||||
$redis = $di->get(Redis::class);
|
||||
$store = new Symfony\Component\Lock\Store\RedisStore($redis);
|
||||
} else {
|
||||
$store = new Symfony\Component\Lock\Store\FlockStore($environment->getTempDirectory());
|
||||
}
|
||||
|
||||
return $store;
|
||||
},
|
||||
|
||||
// Session save handler middleware
|
||||
|
@ -269,9 +292,11 @@ return [
|
|||
Psr\Cache\CacheItemPoolInterface $psr6Cache,
|
||||
Environment $settings
|
||||
) {
|
||||
$proxyCache = new Symfony\Component\Cache\Adapter\ProxyAdapter($psr6Cache, 'annotations.');
|
||||
|
||||
return new Doctrine\Common\Annotations\PsrCachedReader(
|
||||
new Doctrine\Common\Annotations\AnnotationReader,
|
||||
$psr6Cache,
|
||||
$proxyCache,
|
||||
!$settings->isProduction()
|
||||
);
|
||||
},
|
||||
|
@ -441,12 +466,7 @@ return [
|
|||
)
|
||||
);
|
||||
|
||||
$supervisor = new Supervisor\Supervisor($client, $logger);
|
||||
if (!$supervisor->isConnected()) {
|
||||
throw new \App\Exception(sprintf('Could not connect to supervisord.'));
|
||||
}
|
||||
|
||||
return $supervisor;
|
||||
return new Supervisor\Supervisor($client, $logger);
|
||||
},
|
||||
|
||||
// Image Manager
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
<?php
|
||||
|
||||
namespace App\Console\Command;
|
||||
|
||||
use App\Entity\Repository\SettingsRepository;
|
||||
|
|
|
@ -59,6 +59,7 @@ class Environment
|
|||
public const DB_USER = 'MYSQL_USER';
|
||||
public const DB_PASSWORD = 'MYSQL_PASSWORD';
|
||||
|
||||
public const ENABLE_REDIS = 'ENABLE_REDIS';
|
||||
public const REDIS_HOST = 'REDIS_HOST';
|
||||
public const REDIS_PORT = 'REDIS_PORT';
|
||||
public const REDIS_DB = 'REDIS_DB';
|
||||
|
@ -73,6 +74,8 @@ class Environment
|
|||
|
||||
self::ASSET_URL => '/static',
|
||||
|
||||
self::ENABLE_REDIS => true,
|
||||
|
||||
self::SUPPORTED_LOCALES => [
|
||||
'en_US.UTF-8' => 'English (Default)',
|
||||
'cs_CZ.UTF-8' => 'čeština', // Czech
|
||||
|
@ -99,6 +102,17 @@ class Environment
|
|||
$this->data = array_merge($this->defaults, $elements);
|
||||
}
|
||||
|
||||
protected function envToBool(string|bool $value): bool
|
||||
{
|
||||
if (is_bool($value)) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
return str_starts_with(strtolower($value), 'y')
|
||||
|| 'true' === strtolower($value)
|
||||
|| '1' === $value;
|
||||
}
|
||||
|
||||
public function getAppEnvironment(): string
|
||||
{
|
||||
return $this->data[self::APP_ENV] ?? self::ENV_PRODUCTION;
|
||||
|
@ -121,7 +135,7 @@ class Environment
|
|||
|
||||
public function isDocker(): bool
|
||||
{
|
||||
return (bool)($this->data[self::IS_DOCKER] ?? true);
|
||||
return $this->envToBool($this->data[self::IS_DOCKER] ?? true);
|
||||
}
|
||||
|
||||
public function isCli(): bool
|
||||
|
@ -284,6 +298,11 @@ class Environment
|
|||
];
|
||||
}
|
||||
|
||||
public function enableRedis(): bool
|
||||
{
|
||||
return $this->envToBool($this->data[self::ENABLE_REDIS] ?? true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed[]
|
||||
*/
|
||||
|
@ -298,12 +317,12 @@ class Environment
|
|||
|
||||
public function isProfilingExtensionEnabled(): bool
|
||||
{
|
||||
return (1 === (int)($this->data[self::PROFILING_EXTENSION_ENABLED] ?? 0));
|
||||
return $this->envToBool($this->data[self::PROFILING_EXTENSION_ENABLED] ?? false);
|
||||
}
|
||||
|
||||
public function isProfilingExtensionAlwaysOn(): bool
|
||||
{
|
||||
return (1 === (int)($this->data[self::PROFILING_EXTENSION_ALWAYS_ON] ?? 0));
|
||||
return $this->envToBool($this->data[self::PROFILING_EXTENSION_ALWAYS_ON] ?? false);
|
||||
}
|
||||
|
||||
public function getProfilingExtensionHttpKey(): string
|
||||
|
|
|
@ -3,51 +3,44 @@
|
|||
namespace App;
|
||||
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Redis;
|
||||
use Symfony\Component\Lock\Key;
|
||||
use Symfony\Component\Lock\Lock;
|
||||
use Symfony\Component\Lock\BlockingStoreInterface;
|
||||
use Symfony\Component\Lock\LockFactory as SymfonyLockFactory;
|
||||
use Symfony\Component\Lock\LockInterface;
|
||||
use Symfony\Component\Lock\Store\RedisStore;
|
||||
use Symfony\Component\Lock\PersistingStoreInterface;
|
||||
use Symfony\Component\Lock\Store\RetryTillSaveStore;
|
||||
|
||||
class LockFactory
|
||||
class LockFactory extends SymfonyLockFactory
|
||||
{
|
||||
public function __construct(
|
||||
protected Redis $redis,
|
||||
protected LoggerInterface $logger
|
||||
protected Environment $environment,
|
||||
PersistingStoreInterface $lockStore,
|
||||
LoggerInterface $logger
|
||||
) {
|
||||
if (!$lockStore instanceof BlockingStoreInterface) {
|
||||
$lockStore = new RetryTillSaveStore($lockStore, 30, 1000);
|
||||
$lockStore->setLogger($logger);
|
||||
}
|
||||
|
||||
parent::__construct($lockStore);
|
||||
$this->setLogger($logger);
|
||||
}
|
||||
|
||||
public function createLock(
|
||||
string $resource,
|
||||
?float $ttl = 300.0,
|
||||
bool $autoRelease = true,
|
||||
int $retrySleep = 1000,
|
||||
int $retryCount = 30
|
||||
): LockInterface {
|
||||
$store = new RedisStore($this->redis);
|
||||
$store = new RetryTillSaveStore($store, $retrySleep, $retryCount);
|
||||
$store->setLogger($this->logger);
|
||||
|
||||
$lock = new Lock(new Key($this->getPrefixedResourceName($resource)), $store, $ttl, $autoRelease);
|
||||
$lock->setLogger($this->logger);
|
||||
|
||||
return $lock;
|
||||
public function createLock(string $resource, ?float $ttl = 300.0, bool $autoRelease = true): LockInterface
|
||||
{
|
||||
return parent::createLock($this->getPrefixedResourceName($resource), $ttl, $autoRelease);
|
||||
}
|
||||
|
||||
public function createAndAcquireLock(
|
||||
string $resource,
|
||||
?float $ttl = 300.0,
|
||||
bool $autoRelease = true,
|
||||
int $retrySleep = 1000,
|
||||
int $retryCount = 30,
|
||||
bool $force = false
|
||||
): LockInterface|bool {
|
||||
$lock = $this->createLock($resource, $ttl, $autoRelease, $retrySleep, $retryCount);
|
||||
$lock = $this->createLock($resource, $ttl, $autoRelease);
|
||||
|
||||
if ($force) {
|
||||
$this->clearQueue($resource);
|
||||
try {
|
||||
$lock->release();
|
||||
$lock->acquire(true);
|
||||
} catch (\Exception) {
|
||||
return false;
|
||||
|
@ -59,11 +52,6 @@ class LockFactory
|
|||
return $lock;
|
||||
}
|
||||
|
||||
public function clearQueue(string $resource): void
|
||||
{
|
||||
$this->redis->del($this->getPrefixedResourceName($resource));
|
||||
}
|
||||
|
||||
protected function getPrefixedResourceName(string $resource): string
|
||||
{
|
||||
return 'lock_' . $resource;
|
||||
|
|
|
@ -13,15 +13,15 @@ class RateLimit
|
|||
{
|
||||
public function __construct(
|
||||
protected string $rl_group = 'default',
|
||||
protected int $rl_timeout = 5,
|
||||
protected int $rl_interval = 2
|
||||
protected int $rl_interval = 5,
|
||||
protected int $rl_limit = 2
|
||||
) {
|
||||
}
|
||||
|
||||
public function __invoke(ServerRequest $request, RequestHandlerInterface $handler): ResponseInterface
|
||||
{
|
||||
$rateLimit = $request->getRateLimit();
|
||||
$rateLimit->checkRequestRateLimit($request, $this->rl_group, $this->rl_timeout, $this->rl_interval);
|
||||
$rateLimit->checkRequestRateLimit($request, $this->rl_group, $this->rl_interval, $this->rl_limit);
|
||||
|
||||
return $handler->handle($request);
|
||||
}
|
||||
|
|
|
@ -3,71 +3,73 @@
|
|||
namespace App;
|
||||
|
||||
use App\Http\ServerRequest;
|
||||
use Redis;
|
||||
use Psr\Cache\CacheItemPoolInterface;
|
||||
use Symfony\Component\Cache\Adapter\ProxyAdapter;
|
||||
use Symfony\Component\RateLimiter\RateLimiterFactory;
|
||||
use Symfony\Component\RateLimiter\Storage\CacheStorage;
|
||||
|
||||
class RateLimit
|
||||
{
|
||||
protected CacheItemPoolInterface $psr6Cache;
|
||||
|
||||
public function __construct(
|
||||
protected Redis $redis,
|
||||
protected Environment $environment
|
||||
protected LockFactory $lockFactory,
|
||||
protected Environment $environment,
|
||||
CacheItemPoolInterface $cacheItemPool
|
||||
) {
|
||||
$this->psr6Cache = new ProxyAdapter($cacheItemPool, 'ratelimit.');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ServerRequest $request
|
||||
* @param string $groupName
|
||||
* @param int $timeout
|
||||
* @param int $interval
|
||||
* @param int $limit
|
||||
*
|
||||
* @throws Exception\RateLimitExceededException
|
||||
*/
|
||||
public function checkRequestRateLimit(
|
||||
ServerRequest $request,
|
||||
string $groupName,
|
||||
int $timeout = 5,
|
||||
int $interval = 2
|
||||
int $interval = 5,
|
||||
int $limit = 2
|
||||
): void {
|
||||
if ($this->environment->isTesting() || $this->environment->isCli()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$ip = $request->getIp();
|
||||
$cacheName = sprintf(
|
||||
'%s.%s',
|
||||
$groupName,
|
||||
str_replace(':', '.', $ip)
|
||||
);
|
||||
|
||||
$this->checkRateLimit($cacheName, $timeout, $interval);
|
||||
$ipKey = str_replace([':', '.'], '_', $request->getIp());
|
||||
$this->checkRateLimit($groupName, $ipKey, $interval, $limit);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $groupName
|
||||
* @param int $timeout
|
||||
* @param string $key
|
||||
* @param int $interval
|
||||
* @param int $limit
|
||||
*
|
||||
* @throws Exception\RateLimitExceededException
|
||||
*/
|
||||
public function checkRateLimit(
|
||||
string $groupName,
|
||||
int $timeout = 5,
|
||||
int $interval = 2
|
||||
string $key,
|
||||
int $interval = 5,
|
||||
int $limit = 2
|
||||
): void {
|
||||
$cacheName = sprintf(
|
||||
'rate_limit.%s',
|
||||
$groupName
|
||||
);
|
||||
$cacheStore = new CacheStorage($this->psr6Cache);
|
||||
|
||||
$result = $this->redis->get($cacheName);
|
||||
$config = [
|
||||
'id' => 'ratelimit.' . $groupName,
|
||||
'policy' => 'sliding_window',
|
||||
'interval' => $interval . ' seconds',
|
||||
'limit' => $limit,
|
||||
];
|
||||
|
||||
if ($result !== false) {
|
||||
if ((int)$result + 1 > $interval) {
|
||||
throw new Exception\RateLimitExceededException();
|
||||
}
|
||||
$rateLimiterFactory = new RateLimiterFactory($config, $cacheStore, $this->lockFactory);
|
||||
$rateLimiter = $rateLimiterFactory->create($key);
|
||||
|
||||
$this->redis->incr($cacheName);
|
||||
} else {
|
||||
$this->redis->setex($cacheName, $timeout, 1);
|
||||
if (false === $rateLimiter->consume(1)->isAccepted()) {
|
||||
throw new Exception\RateLimitExceededException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -49,9 +49,7 @@ class LastFm
|
|||
$rateLimitLock = $this->lockFactory->createLock(
|
||||
'api_lastfm',
|
||||
1,
|
||||
false,
|
||||
500,
|
||||
10
|
||||
false
|
||||
);
|
||||
|
||||
try {
|
||||
|
|
|
@ -39,9 +39,7 @@ class MusicBrainz
|
|||
$rateLimitLock = $this->lockFactory->createLock(
|
||||
'api_musicbrainz',
|
||||
1,
|
||||
false,
|
||||
500,
|
||||
10
|
||||
false
|
||||
);
|
||||
|
||||
try {
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
{{if eq .Env.ENABLE_REDIS "true"}}
|
||||
upstream redis_server {
|
||||
nchan_redis_server "redis://redis:6379";
|
||||
}
|
||||
{{end}}
|
||||
|
||||
resolver 127.0.0.11;
|
||||
|
||||
|
@ -50,7 +52,9 @@ server {
|
|||
|
||||
location ~ /pub/(\w+)$ {
|
||||
nchan_publisher;
|
||||
{{if eq .Env.ENABLE_REDIS "true"}}
|
||||
nchan_redis_pass redis_server;
|
||||
{{end}}
|
||||
|
||||
nchan_channel_group "azuracast_nowplaying";
|
||||
nchan_channel_id $1;
|
||||
|
@ -152,7 +156,9 @@ server {
|
|||
nchan_access_control_allow_origin "*";
|
||||
|
||||
nchan_subscriber;
|
||||
{{if eq .Env.ENABLE_REDIS "true"}}
|
||||
nchan_redis_pass redis_server;
|
||||
{{end}}
|
||||
|
||||
nchan_channel_group "azuracast_nowplaying";
|
||||
nchan_channel_id "$1";
|
||||
|
|
|
@ -1,4 +1,20 @@
|
|||
#!/bin/bash
|
||||
|
||||
bool() {
|
||||
case "$1" in
|
||||
Y* | y* | true | TRUE | 1) return 0 ;;
|
||||
esac
|
||||
return 1
|
||||
}
|
||||
|
||||
ENABLE_REDIS=${ENABLE_REDIS:-false}
|
||||
if bool "$ENABLE_REDIS"; then
|
||||
ENABLE_REDIS=true
|
||||
else
|
||||
ENABLE_REDIS=false
|
||||
fi
|
||||
|
||||
export ENABLE_REDIS
|
||||
|
||||
# Copy the nginx template to its destination.
|
||||
dockerize -template "/etc/nginx/azuracast.conf.tmpl:/etc/nginx/conf.d/azuracast.conf"
|
||||
|
|
Loading…
Reference in a new issue