Fixed merge conflicts

This commit is contained in:
Alejandro Celaya
2022-08-09 18:59:55 +02:00
263 changed files with 4542 additions and 2289 deletions

View File

@@ -0,0 +1,11 @@
<?php
declare(strict_types=1);
return [
'app_options' => [
'version' => 'latest',
],
];

View File

@@ -7,7 +7,7 @@ namespace Shlinkio\Shlink;
use Shlinkio\Shlink\Core\Config\EnvVars;
return (static function (): array {
$threshold = EnvVars::DELETE_SHORT_URL_THRESHOLD()->loadFromEnv();
$threshold = EnvVars::DELETE_SHORT_URL_THRESHOLD->loadFromEnv();
return [

View File

@@ -8,7 +8,7 @@ use Shlinkio\Shlink\Core\Config\EnvVars;
use function Functional\contains;
return (static function (): array {
$driver = EnvVars::DB_DRIVER()->loadFromEnv();
$driver = EnvVars::DB_DRIVER->loadFromEnv();
$isMysqlCompatible = contains(['maria', 'mysql'], $driver);
$resolveDriver = static fn () => match ($driver) {
@@ -35,12 +35,12 @@ return (static function (): array {
],
default => [
'driver' => $resolveDriver(),
'dbname' => EnvVars::DB_NAME()->loadFromEnv('shlink'),
'user' => EnvVars::DB_USER()->loadFromEnv(),
'password' => EnvVars::DB_PASSWORD()->loadFromEnv(),
'host' => EnvVars::DB_HOST()->loadFromEnv(EnvVars::DB_UNIX_SOCKET()->loadFromEnv()),
'port' => EnvVars::DB_PORT()->loadFromEnv($resolveDefaultPort()),
'unix_socket' => $isMysqlCompatible ? EnvVars::DB_UNIX_SOCKET()->loadFromEnv() : null,
'dbname' => EnvVars::DB_NAME->loadFromEnv('shlink'),
'user' => EnvVars::DB_USER->loadFromEnv(),
'password' => EnvVars::DB_PASSWORD->loadFromEnv(),
'host' => EnvVars::DB_HOST->loadFromEnv(EnvVars::DB_UNIX_SOCKET->loadFromEnv()),
'port' => EnvVars::DB_PORT->loadFromEnv($resolveDefaultPort()),
'unix_socket' => $isMysqlCompatible ? EnvVars::DB_UNIX_SOCKET->loadFromEnv() : null,
'charset' => $resolveCharset(),
],
};

View File

@@ -9,7 +9,7 @@ return [
'geolite2' => [
'db_location' => __DIR__ . '/../../data/GeoLite2-City.mmdb',
'temp_dir' => __DIR__ . '/../../data',
'license_key' => EnvVars::GEOLITE_LICENSE_KEY()->loadFromEnv(),
'license_key' => EnvVars::GEOLITE_LICENSE_KEY->loadFromEnv(),
],
];

View File

@@ -27,10 +27,12 @@ return [
Option\Redirect\Regular404RedirectConfigOption::class,
Option\Visit\VisitsThresholdConfigOption::class,
Option\BasePathConfigOption::class,
Option\TimezoneConfigOption::class,
Option\Worker\TaskWorkerNumConfigOption::class,
Option\Worker\WebWorkerNumConfigOption::class,
Option\Redis\RedisServersConfigOption::class,
Option\Redis\RedisSentinelServiceConfigOption::class,
Option\Redis\RedisPubSubConfigOption::class,
Option\UrlShortener\ShortCodeLengthOption::class,
Option\Mercure\EnableMercureConfigOption::class,
Option\Mercure\MercurePublicUrlConfigOption::class,
@@ -41,6 +43,7 @@ return [
Option\UrlShortener\RedirectCacheLifeTimeConfigOption::class,
Option\UrlShortener\AutoResolveTitlesConfigOption::class,
Option\UrlShortener\AppendExtraPathConfigOption::class,
Option\UrlShortener\EnableMultiSegmentSlugsConfigOption::class,
Option\Tracking\IpAnonymizationConfigOption::class,
Option\Tracking\OrphanVisitsTrackingConfigOption::class,
Option\Tracking\DisableTrackParamConfigOption::class,
@@ -63,13 +66,13 @@ return [
],
'installation_commands' => [
InstallationCommand::DB_CREATE_SCHEMA => [
InstallationCommand::DB_CREATE_SCHEMA->value => [
'command' => 'bin/cli ' . Command\Db\CreateDatabaseCommand::NAME,
],
InstallationCommand::DB_MIGRATE => [
InstallationCommand::DB_MIGRATE->value => [
'command' => 'bin/cli ' . Command\Db\MigrateDatabaseCommand::NAME,
],
InstallationCommand::GEOLITE_DOWNLOAD_DB => [
InstallationCommand::GEOLITE_DOWNLOAD_DB->value => [
'command' => 'bin/cli ' . Command\Visit\DownloadGeoLiteDbCommand::NAME,
],
],

View File

@@ -3,7 +3,7 @@
declare(strict_types=1);
use Laminas\ServiceManager\AbstractFactory\ConfigAbstractFactory;
use Predis\ClientInterface as PredisClient;
use Shlinkio\Shlink\Common\Cache\RedisFactory;
use Shlinkio\Shlink\Common\Logger\LoggerAwareDelegatorFactory;
use Shlinkio\Shlink\Core\Config\EnvVars;
use Symfony\Component\Lock;
@@ -24,7 +24,7 @@ return [
LOCAL_LOCK_FACTORY => ConfigAbstractFactory::class,
],
'aliases' => [
'lock_store' => EnvVars::REDIS_SERVERS()->existsInEnv() ? 'redis_lock_store' : 'local_lock_store',
'lock_store' => EnvVars::REDIS_SERVERS->existsInEnv() ? 'redis_lock_store' : 'local_lock_store',
'redis_lock_store' => Lock\Store\RedisStore::class,
'local_lock_store' => Lock\Store\FlockStore::class,
@@ -38,7 +38,7 @@ return [
ConfigAbstractFactory::class => [
Lock\Store\FlockStore::class => ['config.locks.locks_dir'],
Lock\Store\RedisStore::class => [PredisClient::class],
Lock\Store\RedisStore::class => [RedisFactory::SERVICE_NAME],
Lock\LockFactory::class => ['lock_store'],
LOCAL_LOCK_FACTORY => ['local_lock_store'],
],

View File

@@ -4,72 +4,36 @@ declare(strict_types=1);
namespace Shlinkio\Shlink;
use Monolog\Formatter;
use Monolog\Handler;
use Monolog\Level;
use Monolog\Logger;
use Monolog\Processor;
use MonologFactory\DiContainerLoggerFactory;
use PhpMiddleware\RequestId;
use Psr\Log\LoggerInterface;
use Shlinkio\Shlink\Common\Logger\LoggerFactory;
use Shlinkio\Shlink\Common\Logger\LoggerType;
use const PHP_EOL;
$processors = [
'exception_with_new_line' => [
'name' => Common\Logger\Processor\ExceptionWithNewLineProcessor::class,
],
'psr3' => [
'name' => Processor\PsrLogMessageProcessor::class,
],
'request_id' => RequestId\MonologProcessor::class,
];
$formatter = [
'name' => Formatter\LineFormatter::class,
'params' => [
'format' => '[%datetime%] [%extra.request_id%] %channel%.%level_name% - %message%' . PHP_EOL,
'allow_inline_line_breaks' => true,
],
$common = [
'level' => Level::Info->value,
'processors' => [RequestId\MonologProcessor::class],
'line_format' => '[%datetime%] [%extra.request_id%] %channel%.%level_name% - %message%',
];
return [
'logger' => [
'Shlink' => [
'name' => 'Shlink',
'handlers' => [
'shlink_handler' => [
'name' => Handler\RotatingFileHandler::class,
'params' => [
'level' => Logger::INFO,
'filename' => 'data/log/shlink_log.log',
'max_files' => 30,
'file_permission' => 0666,
],
'formatter' => $formatter,
],
],
'processors' => $processors,
'type' => LoggerType::FILE->value,
...$common,
],
'Access' => [
'name' => 'Access',
'handlers' => [
'access_handler' => [
'name' => Handler\StreamHandler::class,
'params' => [
'level' => Logger::INFO,
'stream' => 'php://stdout',
],
'formatter' => $formatter,
],
],
'processors' => $processors,
'type' => LoggerType::STREAM->value,
...$common,
],
],
'dependencies' => [
'factories' => [
'Logger_Shlink' => [DiContainerLoggerFactory::class, 'Shlink'],
'Logger_Access' => [DiContainerLoggerFactory::class, 'Access'],
'Logger_Shlink' => [LoggerFactory::class, 'Shlink'],
'Logger_Access' => [LoggerFactory::class, 'Access'],
],
'aliases' => [
'logger' => 'Logger_Shlink',

View File

@@ -2,33 +2,18 @@
declare(strict_types=1);
use Monolog\Handler\StreamHandler;
use Monolog\Logger;
use Monolog\Level;
use Shlinkio\Shlink\Common\Logger\LoggerType;
$isSwoole = extension_loaded('openswoole');
// For swoole, send logs to standard output
$handler = $isSwoole
? [
'name' => StreamHandler::class,
'params' => [
'level' => Logger::DEBUG,
'stream' => 'php://stdout',
],
]
: [
'params' => [
'level' => Logger::DEBUG,
],
];
return [
'logger' => [
'Shlink' => [
'handlers' => [
'shlink_handler' => $handler,
],
// For swoole, send logs as stream
'type' => $isSwoole ? LoggerType::STREAM->value : LoggerType::FILE->value,
'level' => Level::Debug->value,
],
],

View File

@@ -9,14 +9,14 @@ use Symfony\Component\Mercure\Hub;
use Symfony\Component\Mercure\HubInterface;
return (static function (): array {
$publicUrl = EnvVars::MERCURE_PUBLIC_HUB_URL()->loadFromEnv();
$publicUrl = EnvVars::MERCURE_PUBLIC_HUB_URL->loadFromEnv();
return [
'mercure' => [
'public_hub_url' => $publicUrl,
'internal_hub_url' => EnvVars::MERCURE_INTERNAL_HUB_URL()->loadFromEnv($publicUrl),
'jwt_secret' => EnvVars::MERCURE_JWT_SECRET()->loadFromEnv(),
'internal_hub_url' => EnvVars::MERCURE_INTERNAL_HUB_URL->loadFromEnv($publicUrl),
'jwt_secret' => EnvVars::MERCURE_JWT_SECRET->loadFromEnv(),
'jwt_issuer' => 'Shlink',
],

View File

@@ -13,13 +13,13 @@ use const Shlinkio\Shlink\DEFAULT_QR_CODE_SIZE;
return [
'qr_codes' => [
'size' => (int) EnvVars::DEFAULT_QR_CODE_SIZE()->loadFromEnv(DEFAULT_QR_CODE_SIZE),
'margin' => (int) EnvVars::DEFAULT_QR_CODE_MARGIN()->loadFromEnv(DEFAULT_QR_CODE_MARGIN),
'format' => EnvVars::DEFAULT_QR_CODE_FORMAT()->loadFromEnv(DEFAULT_QR_CODE_FORMAT),
'error_correction' => EnvVars::DEFAULT_QR_CODE_ERROR_CORRECTION()->loadFromEnv(
'size' => (int) EnvVars::DEFAULT_QR_CODE_SIZE->loadFromEnv(DEFAULT_QR_CODE_SIZE),
'margin' => (int) EnvVars::DEFAULT_QR_CODE_MARGIN->loadFromEnv(DEFAULT_QR_CODE_MARGIN),
'format' => EnvVars::DEFAULT_QR_CODE_FORMAT->loadFromEnv(DEFAULT_QR_CODE_FORMAT),
'error_correction' => EnvVars::DEFAULT_QR_CODE_ERROR_CORRECTION->loadFromEnv(
DEFAULT_QR_CODE_ERROR_CORRECTION,
),
'round_block_size' => (bool) EnvVars::DEFAULT_QR_CODE_ROUND_BLOCK_SIZE()->loadFromEnv(
'round_block_size' => (bool) EnvVars::DEFAULT_QR_CODE_ROUND_BLOCK_SIZE->loadFromEnv(
DEFAULT_QR_CODE_ROUND_BLOCK_SIZE,
),
],

View File

@@ -2,46 +2,20 @@
declare(strict_types=1);
use Laminas\ServiceManager\AbstractFactory\ConfigAbstractFactory;
use Laminas\ServiceManager\Proxy\LazyServiceFactory;
use PhpAmqpLib\Connection\AMQPStreamConnection;
use Shlinkio\Shlink\Core\Config\EnvVars;
return [
'rabbitmq' => [
'enabled' => (bool) EnvVars::RABBITMQ_ENABLED()->loadFromEnv(false),
'host' => EnvVars::RABBITMQ_HOST()->loadFromEnv(),
'port' => (int) EnvVars::RABBITMQ_PORT()->loadFromEnv('5672'),
'user' => EnvVars::RABBITMQ_USER()->loadFromEnv(),
'password' => EnvVars::RABBITMQ_PASSWORD()->loadFromEnv(),
'vhost' => EnvVars::RABBITMQ_VHOST()->loadFromEnv('/'),
],
'enabled' => (bool) EnvVars::RABBITMQ_ENABLED->loadFromEnv(false),
'host' => EnvVars::RABBITMQ_HOST->loadFromEnv(),
'port' => (int) EnvVars::RABBITMQ_PORT->loadFromEnv('5672'),
'user' => EnvVars::RABBITMQ_USER->loadFromEnv(),
'password' => EnvVars::RABBITMQ_PASSWORD->loadFromEnv(),
'vhost' => EnvVars::RABBITMQ_VHOST->loadFromEnv('/'),
'dependencies' => [
'factories' => [
AMQPStreamConnection::class => ConfigAbstractFactory::class,
],
'delegators' => [
AMQPStreamConnection::class => [
LazyServiceFactory::class,
],
],
'lazy_services' => [
'class_map' => [
AMQPStreamConnection::class => AMQPStreamConnection::class,
],
],
],
ConfigAbstractFactory::class => [
AMQPStreamConnection::class => [
'config.rabbitmq.host',
'config.rabbitmq.port',
'config.rabbitmq.user',
'config.rabbitmq.password',
'config.rabbitmq.vhost',
],
// Deprecated
'legacy_visits_publishing' => (bool) EnvVars::RABBITMQ_LEGACY_VISITS_PUBLISHING->loadFromEnv(false),
],
];

View File

@@ -10,14 +10,14 @@ use const Shlinkio\Shlink\DEFAULT_REDIRECT_STATUS_CODE;
return [
'not_found_redirects' => [
'invalid_short_url' => EnvVars::DEFAULT_INVALID_SHORT_URL_REDIRECT()->loadFromEnv(),
'regular_404' => EnvVars::DEFAULT_REGULAR_404_REDIRECT()->loadFromEnv(),
'base_url' => EnvVars::DEFAULT_BASE_URL_REDIRECT()->loadFromEnv(),
'invalid_short_url' => EnvVars::DEFAULT_INVALID_SHORT_URL_REDIRECT->loadFromEnv(),
'regular_404' => EnvVars::DEFAULT_REGULAR_404_REDIRECT->loadFromEnv(),
'base_url' => EnvVars::DEFAULT_BASE_URL_REDIRECT->loadFromEnv(),
],
'redirects' => [
'redirect_status_code' => (int) EnvVars::REDIRECT_STATUS_CODE()->loadFromEnv(DEFAULT_REDIRECT_STATUS_CODE),
'redirect_cache_lifetime' => (int) EnvVars::REDIRECT_CACHE_LIFETIME()->loadFromEnv(
'redirect_status_code' => (int) EnvVars::REDIRECT_STATUS_CODE->loadFromEnv(DEFAULT_REDIRECT_STATUS_CODE),
'redirect_cache_lifetime' => (int) EnvVars::REDIRECT_CACHE_LIFETIME->loadFromEnv(
DEFAULT_REDIRECT_CACHE_LIFETIME,
),
],

View File

@@ -5,17 +5,23 @@ declare(strict_types=1);
use Shlinkio\Shlink\Core\Config\EnvVars;
return (static function (): array {
$redisServers = EnvVars::REDIS_SERVERS()->loadFromEnv();
$redisServers = EnvVars::REDIS_SERVERS->loadFromEnv();
$pubSub = [
'redis' => [
'pub_sub_enabled' => $redisServers !== null && EnvVars::REDIS_PUB_SUB_ENABLED->loadFromEnv(false),
],
];
return match ($redisServers) {
null => [],
null => $pubSub,
default => [
'cache' => [
'redis' => [
'servers' => $redisServers,
'sentinel_service' => EnvVars::REDIS_SENTINEL_SERVICE()->loadFromEnv(),
'sentinel_service' => EnvVars::REDIS_SENTINEL_SERVICE->loadFromEnv(),
],
],
...$pubSub,
],
};
})();

View File

@@ -7,12 +7,13 @@ return [
'cache' => [
'redis' => [
'servers' => 'tcp://shlink_redis:6379',
// 'servers' => [
// 'tcp://shlink_redis:6379',
// ],
],
],
'redis' => [
'pub_sub_enabled' => true,
],
'dependencies' => [
'aliases' => [
// With this config, a user could alias 'lock_store' => 'redis_lock_store' to override the default

View File

@@ -5,6 +5,7 @@ declare(strict_types=1);
use Laminas\ServiceManager\AbstractFactory\ConfigAbstractFactory;
use Laminas\ServiceManager\Factory\InvokableFactory;
use PhpMiddleware\RequestId;
use Shlinkio\Shlink\Common\Logger\Processor\BackwardsCompatibleMonologProcessorDelegator;
return [
@@ -20,6 +21,11 @@ return [
RequestId\RequestIdMiddleware::class => ConfigAbstractFactory::class,
RequestId\MonologProcessor::class => ConfigAbstractFactory::class,
],
'delegators' => [
RequestId\MonologProcessor::class => [
BackwardsCompatibleMonologProcessorDelegator::class,
],
],
],
ConfigAbstractFactory::class => [

View File

@@ -8,7 +8,7 @@ use Shlinkio\Shlink\Core\Config\EnvVars;
return [
'router' => [
'base_path' => EnvVars::BASE_PATH()->loadFromEnv(''),
'base_path' => EnvVars::BASE_PATH->loadFromEnv(''),
'fastroute' => [
FastRouteRouter::CONFIG_CACHE_ENABLED => true,

View File

@@ -0,0 +1,103 @@
<?php
declare(strict_types=1);
namespace Shlinkio\Shlink;
use Fig\Http\Message\RequestMethodInterface;
use RKA\Middleware\IpAddress;
use Shlinkio\Shlink\Core\Action as CoreAction;
use Shlinkio\Shlink\Rest\Action;
use Shlinkio\Shlink\Rest\ConfigProvider;
use Shlinkio\Shlink\Rest\Middleware;
use Shlinkio\Shlink\Rest\Middleware\Mercure\NotConfiguredMercureErrorHandler;
return (static function (): array {
$contentNegotiationMiddleware = Middleware\ShortUrl\CreateShortUrlContentNegotiationMiddleware::class;
$dropDomainMiddleware = Middleware\ShortUrl\DropDefaultDomainFromRequestMiddleware::class;
$overrideDomainMiddleware = Middleware\ShortUrl\OverrideDomainMiddleware::class;
return [
// The order of the routes defined here matters. Changing it might cause path conflicts
'routes' => [
// Rest
...ConfigProvider::applyRoutesPrefix([
Action\HealthAction::getRouteDef(),
// Visits
Action\Visit\ShortUrlVisitsAction::getRouteDef([$dropDomainMiddleware]),
Action\Visit\TagVisitsAction::getRouteDef(),
Action\Visit\DomainVisitsAction::getRouteDef(),
Action\Visit\GlobalVisitsAction::getRouteDef(),
Action\Visit\OrphanVisitsAction::getRouteDef(),
Action\Visit\NonOrphanVisitsAction::getRouteDef(),
// Short URLs
Action\ShortUrl\CreateShortUrlAction::getRouteDef([
$contentNegotiationMiddleware,
$dropDomainMiddleware,
$overrideDomainMiddleware,
Middleware\ShortUrl\DefaultShortCodesLengthMiddleware::class,
]),
Action\ShortUrl\SingleStepCreateShortUrlAction::getRouteDef([
$contentNegotiationMiddleware,
$overrideDomainMiddleware,
]),
Action\ShortUrl\EditShortUrlAction::getRouteDef([$dropDomainMiddleware]),
Action\ShortUrl\DeleteShortUrlAction::getRouteDef([$dropDomainMiddleware]),
Action\ShortUrl\ResolveShortUrlAction::getRouteDef([$dropDomainMiddleware]),
Action\ShortUrl\ListShortUrlsAction::getRouteDef(),
// Tags
Action\Tag\ListTagsAction::getRouteDef(),
Action\Tag\TagsStatsAction::getRouteDef(),
Action\Tag\DeleteTagsAction::getRouteDef(),
Action\Tag\UpdateTagAction::getRouteDef(),
// Domains
Action\Domain\ListDomainsAction::getRouteDef(),
Action\Domain\DomainRedirectsAction::getRouteDef(),
Action\MercureInfoAction::getRouteDef([NotConfiguredMercureErrorHandler::class]),
]),
// Non-rest
[
'name' => CoreAction\RobotsAction::class,
'path' => '/robots.txt',
'middleware' => [
CoreAction\RobotsAction::class,
],
'allowed_methods' => [RequestMethodInterface::METHOD_GET],
],
[
'name' => CoreAction\PixelAction::class,
'path' => '/{shortCode}/track',
'middleware' => [
IpAddress::class,
CoreAction\PixelAction::class,
],
'allowed_methods' => [RequestMethodInterface::METHOD_GET],
],
[
'name' => CoreAction\QrCodeAction::class,
'path' => '/{shortCode}/qr-code',
'middleware' => [
CoreAction\QrCodeAction::class,
],
'allowed_methods' => [RequestMethodInterface::METHOD_GET],
],
[
'name' => CoreAction\RedirectAction::class,
'path' => '/{shortCode}',
'middleware' => [
IpAddress::class,
CoreAction\RedirectAction::class,
],
'allowed_methods' => [RequestMethodInterface::METHOD_GET],
],
],
];
})();

View File

@@ -6,8 +6,8 @@ use Shlinkio\Shlink\Core\Config\EnvVars;
use const Shlinkio\Shlink\MIN_TASK_WORKERS;
return (static function () {
$taskWorkers = (int) EnvVars::TASK_WORKER_NUM()->loadFromEnv(16);
return (static function (): array {
$taskWorkers = (int) EnvVars::TASK_WORKER_NUM->loadFromEnv(16);
return [
@@ -17,11 +17,11 @@ return (static function () {
'swoole-http-server' => [
'host' => '0.0.0.0',
'port' => (int) EnvVars::PORT()->loadFromEnv(8080),
'port' => (int) EnvVars::PORT->loadFromEnv(8080),
'process-name' => 'shlink',
'options' => [
'worker_num' => (int) EnvVars::WEB_WORKER_NUM()->loadFromEnv(16),
'worker_num' => (int) EnvVars::WEB_WORKER_NUM->loadFromEnv(16),
'task_worker_num' => max($taskWorkers, MIN_TASK_WORKERS),
],
],

View File

@@ -9,28 +9,28 @@ return [
'tracking' => [
// Tells if IP addresses should be anonymized before persisting, to fulfil data protection regulations
// This applies only if IP address tracking is enabled
'anonymize_remote_addr' => (bool) EnvVars::ANONYMIZE_REMOTE_ADDR()->loadFromEnv(true),
'anonymize_remote_addr' => (bool) EnvVars::ANONYMIZE_REMOTE_ADDR->loadFromEnv(true),
// Tells if visits to not-found URLs should be tracked. The disable_tracking option takes precedence
'track_orphan_visits' => (bool) EnvVars::TRACK_ORPHAN_VISITS()->loadFromEnv(true),
'track_orphan_visits' => (bool) EnvVars::TRACK_ORPHAN_VISITS->loadFromEnv(true),
// A query param that, if provided, will disable tracking of one particular visit. Always takes precedence
'disable_track_param' => EnvVars::DISABLE_TRACK_PARAM()->loadFromEnv(),
'disable_track_param' => EnvVars::DISABLE_TRACK_PARAM->loadFromEnv(),
// If true, visits will not be tracked at all
'disable_tracking' => (bool) EnvVars::DISABLE_TRACKING()->loadFromEnv(false),
'disable_tracking' => (bool) EnvVars::DISABLE_TRACKING->loadFromEnv(false),
// If true, visits will be tracked, but neither the IP address, nor the location will be resolved
'disable_ip_tracking' => (bool) EnvVars::DISABLE_IP_TRACKING()->loadFromEnv(false),
'disable_ip_tracking' => (bool) EnvVars::DISABLE_IP_TRACKING->loadFromEnv(false),
// If true, the referrer will not be tracked
'disable_referrer_tracking' => (bool) EnvVars::DISABLE_REFERRER_TRACKING()->loadFromEnv(false),
'disable_referrer_tracking' => (bool) EnvVars::DISABLE_REFERRER_TRACKING->loadFromEnv(false),
// If true, the user agent will not be tracked
'disable_ua_tracking' => (bool) EnvVars::DISABLE_UA_TRACKING()->loadFromEnv(false),
'disable_ua_tracking' => (bool) EnvVars::DISABLE_UA_TRACKING->loadFromEnv(false),
// A list of IP addresses, patterns or CIDR blocks from which tracking is disabled by default
'disable_tracking_from' => EnvVars::DISABLE_TRACKING_FROM()->loadFromEnv(),
'disable_tracking_from' => EnvVars::DISABLE_TRACKING_FROM->loadFromEnv(),
],
];

View File

@@ -9,20 +9,21 @@ use const Shlinkio\Shlink\MIN_SHORT_CODES_LENGTH;
return (static function (): array {
$shortCodesLength = max(
(int) EnvVars::DEFAULT_SHORT_CODES_LENGTH()->loadFromEnv(DEFAULT_SHORT_CODES_LENGTH),
(int) EnvVars::DEFAULT_SHORT_CODES_LENGTH->loadFromEnv(DEFAULT_SHORT_CODES_LENGTH),
MIN_SHORT_CODES_LENGTH,
);
return [
'url_shortener' => [
'domain' => [
'schema' => ((bool) EnvVars::IS_HTTPS_ENABLED()->loadFromEnv(true)) ? 'https' : 'http',
'hostname' => EnvVars::DEFAULT_DOMAIN()->loadFromEnv(''),
'domain' => [ // TODO Refactor this structure to url_shortener.schema and url_shortener.default_domain
'schema' => ((bool) EnvVars::IS_HTTPS_ENABLED->loadFromEnv(true)) ? 'https' : 'http',
'hostname' => EnvVars::DEFAULT_DOMAIN->loadFromEnv(''),
],
'default_short_codes_length' => $shortCodesLength,
'auto_resolve_titles' => (bool) EnvVars::AUTO_RESOLVE_TITLES()->loadFromEnv(false),
'append_extra_path' => (bool) EnvVars::REDIRECT_APPEND_EXTRA_PATH()->loadFromEnv(false),
'auto_resolve_titles' => (bool) EnvVars::AUTO_RESOLVE_TITLES->loadFromEnv(false),
'append_extra_path' => (bool) EnvVars::REDIRECT_APPEND_EXTRA_PATH->loadFromEnv(false),
'multi_segment_slugs_enabled' => (bool) EnvVars::MULTI_SEGMENT_SLUGS_ENABLED->loadFromEnv(false),
],
];

View File

@@ -12,6 +12,7 @@ return [
'hostname' => sprintf('localhost:%s', $isSwoole ? '8080' : '8000'),
],
'auto_resolve_titles' => true,
// 'multi_segment_slugs_enabled' => true,
],
];

View File

@@ -6,14 +6,14 @@ use Shlinkio\Shlink\Core\Config\EnvVars;
// Deprecated. Webhooks are no longer supported. To be removed in Shlink 4.0.0
return (static function (): array {
$webhooks = EnvVars::VISITS_WEBHOOKS()->loadFromEnv();
$webhooks = EnvVars::VISITS_WEBHOOKS->loadFromEnv();
return [
'visits_webhooks' => [
'webhooks' => $webhooks === null ? [] : explode(',', $webhooks),
'notify_orphan_visits_to_webhooks' =>
(bool) EnvVars::NOTIFY_ORPHAN_VISITS_TO_WEBHOOKS()->loadFromEnv(false),
(bool) EnvVars::NOTIFY_ORPHAN_VISITS_TO_WEBHOOKS->loadFromEnv(false),
],
];

View File

@@ -5,7 +5,7 @@ declare(strict_types=1);
use Psr\Container\ContainerInterface;
use Symfony\Component\Console\Application as CliApp;
return (static function () {
return (static function (): CliApp {
/** @var ContainerInterface $container */
$container = include __DIR__ . '/container.php';
return $container->get(CliApp::class);

View File

@@ -21,7 +21,7 @@ $isTestEnv = env('APP_ENV') === 'test';
return (new ConfigAggregator\ConfigAggregator([
! $isTestEnv
? new EnvVarLoaderProvider('config/params/generated_config.php', Core\Config\EnvVars::cases())
? new EnvVarLoaderProvider('config/params/generated_config.php', Core\Config\EnvVars::values())
: new ConfigAggregator\ArrayProvider([]),
Mezzio\ConfigProvider::class,
Mezzio\Router\ConfigProvider::class,
@@ -43,6 +43,9 @@ return (new ConfigAggregator\ConfigAggregator([
$isTestEnv
? new ConfigAggregator\PhpFileProvider('config/test/*.global.php')
: new ConfigAggregator\ArrayProvider([]),
// Routes have to be loaded last
new ConfigAggregator\PhpFileProvider('config/autoload/routes.config.php'),
], 'data/cache/app_config.php', [
Core\Config\BasePathPrefixer::class,
Core\Config\MultiSegmentSlugProcessor::class,
]))->getMergedConfig();

View File

@@ -19,3 +19,4 @@ const DEFAULT_QR_CODE_FORMAT = 'png';
const DEFAULT_QR_CODE_ERROR_CORRECTION = 'l';
const DEFAULT_QR_CODE_ROUND_BLOCK_SIZE = true;
const MIN_TASK_WORKERS = 4;
const MIGRATIONS_TABLE = 'migrations';

View File

@@ -3,6 +3,7 @@
declare(strict_types=1);
use Laminas\ServiceManager\ServiceManager;
use Shlinkio\Shlink\Core\Config\EnvVars;
use Symfony\Component\Lock;
use const Shlinkio\Shlink\LOCAL_LOCK_FACTORY;
@@ -11,6 +12,9 @@ chdir(dirname(__DIR__));
require 'vendor/autoload.php';
// This is one of the first files loaded. Configure the timezone here
date_default_timezone_set(EnvVars::TIMEZONE->loadFromEnv(date_default_timezone_get()));
// This class alias tricks the ConfigAbstractFactory to return Lock\Factory instances even with a different service name
// It needs to be placed here as individual config files will not be loaded once config is cached
if (! class_exists(LOCAL_LOCK_FACTORY)) {
@@ -18,7 +22,7 @@ if (! class_exists(LOCAL_LOCK_FACTORY)) {
}
// Build container
return (function () {
return (static function (): ServiceManager {
$config = require __DIR__ . '/config.php';
$container = new ServiceManager($config['dependencies']);
$container->setService('config', $config);

View File

@@ -3,9 +3,10 @@
declare(strict_types=1);
use Doctrine\ORM\EntityManager;
use Doctrine\ORM\EntityManagerInterface;
use Psr\Container\ContainerInterface;
return (static function () {
return (static function (): EntityManagerInterface {
/** @var ContainerInterface $container */
$container = include __DIR__ . '/container.php';
return $container->get(EntityManager::class);

View File

@@ -8,5 +8,5 @@ use Psr\Container\ContainerInterface;
/** @var ContainerInterface $container */
$container = require __DIR__ . '/../container.php';
$container->get(Helper\TestHelper::class)->createTestDb();
$container->get(Helper\TestHelper::class)->createTestDb(['bin/cli', 'db:create'], ['bin/cli', 'db:migrate']);
DbTest\DatabaseTestCase::setEntityManager($container->get('em'));

View File

@@ -10,7 +10,7 @@ use Laminas\Diactoros\Response\EmptyResponse;
use Laminas\ServiceManager\Factory\InvokableFactory;
use League\Event\EventDispatcher;
use Monolog\Handler\StreamHandler;
use Monolog\Logger;
use Monolog\Level;
use PHPUnit\Runner\Version;
use Psr\Container\ContainerInterface;
use Psr\Http\Message\ResponseInterface;
@@ -26,6 +26,7 @@ use Symfony\Component\Console\Application;
use Symfony\Component\Console\Event\ConsoleCommandEvent;
use Symfony\Component\Console\Event\ConsoleTerminateEvent;
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
use Shlinkio\Shlink\Common\Logger\LoggerType;
use function Laminas\Stratigility\middleware;
use function Shlinkio\Shlink\Config\env;
@@ -107,16 +108,10 @@ $buildDbConnection = static function (): array {
};
};
$buildTestLoggerConfig = fn (string $handlerName, string $filename) => [
'handlers' => [
$handlerName => [
'name' => StreamHandler::class,
'params' => [
'level' => Logger::DEBUG,
'stream' => sprintf('data/log/api-tests/%s', $filename),
],
],
],
$buildTestLoggerConfig = static fn (string $filename) => [
'level' => Level::Debug->value,
'type' => LoggerType::STREAM->value,
'destination' => sprintf('data/log/api-tests/%s', $filename),
];
return [
@@ -262,8 +257,8 @@ return [
],
'logger' => [
'Shlink' => $buildTestLoggerConfig('shlink_handler', 'shlink.log'),
'Access' => $buildTestLoggerConfig('access_handler', 'access.log'),
'Shlink' => $buildTestLoggerConfig('shlink.log'),
'Access' => $buildTestLoggerConfig('access.log'),
],
];