Compare commits

...

565 Commits

Author SHA1 Message Date
Alejandro Celaya
89419e278c
Merge pull request #2539 from acelaya-forks/symfony-8.0
Update to Symfony 8.0
2025-12-03 07:58:39 +01:00
Alejandro Celaya
1996745f64 Update to Symfony 8.0 2025-12-02 12:20:52 +01:00
Alejandro Celaya
cfab13bc47
Merge pull request #2533 from acelaya-forks/improve-coverage-2
Add more code coverage improvements
2025-11-18 09:33:20 +01:00
Alejandro Celaya
9432a5ba78 Add tests for events 2025-11-18 09:30:30 +01:00
Alejandro Celaya
7812a85b39 Remove unused AppOptions::__toString method 2025-11-18 09:20:52 +01:00
Alejandro Celaya
1e0b6be67d Improved NorFoundRedirectResolver test 2025-11-18 09:06:11 +01:00
Alejandro Celaya
88e5bb5618 Add test for AbstractRestAction::getRouteDef() 2025-11-18 08:56:09 +01:00
Alejandro Celaya
db1411d3f8 Remove unused method in ApiKeyNotFoundException 2025-11-18 08:45:31 +01:00
Alejandro Celaya
933c54e884 Improve some console commands coverage 2025-11-18 08:44:15 +01:00
Alejandro Celaya
f3ff059d48 Improve RoleResolver coverage 2025-11-17 12:33:08 +01:00
Alejandro Celaya
039a58bb44
Merge pull request #2532 from acelaya-forks/improve-coverage
Remove dead code that is affecting code coverage
2025-11-17 12:23:55 +01:00
Alejandro Celaya
0604237b94 Remove dead code that is affecting code coverage 2025-11-17 12:12:06 +01:00
Alejandro Celaya
c8537e4f71
Merge pull request #2529 from acelaya-forks/php-8.4-goodies
Simplify NotFoundRedirectConfigInterface with property hooks and asymetric visibility
2025-11-08 22:58:34 +01:00
Alejandro Celaya
c42fb67efc Simplify NotFoundRedirectConfigInterface with property hooks and asymetric visibility 2025-11-08 22:47:24 +01:00
Alejandro Celaya
ad15ae1922
Merge pull request #2526 from acelaya-forks/remove-trusted-proxies-workaround
Remove workaround to detect trusted proxies automatically
2025-11-08 10:44:09 +01:00
Alejandro Celaya
a731e01bd4 Remove test covering trusted proxies workaround 2025-11-08 10:41:03 +01:00
Alejandro Celaya
63bea36c05 Remove workaround to detect trusted proxies automatically 2025-11-08 10:33:06 +01:00
Alejandro Celaya
8a33c6968a
Merge pull request #2525 from acelaya-forks/remove-tags-option
Remove deprecated --tags option in console commands
2025-11-08 10:29:26 +01:00
Alejandro Celaya
359129f586 Remove deprecated --tags option in console commands 2025-11-08 10:22:02 +01:00
Alejandro Celaya
fdcc9933a3
Merge pull request #2524 from acelaya-forks/list-urls-deprecations
Remove deprecated options from short-url:list command
2025-11-08 10:13:15 +01:00
Alejandro Celaya
94adba95eb Fix codecov/codecov-action arguments for v5 2025-11-08 10:09:20 +01:00
Alejandro Celaya
8bafd82e1d Remove deprecated options from short-url:list command 2025-11-08 10:07:51 +01:00
Alejandro Celaya
d2bc9f7c2b
Merge pull request #2523 from acelaya-forks/no-disable-by-api-key
Do not allow API keys to be disabled by plain-text key
2025-11-08 09:34:34 +01:00
Alejandro Celaya
9f564b9785 Do not allow API keys to be disabled by plain-text key 2025-11-08 09:16:15 +01:00
Alejandro Celaya
1b6929acf6
Merge pull request #2518 from acelaya-forks/remove-extra-path
Remove REDIRECT_APPEND_EXTRA_PATH env var
2025-11-08 08:53:33 +01:00
Alejandro Celaya
91fd5809ff Remove REDIRECT_APPEND_EXTRA_PATH env var 2025-11-08 08:28:52 +01:00
Alejandro Celaya
c7fd6b3cba
Merge pull request #2516 from acelaya-forks/drop-php-8.3
Drop support for PHP 8.3
2025-11-08 08:17:18 +01:00
Alejandro Celaya
1eb1f5344c Drop support for PHP 8.3 2025-11-07 17:21:54 +01:00
Alejandro Celaya
f9ec4cea62
Merge pull request #2515 from acelaya-forks/remove-qr-codes
Drop support for QR code generation
2025-11-07 17:21:04 +01:00
Alejandro Celaya
c3961b139a Remove image extensions from dev docker containers 2025-11-07 17:10:51 +01:00
Alejandro Celaya
c2aae9640d Remove requirement on ext-gd 2025-11-07 17:07:34 +01:00
Alejandro Celaya
b4043be7fa Drop support for QR code generation 2025-11-07 16:58:19 +01:00
Alejandro Celaya
49c67abf0a Add missing entry in 4.6.0 changelog 2025-11-01 12:53:14 +01:00
Alejandro Celaya
c6f718eb11 Add proper version contraints for shlinkio packages 2025-11-01 12:43:22 +01:00
Alejandro Celaya
d3e8e9a735 Add v4.6.0 to changelog 2025-11-01 12:38:00 +01:00
Alejandro Celaya
8f1542c7aa
Merge pull request #2509 from acelaya-forks/invokable-commands
Invokable commands
2025-11-01 12:37:04 +01:00
Alejandro Celaya
058c0ebfaf Update changelog 2025-11-01 12:31:46 +01:00
Alejandro Celaya
b69db91378 Make DownloadGeoLiteDbCommand invokable 2025-11-01 12:30:15 +01:00
Alejandro Celaya
6113c28768 Make RenameTagCommand invokable 2025-11-01 12:28:04 +01:00
Alejandro Celaya
506ed47531 Make ListTagsCommand invokable 2025-11-01 12:25:52 +01:00
Alejandro Celaya
10173d2ab8 Make DeleteTagsCommand invokable 2025-11-01 12:24:18 +01:00
Alejandro Celaya
9ee709f0f3 Make DeleteExpiredShortUrlsCommand invokable 2025-11-01 12:18:29 +01:00
Alejandro Celaya
0fe28a5eb5 Make MatomoSendVisitsCommand invokable 2025-11-01 11:56:40 +01:00
Alejandro Celaya
2142afae89 Make ListDomainsCommand invokable 2025-11-01 11:50:43 +01:00
Alejandro Celaya
e7f4b84c65 Make DomainRedirectsCommand invokable 2025-11-01 11:45:27 +01:00
Alejandro Celaya
2d83b8d046 Make InitialApiKeyCommand invokable 2025-11-01 11:41:50 +01:00
Alejandro Celaya
dfef735c89 Make ReadEnvVarCommand invokable 2025-11-01 11:38:10 +01:00
Alejandro Celaya
c34c4e0eea
Merge pull request #2508 from acelaya-forks/php-8.5-support
Add support for PHP 8.5
2025-11-01 11:04:08 +01:00
Alejandro Celaya
f024fd414e Add support for PHP 8.5 2025-11-01 10:13:00 +01:00
Alejandro Celaya
12d81c3213 Update changelog 2025-11-01 10:03:03 +01:00
Alejandro Celaya
628fb9ebb5
Merge pull request #2503 from acelaya-forks/domain-visits-filter
Allow tags, orphan and non-orphan visits lists to be filtered by domain
2025-11-01 10:02:32 +01:00
Alejandro Celaya
e21cea1971 Add API tests for visits domain filtering 2025-11-01 09:56:15 +01:00
Alejandro Celaya
37088b1a4b Allow filtering orphan visits by domain via DEFAULT keyword 2025-10-31 08:53:31 +01:00
Alejandro Celaya
b5f8e8a4cd Document domain param for visits endpoints 2025-10-30 10:23:00 +01:00
Alejandro Celaya
a236f19dc4 Allow filtering by domain in VisitRepository::findNonOrphanVisits 2025-10-30 10:08:46 +01:00
Alejandro Celaya
94426c7bf4 Allow filtering by domain in VisitRepository::findOrphanVisits 2025-10-30 09:04:51 +01:00
Alejandro Celaya
9dcc51abde Allow filtering by domain in VisitRepository::findVisitsByTag 2025-10-29 12:04:36 +01:00
Alejandro Celaya
70e376d569 Allow domain to be passed to tag:visits, visit:orphan and visit:non-orphan commands 2025-10-29 08:43:01 +01:00
Alejandro Celaya
14a7e3bb05 Allow tags, orphan and non-orphan visits to be provided a domain filter param 2025-10-28 11:08:33 +01:00
Alejandro Celaya
10dab5be20
Merge pull request #2504 from acelaya-forks/remove-set-accessible
Remove all calls to ReflectionProperty::setAccessible
2025-10-28 11:08:03 +01:00
Alejandro Celaya
532700028a Remove all calls to ReflectionProperty::setAccessible 2025-10-28 11:03:41 +01:00
Alejandro Celaya
fc54a25c32
Merge pull request #2501 from acelaya-forks/redis-sentinels-acl
Add support for redis credentials when using sentinels
2025-10-27 10:47:36 +01:00
Alejandro Celaya
ba16ba45f2 Add support for redis credentials when using sentinels 2025-10-27 10:16:57 +01:00
Alejandro Celaya
51c732a013 Document support for frankenphp in changelog 2025-10-24 08:45:52 +02:00
Alejandro Celaya
0f17990821
Merge pull request #2499 from acelaya-forks/api-key-filter
Allow filtering short URLs by API key name
2025-10-22 08:40:33 +02:00
Alejandro Celaya
02500143c1 Update changelog 2025-10-22 08:31:04 +02:00
Alejandro Celaya
9c22c7fc9c Add more tests for apiKeyName short URLs filtering 2025-10-22 08:28:45 +02:00
Alejandro Celaya
7860225c25 Add api-key-name option to short-url:list command 2025-10-22 08:04:29 +02:00
Alejandro Celaya
506ed6207f Allow filtering short URLs by API key name 2025-10-21 12:25:06 +02:00
Alejandro Celaya
30ed1d7c6b
Merge pull request #2497 from acelaya-forks/delete-api-key
Add new command to delete API keys
2025-10-20 15:06:50 +02:00
Alejandro Celaya
b5a9353b85 Add new command to delete API keys 2025-10-20 10:34:53 +02:00
Alejandro Celaya
cae18ccfb3
Merge pull request #2495 from acelaya-forks/tags-option
Extract tags option to its own Option class
2025-10-20 09:02:15 +02:00
Alejandro Celaya
f876769b67 Extract tags option to its own Option class 2025-10-20 08:58:07 +02:00
Alejandro Celaya
2b06f56a9a
Merge pull request #2492 from acelaya-forks/feature/exclude-tags
Allow listing short URLs which DO NOT include certain tags
2025-10-17 09:33:51 +02:00
Alejandro Celaya
1c38ab1217 Add exclude-tags CLI tests 2025-10-17 09:26:18 +02:00
Alejandro Celaya
fb9e8cd79f Update changelog 2025-10-17 08:56:26 +02:00
Alejandro Celaya
eb199a61da Add exclude-tags API tests 2025-10-17 08:52:25 +02:00
Alejandro Celaya
25de0263c5 Deprecate --tags and add --tag for short-url:list command 2025-10-17 08:35:41 +02:00
Alejandro Celaya
41c03a66e4 Fix static analysis 2025-10-16 19:16:11 +02:00
Alejandro Celaya
13c1b12d84 Update logic in ShortUrlListRepository to take excluded tags into consideration 2025-10-16 19:16:11 +02:00
Alejandro Celaya
fe10aaf245 Make --tags option allow multiple values in list short URLs command 2025-10-16 19:16:11 +02:00
Alejandro Celaya
464e3d7f8e Support excludeTags and excludeTagsMode in list short URLs command 2025-10-16 19:16:11 +02:00
Alejandro Celaya
ac40a7021b Document excludeTags and excludeTagsMode params for short URLs list 2025-10-16 19:16:11 +02:00
Alejandro Celaya
c60a5e750b Reference Jetbrains in README 2025-10-16 15:08:29 +02:00
Alejandro Celaya
785f728afc
Merge pull request #2493 from acelaya-forks/fix-phpstan
Fix issue reported by phpstan in CrossDomainMiddleware
2025-10-16 10:16:33 +02:00
Alejandro Celaya
648696f778 Fix issue reported by phpstan in CrossDomainMiddleware 2025-10-16 10:08:24 +02:00
Alejandro Celaya
774a579a94 Add v4.5.3 to changelog 2025-10-10 10:29:06 +02:00
Alejandro Celaya
98bbb01165 Update coding standard 2025-10-06 08:46:34 +02:00
Alejandro Celaya
0bcb9e0438 Update changelog 2025-10-03 10:24:38 +02:00
Alejandro Celaya
edb8b57a48
Merge pull request #2489 from acelaya-forks/feature/cors-credentials-fix
Make sure Access-Control-Allow-Credentials is always set if configured
2025-10-03 10:23:02 +02:00
Alejandro Celaya
b01f271f72 Make sure Access-Control-Allow-Credentials is always set if configured 2025-10-03 10:15:27 +02:00
Alejandro Celaya
98b504a2de
Merge pull request #2484 from acelaya-forks/feature/memory-efficient-geolite
Make GeoLite db download memory efficient
2025-09-11 09:39:32 +02:00
Alejandro Celaya
075e6347b6 Make GeoLite db download memory efficient 2025-09-11 09:28:44 +02:00
Alejandro Celaya
92a70b8c11
Merge pull request #2477 from acelaya-forks/feature/frankenphp
Add a development FrankenPHP server
2025-08-28 09:01:41 +02:00
Alejandro Celaya
613c7b7368
Merge pull request #2480 from acelaya-forks/feature/garbage-collection
Garbage collect after every request
2025-08-27 09:41:38 +02:00
Alejandro Celaya
232f6e37c6 Ensure pipeline is not marked as failed if only v8.5 fails 2025-08-27 09:30:41 +02:00
Alejandro Celaya
c818d5603d Garbage collect after every request 2025-08-27 09:24:28 +02:00
Alejandro Celaya
766b227e47 Add a development FrankenPHP server 2025-08-26 08:52:22 +02:00
Alejandro Celaya
95be5a93fc
Merge pull request #2478 from acelaya-forks/feature/memory-leak-mitigation
Try to mitigate memory leaks when using RoadRunner
2025-08-24 11:23:57 +02:00
Alejandro Celaya
20c41690da Try to mitigate memory leaks when using RoadRunner 2025-08-24 11:18:40 +02:00
Alejandro Celaya
22b5fa5a83
Merge pull request #2474 from acelaya-forks/feature/symfony-lock
Update to symfony/lock ^7.3.2
2025-08-01 08:28:05 +02:00
Alejandro Celaya
0c4d1b6d2f Update to symfony/lock ^7.3.2 2025-08-01 08:21:37 +02:00
Alejandro Celaya
d2514b7555
Merge pull request #2470 from acelaya-forks/feature/release-4.5.0
Add v4.5.0 to changelog
2025-07-24 12:11:03 +02:00
Alejandro Celaya
2d5734fc8b Add v4.5.0 to changelog 2025-07-24 12:07:11 +02:00
Alejandro Celaya
478ac344ff
Merge pull request #2469 from acelaya-forks/feature/logs-encoding
Allow logs format to be configured as console or JSON
2025-07-24 10:01:36 +02:00
Alejandro Celaya
e40b82618a Allow logs format to be configured as console or JSON 2025-07-24 09:57:34 +02:00
Alejandro Celaya
51dd671174
Merge pull request #2467 from acelaya-forks/feature/nullable-match-value
Make RedirectCondition->matchValue nullable
2025-07-22 08:32:25 +02:00
Alejandro Celaya
5b5d0aae49 Make RedirectCondition->matchValue nullable 2025-07-22 08:28:09 +02:00
Alejandro Celaya
56df880a93
Merge pull request #2466 from acelaya-forks/feature/php-8.5
Run tests under PHP 8.5 in CI
2025-07-21 10:38:02 +02:00
Alejandro Celaya
afa509613a Run tests under PHP 8.5 in CI 2025-07-21 10:30:35 +02:00
Alejandro Celaya
3be49a25a0
Merge pull request #2465 from acelaya-forks/feature/redirect-cache-visibility
Allow redirect cache visibility to be configured
2025-07-21 10:21:36 +02:00
Alejandro Celaya
8b259b364d Allow redirect cache visibility to be configured 2025-07-21 10:13:17 +02:00
Alejandro Celaya
13d9b7b0a7
Merge pull request #2464 from acelaya-forks/feature/desktop-devices
Add support for more device types in device-specific redirects
2025-07-20 12:02:11 +02:00
Alejandro Celaya
2b33095392 Add support for more device types in device-specific redirects 2025-07-20 11:56:33 +02:00
Alejandro Celaya
3a1ce40a49
Merge pull request #2461 from acelaya-forks/feature/trusted-proxies
Allow trusted proxies to be provided via TRUSTED_PROXIES env var or config option
2025-07-18 08:32:48 +02:00
Alejandro Celaya
a68300f19a Fix phpstan report 2025-07-18 08:29:16 +02:00
Alejandro Celaya
3318987d63 Allow providing hop count via TRUSTED_PROXIES 2025-07-18 08:24:57 +02:00
Alejandro Celaya
1f825797f6 Allow trusted proxies to be provided via TRUSTED_PROXIES env var 2025-07-17 09:57:34 +02:00
Alejandro Celaya
650fafb7c4 Register ReverseForwardedAddressesMiddlewareDecorator via ServiceManager delegator 2025-07-17 09:47:02 +02:00
Alejandro Celaya
978e24d6fa
Merge pull request #2460 from acelaya-forks/feature/enhanced-query-param-rules
Add support for any-value and valueless query param redirect rules
2025-07-17 08:57:30 +02:00
Alejandro Celaya
c3d3cc6288 Test RedirectConditionType::isValid() in isolation 2025-07-17 08:51:59 +02:00
Alejandro Celaya
223901324f Enhance RedirectRuleHandlerTest with new query-param-related conditions 2025-07-17 08:44:19 +02:00
Alejandro Celaya
47293be85c Enhance RedirectConditionTest with new query-param-related conditions 2025-07-17 08:39:37 +02:00
Alejandro Celaya
18c4c39fee Add support for any-value and valueless query param redirect rules 2025-07-17 08:31:29 +02:00
Alejandro Celaya
e762d28b67
Merge pull request #2455 from acelaya-forks/feature/cors-customization
Add new CORS configuration options
2025-07-16 08:41:42 +02:00
Alejandro Celaya
f5c6bc8204 Update changelog 2025-07-16 08:39:12 +02:00
Alejandro Celaya
3369afe22c Add CorsOptions test 2025-07-16 08:29:57 +02:00
Alejandro Celaya
1d96cc0279 Update CrossDomainMiddleware test 2025-07-08 13:17:46 +02:00
Alejandro Celaya
cd4fcc9b0a Update shlink-installer 2025-07-08 13:07:04 +02:00
Alejandro Celaya
834bc4ae20 Allow credentials to be enabled in CORS 2025-07-08 10:36:12 +02:00
Alejandro Celaya
92d7a44cee Add new CORS configuration options 2025-07-05 10:34:50 +02:00
Alejandro Celaya
c8e3b3df0a Update changelog 2025-07-04 18:31:20 +02:00
Alejandro Celaya
77244b52c9
Merge pull request #2454 from acelaya-forks/feature/real-time-updates-options
Allow individual real-time updates topics to be enabled
2025-07-04 18:29:12 +02:00
Alejandro Celaya
9e93e34e12 Add test to cover when visit updates topics are disabled 2025-07-04 18:25:45 +02:00
Alejandro Celaya
733b2e5647 Add test to cover when short URL updates topic is disabled 2025-07-04 18:04:27 +02:00
Alejandro Celaya
26fef87f3b Add RealTimeUpdatesOptions test 2025-07-04 10:07:40 +02:00
Alejandro Celaya
f4aaf02d55 Reduce duplicated code between enumValues and enumNames 2025-07-04 09:52:35 +02:00
Alejandro Celaya
314a99862d Update to latest shlink-installer with real-time updates support 2025-07-03 18:35:14 +02:00
Alejandro Celaya
240d9df177 Validate topic names in RealTimeUpdateOptions 2025-07-03 14:34:27 +02:00
Alejandro Celaya
fb995f2bea Allow individual real-time updates topics to be enabled 2025-07-03 10:10:06 +02:00
Alejandro Celaya
436be1985c
Merge pull request #2452 from acelaya-forks/feature/invokable-command-poc
Use invokable commands approach on some API console commands
2025-06-26 08:46:20 +02:00
Alejandro Celaya
850e8574e9 Use invokable commands approach on some API console commands 2025-06-26 08:41:18 +02:00
Alejandro Celaya
c2743cb488
Merge pull request #2453 from acelaya-forks/feature/phpunit-warnings
Adjust tests to fix warnings
2025-06-26 08:40:10 +02:00
Alejandro Celaya
f1157aa177 Adjust tests to fix warnings 2025-06-24 19:47:18 +02:00
Alejandro Celaya
497429e685 Forward questions to the global discussions repo 2025-06-23 10:14:18 +02:00
Alejandro Celaya
2cad5dd435 Update to roadrunner 2025.1 2025-05-27 14:23:49 +02:00
Alejandro Celaya
f38f1ae5da
Merge pull request #2439 from acelaya-forks/feature/mercure-enabled
Add new MERCURE_ENABLED env var
2025-05-22 08:29:23 +01:00
Alejandro Celaya
9c1db35d81 Add new MERCURE_ENABLED env var 2025-05-22 09:20:50 +02:00
Alejandro Celaya
11b8943919
Merge pull request #2432 from acelaya-forks/feature/docker-env-syntax
Update syntax used for env vars in Dockerfiles
2025-05-06 12:25:14 +02:00
Alejandro Celaya
27d24a4f15 Update syntax used for env vars in Dockerfiles 2025-05-06 11:56:49 +02:00
Alejandro Celaya
b2dbc4cf52
Fix typo in Dockerfile 2025-05-04 15:57:29 +02:00
Alejandro Celaya
1a7a745f2e
Update Dockerfile marking image-related extensions as delegated 2025-05-04 15:56:44 +02:00
Alejandro Celaya
99bc1a21dd
Merge pull request #2425 from acelaya-forks/feature/command-exit-codes
Replace ExitCode with standard symfony Command constants
2025-04-22 19:49:16 +02:00
Alejandro Celaya
cea8a982e2 Replace ExitCode with standard symfony Command constants 2025-04-22 12:07:41 +02:00
Alejandro Celaya
8bd1c6a79a
Merge pull request #2423 from acelaya-forks/feature/remove-bootstrap
Remove references to bootstrap from error templates
2025-04-22 09:12:08 +02:00
Alejandro Celaya
71a3b993b1 Remove references to bootstrap from error templates 2025-04-22 09:09:52 +02:00
Alejandro Celaya
6e25e3c31d
Merge pull request #2422 from acelaya-forks/feature/deprecate-qr-codes
Deprecate QR code generation endpoint
2025-04-22 08:50:34 +02:00
Alejandro Celaya
b15e832cf4 Deprecate QR code generation endpoint 2025-04-22 08:47:37 +02:00
Alejandro Celaya
851929ebef
Merge pull request #2403 from acelaya-forks/feature/phpunit-phpstan-fixes
Fix compatibility with PHPUnit 12.0.9 and phpstan-phpunit
2025-03-24 19:36:44 +01:00
Alejandro Celaya
87d5f9bc75 Fix compatibility with PHPUnit 12.0.9 and phpstan-phpunit 2025-03-24 19:33:52 +01:00
Alejandro Celaya
b7d9ba8258
Merge pull request #2397 from acelaya-forks/feature/endroid-fix
Fix error intrduced by endroid/qr-code 6.0.4
2025-03-20 09:19:58 +01:00
Alejandro Celaya
6526cf8c44 Fix error intrduced by endroid/qr-code 6.0.4 2025-03-20 09:16:53 +01:00
Alejandro Celaya
a85afb2bee
Merge pull request #2394 from acelaya-forks/feature/fix-artifact-removal
Update geekyeggo/delete-artifact action to v5
2025-03-14 18:00:47 +01:00
Alejandro Celaya
8b4067efbe Update geekyeggo/delete-artifact action to v5 2025-03-14 17:57:55 +01:00
Alejandro Celaya
c7c2272fab Update changelog 2025-03-14 17:53:23 +01:00
Alejandro Celaya
bc77750713
Merge pull request #2392 from wuuei/patch-1
Fix Matomo country logging by sending country code instead of country
2025-03-14 17:51:37 +01:00
Alejandro Celaya
1ceb38f50b Test actual arguments set to matomo tracker when sending visits 2025-03-14 17:40:37 +01:00
wuuei
d273b56144 Lock "endroid/qr-code" to 6.0.3 so that unit tests complete 2025-03-14 15:21:55 +00:00
wuuei
5cd7305666 Fix code style to resolve failing check 2025-03-14 15:20:49 +00:00
wuuei
3040a22c02
Fix Matomo country logging by sending country code instead of country name
Matomo expects the country code in lowercase for accurate logging and proper flag display
2025-03-13 15:33:00 +01:00
Alejandro Celaya
5eb1808217
Update CHANGELOG.md with V4.4.5 2025-03-01 09:14:37 +01:00
Alejandro Celaya
5eb14c5315
Merge pull request #2375 from acelaya-forks/feature/deprecation-error-reporting
Disable deprecation warnings when running in production envs
2025-02-21 21:18:44 +01:00
Alejandro Celaya
a18360a4d6 Disable deprecation warnings when running in production envs 2025-02-21 21:13:29 +01:00
Alejandro Celaya
af2d67695b
Merge pull request #2370 from acelaya-forks/feature/missing-join-fix
Fix 500 error when listing non-orphan visits with short-url-depending API key
2025-02-19 19:37:36 +01:00
Alejandro Celaya
449a588796 Fix 500 error when listing non-orphan visits with short-url-depending API key 2025-02-19 19:33:44 +01:00
Alejandro Celaya
7bbc938743
Merge pull request #2369 from acelaya-forks/feature/redis-cluster-fix
Downgrade to symfony/lock 7.1.6
2025-02-19 17:55:53 +01:00
Alejandro Celaya
766758ff9b Downgrade to symfony/lock 7.1.6 2025-02-19 17:45:52 +01:00
Alejandro Celaya
63d943d59d
Merge pull request #2363 from acelaya-forks/feature/find-url-perf
Fix unique_short_code_plus_domain index in Microsoft SQL
2025-02-15 11:24:26 +01:00
Alejandro Celaya
053e1f3073 Update changelog 2025-02-15 11:19:30 +01:00
Alejandro Celaya
f3da345bf3 Fix unique_short_code_plus_domain index in Microsoft SQL 2025-02-15 11:17:14 +01:00
Alejandro Celaya
745255736a Simplify query to find short URL when domain is null 2025-02-14 10:20:50 +01:00
Alejandro Celaya
8fd53afe3f
Merge pull request #2361 from acelaya-forks/feature/lock-downgrade
Downgrade symfony/lock to v7.2.0 to work around redis issue
2025-02-14 08:52:33 +01:00
Alejandro Celaya
259635ea2a Downgrade symfony/lock to v7.2.0 to work around redis issue 2025-02-14 08:40:06 +01:00
Alejandro Celaya
a1f2e6dc5c
Merge pull request #2359 from acelaya-forks/feature/multi-proxy-fix
Workaround for IP resolution from x-Forwarded-For with multiple proxies
2025-02-13 22:03:36 +01:00
Alejandro Celaya
81e07bf08d
Merge pull request #2358 from acelaya-forks/feature/phpunit-12
Update to PHPUnit 12
2025-02-13 21:59:00 +01:00
Alejandro Celaya
c650a3e665 Workaround for IP resolution from x-Forwarded-For with multiple proxies 2025-02-13 21:52:38 +01:00
Alejandro Celaya
65c01034ff Update to PHPUnit 12 2025-02-13 10:35:58 +01:00
Alejandro Celaya
48f910aaaa
Merge pull request #2355 from acelaya-forks/feature/openapi-warnings
Remove suppressed warnings when running openapi tools
2025-02-05 08:43:28 +01:00
Alejandro Celaya
e511e15a87 Remove suppressed warnings when running openapi tools 2025-02-05 08:39:22 +01:00
Alejandro Celaya
ed09bf90eb Tag v4.4.2 in changelog 2025-01-29 12:05:53 +01:00
Alejandro Celaya
0ddfcb75dd
Merge pull request #2347 from acelaya-forks/feature/docker-arm
Get back docker image building for ARM architecture
2025-01-29 12:02:19 +01:00
Alejandro Celaya
193be55f0c Get back docker image building for ARM architecture 2025-01-29 11:59:42 +01:00
Alejandro Celaya
7ffb64eee1 Do not build docker image for ARM 2025-01-28 15:51:20 +01:00
Alejandro Celaya
0a2cc554c6 Build docker image with buildx 0.19.2 2025-01-28 15:38:47 +01:00
Alejandro Celaya
af783dea57 Add v4.4.1 to changelog 2025-01-28 10:12:15 +01:00
Alejandro Celaya
a68a17f6b4
Merge pull request #2343 from acelaya-forks/feature/defensive-title-encoding
Fix error when creating short URL for page with unsupported encoding
2025-01-28 10:11:04 +01:00
Alejandro Celaya
e9fe1ac5d4 Fix error when creating short URL for page with unsupported encoding 2025-01-28 10:04:30 +01:00
Alejandro Celaya
88e97f18ad
Merge pull request #2342 from acelaya-forks/feature/too-many-connections
Close connections after every async job that uses the db
2025-01-27 15:48:22 +01:00
Alejandro Celaya
3372a2a9c8 Close connections after every async job that uses the db 2025-01-27 15:45:37 +01:00
Alejandro Celaya
f02a8c876c
Merge pull request #2340 from acelaya-forks/feature/update-shlink-deps
Update shlink packages
2025-01-25 16:16:42 +01:00
Alejandro Celaya
1549509eb8 Update shlink packages 2025-01-25 16:13:40 +01:00
Alejandro Celaya
62fde5a8e2 Update changelog 2025-01-13 08:47:19 +01:00
Alejandro Celaya
221e061ea6
Merge pull request #2332 from MaZe3D/develop
Add ADDRESS environment vairable to define the listening interface.
2025-01-13 08:45:20 +01:00
Mark Orlando Zeller
9ad565f8c8 Add ADDRESS environment vairable to define the listening interface. 2025-01-10 22:10:51 +01:00
Alejandro Celaya
11fa28e489
Merge pull request #2316 from acelaya-forks/feature/v4.4
Add v4.4.0 to changelog and update dependencies
2024-12-27 16:27:06 +01:00
Alejandro Celaya
d7e51b388e Add v4.4.0 to changelog and update dependencies 2024-12-27 16:24:25 +01:00
Alejandro Celaya
5ef2df3d53
Merge pull request #2315 from acelaya-forks/feature/import-redirect-rules
Implement logic to import redirect rules from other Shlink instances
2024-12-22 18:50:10 +01:00
Alejandro Celaya
9c251b3646 Update changelog 2024-12-22 18:41:58 +01:00
Alejandro Celaya
2807b9ce2f Fix ImportedLinksProcessorTest 2024-12-22 18:41:03 +01:00
Alejandro Celaya
2f39aff2fe Implement logic to import redirect rules from other Shlink instances 2024-12-22 12:42:06 +01:00
Alejandro Celaya
b8d7917691
Merge pull request #2314 from acelaya-forks/feature/database-ssl
Support encrypted connections to MySQL/Maria and Postgres
2024-12-20 09:54:59 +01:00
Alejandro Celaya
d228c16f82 Fix test for ip middleware 2024-12-20 09:52:30 +01:00
Alejandro Celaya
c34bfac6b1 Update installer with support for DB_USE_ENCRYPTION option 2024-12-20 09:29:28 +01:00
Alejandro Celaya
4e7d09035a Support encrypted connections to MySQL/Maria and Postgres 2024-12-19 09:00:01 +01:00
Alejandro Celaya
83570f5c25
Merge pull request #2313 from acelaya-forks/feature/qr-disable-logo
Allow QR code logo to be individually disabled
2024-12-18 09:14:47 +01:00
Alejandro Celaya
6ad8b03850 Allow QR code logo to be individually disabled 2024-12-18 09:10:53 +01:00
Alejandro Celaya
736e09adfe
Merge pull request #2310 from acelaya-forks/feature/less-restrictive-custom-slugs
Be less restrictive on what characters are disallowed in custom slugs
2024-12-17 18:08:51 +01:00
Alejandro Celaya
e80af78e09 Be less restrictive on what characters are disallowed in custom slugs 2024-12-17 18:04:38 +01:00
Alejandro Celaya
d533adf7ce
Merge pull request #2308 from acelaya-forks/feature/geolocation-updates
Improve how geolocation DB files are downloaded/updated
2024-12-16 20:21:35 +01:00
Alejandro Celaya
509ef668e6 Fix GeolocationDbUpdater test 2024-12-16 19:50:06 +01:00
Alejandro Celaya
e715a0fb6f Track reason for which a geolocation db download was attempted 2024-12-16 09:23:30 +01:00
Alejandro Celaya
72a962ec6d Handle differently when trying to update geolocation and already in progress 2024-12-15 12:03:01 +01:00
Alejandro Celaya
853c50a819 Fix some cases of database download in GeolocationDbUpdater 2024-12-15 11:34:38 +01:00
Alejandro Celaya
f10a9d3972 Simplify geolocation_db_updates indexes 2024-12-15 10:08:22 +01:00
Alejandro Celaya
a77e07f906 Refactor geolocation download logic based on database table 2024-12-15 10:05:32 +01:00
Alejandro Celaya
d4d97c3182 Create new table to track geolocation updates 2024-12-13 10:33:53 +01:00
Alejandro Celaya
55724dbff6
Merge pull request #2306 from acelaya-forks/feature/update-docker-images
Update docker images to Alpine 3.21
2024-12-12 09:06:49 +01:00
Alejandro Celaya
9e34183901 Update docker images to Alpine 3.21 2024-12-12 08:52:01 +01:00
Alejandro Celaya
88c283952c
Merge pull request #2304 from acelaya-forks/feature/geolocation-services-refactor
Move GeolocationDbUpdater to Core module
2024-12-11 08:58:23 +01:00
Alejandro Celaya
2ede615da8 Fix DownloadGeoLiteDbCommandTest 2024-12-11 08:50:56 +01:00
Alejandro Celaya
84d12f6811 Move GeolocationDbUpdaterTest to Core module 2024-12-11 08:47:13 +01:00
Alejandro Celaya
4f3c2c7d2d Fix UpdateGeoLiteDbTest 2024-12-11 08:35:24 +01:00
Alejandro Celaya
b8ac9f3673 Add more strict parameter for GeolocationDbUpdater 2024-12-11 08:27:56 +01:00
Alejandro Celaya
06c0a94b31 Move GeolocationDbUpdater from CLI to Core module 2024-12-10 10:58:08 +01:00
Alejandro Celaya
5d12b1d952
Merge pull request #2302 from acelaya-forks/feature/openapi-names
Use the openapi terminology over swagger
2024-12-06 11:40:15 +01:00
Alejandro Celaya
85c4c09afa Use the openapi terminology over swagger 2024-12-06 11:36:47 +01:00
Alejandro Celaya
e7c83d0b38
Merge pull request #2300 from acelaya-forks/feature/drop-8.2-support
Drop support for PHP 8.2
2024-12-02 09:21:50 +01:00
Alejandro Celaya
58de998596 Drop support for PHP 8.2 2024-12-02 09:16:15 +01:00
Alejandro Celaya
bfaab6c494
Merge pull request #2298 from acelaya-forks/feature/ignore-extra-path
Allow the extra path to be ignored when redirecting
2024-12-01 12:37:56 +01:00
Alejandro Celaya
d83081f4e9 Update shlink-installer 2024-12-01 12:28:29 +01:00
Alejandro Celaya
c65349d265 Allow the extra path to be ignored when redirecting 2024-12-01 09:56:09 +01:00
Alejandro Celaya
e74ee793a0
Merge pull request #2297 from acelaya-forks/feature/docker-php-8.4
Update docker images to PHP 8.4
2024-11-30 18:35:03 +01:00
Alejandro Celaya
ede58efe96 Update docker images to PHP 8.4 2024-11-30 13:53:19 +01:00
Alejandro Celaya
3f30af4794
Merge pull request #2294 from acelaya-forks/feature/user-agent
Migrate from mobiledetectlib to phpuseragentparser
2024-11-28 12:11:29 +01:00
Alejandro Celaya
6331fa3ed3 Migrate from mobiledetectlib to phpuseragentparser 2024-11-28 12:05:10 +01:00
Alejandro Celaya
d121d4d496
Merge pull request #2289 from acelaya-forks/feature/delete-old-migrations
Delete some old migrations
2024-11-28 09:00:40 +01:00
Alejandro Celaya
8499087a3b Move DEFAULT_DOMAIN constant to domains module 2024-11-28 08:54:29 +01:00
Alejandro Celaya
bb72c96ebb Delete some old migrations 2024-11-26 10:17:28 +01:00
Alejandro Celaya
557c74286b Add v4.3.1 to changelog 2024-11-25 23:45:02 +01:00
Alejandro Celaya
67abe21716
Merge pull request #2287 from acelaya-forks/feature/ms-index-fix
Fix columns order in unique_short_code_plus_domain index in MSSQL
2024-11-25 23:43:55 +01:00
Alejandro Celaya
33cea36b15 Fix columns order in unique_short_code_plus_domain index in MSSQL 2024-11-25 22:48:04 +01:00
Alejandro Celaya
4e8f3f737a
Merge pull request #2286 from acelaya-forks/feature/crawler-detect
Use jaybizzle/crawler-detect instead of acelaya/crawler-detect
2024-11-25 22:21:14 +01:00
Alejandro Celaya
35b835ec7b Use jaybizzle/crawler-detect instead of acelaya/crawler-detect 2024-11-25 22:17:15 +01:00
Alejandro Celaya
eff4f1fca3
Merge pull request #2284 from acelaya-forks/feature/rka-ip-address
Go back to using akrabat/ip-address-middleware instead of acelaya/ip-address-middleware
2024-11-25 09:31:43 +01:00
Alejandro Celaya
6f6388b2fc Go back to using akrabat/ip-address-middleware instead of acelaya/ip-address-middleware 2024-11-25 09:23:43 +01:00
Alejandro Celaya
19f56e7ab0 Add v4.3.0 to changelog 2024-11-24 14:26:09 +01:00
Alejandro Celaya
6a96b72b94 Add real version constraints for Shlink packages 2024-11-24 14:23:12 +01:00
Alejandro Celaya
7634f55587
Merge pull request #2282 from acelaya-forks/feature/track-redirect-url
Add redirect_url field to track where a visitor is redirected for a visit
2024-11-24 14:20:12 +01:00
Alejandro Celaya
571a4643ab Update changelog 2024-11-24 14:13:59 +01:00
Alejandro Celaya
d5544554ef Improve API docs description for redirectUrl fields 2024-11-24 14:08:23 +01:00
Alejandro Celaya
85065c9330 Test behavior to track redirect URL 2024-11-24 14:05:33 +01:00
Alejandro Celaya
86cc2b717c Save where a visitor is redirected for any kind of tracked visit 2024-11-24 13:21:48 +01:00
Alejandro Celaya
89f70114e4 Fix typo in migration 2024-11-24 13:18:32 +01:00
Alejandro Celaya
8274525f75 Add redirect_url field to track where a visitor is redirected for a visit 2024-11-24 12:53:49 +01:00
Alejandro Celaya
fef512a7a3
Merge pull request #2280 from acelaya-forks/feature/php-8.4-support
Feature/php 8.4 support
2024-11-24 11:41:59 +01:00
Alejandro Celaya
deb9d4bdc7 Update docker images to Alpine 3.20 2024-11-24 11:37:08 +01:00
Alejandro Celaya
259aadfdb2 Update changelog 2024-11-24 11:05:36 +01:00
Alejandro Celaya
fe660654ed Add PHP 8.4 to the release pipeline 2024-11-24 11:04:41 +01:00
Alejandro Celaya
b2fc19af44 Replace akrabat/ip-address-middleware with acelaya/ip-address-middleware 2024-11-24 11:04:14 +01:00
Alejandro Celaya
7434616a8d Update mobiledetect/mobiledetectlib to a commit including PHP 8.4 fixes 2024-11-24 10:55:55 +01:00
Alejandro Celaya
fbf1aabcf5 Replace jaybizzle/crawler-detect with acelaya/crawler-detect 2024-11-24 10:49:44 +01:00
Alejandro Celaya
8ee905882f
Merge pull request #2277 from acelaya-forks/feature/ip-address-factory
Use `IpAddressFactory` from akrabat/ip-address-middleware
2024-11-22 09:13:01 +01:00
Alejandro Celaya
2946b630c5 Use IpAddressFactory from akrabat/ip-address-middleware 2024-11-22 09:01:27 +01:00
Alejandro Celaya
b2bfe9799a
Merge pull request #2276 from acelaya-forks/feature/visits-list-duplication
Reduce duplication in actions listing visits
2024-11-20 09:51:54 +01:00
Alejandro Celaya
d7e300e2d5 Reduce duplication in actions listing visits 2024-11-20 09:48:12 +01:00
Alejandro Celaya
0c75202936
Merge pull request #2273 from acelaya-forks/feature/remove-laminas-config
Remove dependency on laminas config
2024-11-19 20:15:28 +01:00
Alejandro Celaya
81bed53f90 Update Shlink libraries to remove dependency on laminas-config 2024-11-19 20:12:38 +01:00
Alejandro Celaya
a56ff1293e Remove direct dependency on laminas/laminas-config 2024-11-19 09:18:06 +01:00
Alejandro Celaya
c323bfcd63
Merge pull request #2272 from acelaya-forks/feature/geolocate-localhost-fix
Make sure IpGeolocationMiddleware skips localhost
2024-11-19 09:14:45 +01:00
Alejandro Celaya
f57f159002 Remove no longer used Visit::isLocatable method 2024-11-19 09:10:47 +01:00
Alejandro Celaya
fa08014226 Make sure IpGeolocationMiddleware skips localhost 2024-11-19 09:08:04 +01:00
Alejandro Celaya
052c9e76a1
Merge pull request #2271 from acelaya-forks/feature/api-key-domain-exceptions
Use more meaningful domain exceptions to represent ApiKeyService thrown errors
2024-11-18 09:59:25 +01:00
Alejandro Celaya
8298ef36f8 Use more meaningful domain exceptions to represent ApiKeyService thrown errors 2024-11-18 09:51:27 +01:00
Alejandro Celaya
b11d5c6864 Do not ignore platform reqs when using PHP 8.4 2024-11-18 08:50:20 +01:00
Alejandro Celaya
08394431f8
Merge pull request #2269 from acelaya-forks/feature/no-php-8.4-error
Do not allow pipelines to continue on error
2024-11-17 10:25:33 +01:00
Alejandro Celaya
a9ae4a24d0 Do not allow pipelines to continue on error 2024-11-17 10:15:25 +01:00
Alejandro Celaya
9b7b91402c
Merge pull request #2268 from acelaya-forks/feature/delete-visits-fix
Fix visits counts not being deleted when deleting short URL or orphan visits
2024-11-15 19:26:57 +01:00
Alejandro Celaya
178a99b993 Fix visits counts not being deleted when deleting short URL or orphan visits 2024-11-15 19:22:29 +01:00
Alejandro Celaya
a8f046dfff
Merge pull request #2266 from acelaya-forks/feature/geolocation-middleware
Feature/geolocation middleware
2024-11-15 10:47:18 +01:00
Alejandro Celaya
42ff0d5b69 Create IpGeolocationMiddlewareTest 2024-11-15 10:17:56 +01:00
Alejandro Celaya
6aaea2ac26 Simplify logic in RedirectRule when checking geolocation conditions 2024-11-15 09:00:59 +01:00
Alejandro Celaya
b5ff568651 Use IpGeolocationMiddleware to geolocate visitors instead of LocateVisit event 2024-11-15 08:55:43 +01:00
Alejandro Celaya
4a0b7e3fc9 Refactor Visitor model and allow a Location object to be passed to it 2024-11-14 14:48:18 +01:00
Alejandro Celaya
1fee745786
Merge pull request #2263 from acelaya-forks/feature/geolocation-city-name-redirects
Add support for city name dynamic redirects
2024-11-14 10:07:01 +01:00
Alejandro Celaya
a6e0916272 Add support for city name dynamic redirects 2024-11-14 09:58:53 +01:00
Alejandro Celaya
dbef32ffcb
Merge pull request #2257 from acelaya-forks/feature/geolocation-country-code-redirects
Add new geolocatio-country-code redirect condition type
2024-11-14 09:43:10 +01:00
Alejandro Celaya
7ddb3e7a70 Add tests covering country code validation 2024-11-14 09:40:10 +01:00
Alejandro Celaya
fd34332e69 Improve ExtraPathRedirectMiddlewareTest 2024-11-14 09:28:10 +01:00
Alejandro Celaya
51d838870d Add reference to ISO 3166-1 alpha-2 country codes wikipedia page 2024-11-14 09:14:17 +01:00
Alejandro Celaya
4619ebd014 After tracking a visit, set its location in the request as attribute 2024-11-14 08:21:16 +01:00
Alejandro Celaya
f2371b6124 Update RedirectRuleHandlerTest 2024-11-13 10:01:52 +01:00
Alejandro Celaya
b5b5f92eda Add validation for country-code redirect conditions 2024-11-12 10:25:39 +01:00
Alejandro Celaya
781c083c9f Add new geolocatio-country-code redirect condition type 2024-11-12 10:25:39 +01:00
Alejandro Celaya
a444ed0246
Merge pull request #2258 from acelaya-forks/feature/phpstan-2
Update to PHPStan 2.0
2024-11-12 10:25:02 +01:00
Alejandro Celaya
9a69d06531 Update to PHPStan 2.0 2024-11-12 10:22:23 +01:00
Alejandro Celaya
15cb3bb73c
Merge pull request #2256 from acelaya-forks/feature/unecessary-flush
Remove unnecessary flush calls when used in wrapInTransaction
2024-11-11 09:35:30 +01:00
Alejandro Celaya
7ca605e216 Remove unnecessary flush calls when used in wrapInTransaction 2024-11-11 09:31:23 +01:00
Alejandro Celaya
59a4704658
Merge pull request #2255 from acelaya-forks/feature/expose-tracked-visits
Return `Visit` object created when tracking a visit successfully
2024-11-11 09:19:20 +01:00
Alejandro Celaya
48ecef3436 Update RequestTracker so that its methods return the new Visit instance, if any 2024-11-11 08:58:16 +01:00
Alejandro Celaya
a5a98bd578 Update VisitsTracker so that its methods return the new Visit instance, if any 2024-11-11 08:51:55 +01:00
Alejandro Celaya
12a08cb373
Merge pull request #2253 from acelaya-forks/feature/api-key-improvements
Feature/api key improvements
2024-11-09 12:23:10 +01:00
Alejandro Celaya
3c6f12aec6 Ensure auto-generated name API keys do not throw duplicated name 2024-11-09 12:07:07 +01:00
Alejandro Celaya
d228b88e51 Lock transaction to avoid race conditions when renaming an API key 2024-11-09 11:16:36 +01:00
Alejandro Celaya
95685d958d Update to latest test utils 2024-11-09 11:02:10 +01:00
Alejandro Celaya
1a278eaf07
Merge pull request #2252 from acelaya-forks/feature/readonly-classes
Make classes readonly when possible
2024-11-09 09:58:56 +01:00
Alejandro Celaya
72f1e243b5 Make classes readonly when possible 2024-11-09 09:55:51 +01:00
Alejandro Celaya
d6b103de83
Merge pull request #2251 from acelaya-forks/feature/inject-repos
Feature/inject repos
2024-11-09 09:54:06 +01:00
Alejandro Celaya
fca3891819 Inject ShortUrlRepository in ShortCodeUniquenessHelper 2024-11-09 09:47:47 +01:00
Alejandro Celaya
3ec24e3c67 Inject ShortUrlRepository in UrlShortener 2024-11-09 09:43:55 +01:00
Alejandro Celaya
532102e662 Inject ShortUrlRepository in ShortUrlResolver 2024-11-09 09:39:56 +01:00
Alejandro Celaya
fcd82522ab
Merge pull request #2250 from acelaya-forks/feature/inject-tag-repo
Inject TagRepository in TagService, instead of getting it from EntityManager
2024-11-09 09:39:03 +01:00
Alejandro Celaya
102169b6c7 Inject DomainRepository in DomainService 2024-11-09 09:34:24 +01:00
Alejandro Celaya
dba9302f78 Inject TagRepository in TagService, instead of getting it from EntityManager 2024-11-09 09:25:01 +01:00
Alejandro Celaya
92ad6d2732
Merge pull request #2249 from acelaya-forks/feature/hash-api-keys
Feature/hash api keys
2024-11-09 09:14:38 +01:00
Alejandro Celaya
7e573bdb9b Add tests for RenameApiKeyCOmmand and ApiKeyMeta 2024-11-08 09:58:02 +01:00
Alejandro Celaya
6f837b3b91 Move logic to determine if a new key has a duplicated name to the APiKeyService 2024-11-08 09:03:50 +01:00
Alejandro Celaya
b08c498b13 Create command to rename API keys 2024-11-08 08:47:49 +01:00
Alejandro Celaya
a661d05100 Allow API keys to be renamed 2024-11-08 08:25:07 +01:00
Alejandro Celaya
9e6f129de6 Make sure a unique name is required by api-key:generate command 2024-11-07 14:52:06 +01:00
Alejandro Celaya
4c1ff72438 Add method to check if an API exists for a given name 2024-11-07 09:55:10 +01:00
Alejandro Celaya
6f95acc202 Inject ApiKeyRepository in ApiKeyService 2024-11-07 09:34:42 +01:00
Alejandro Celaya
bd73362c94 Update api-key:disable command to allow passing a name 2024-11-06 20:10:06 +01:00
Alejandro Celaya
f6d70c599e Make name required in ApiKey entity 2024-11-06 08:57:10 +01:00
Alejandro Celaya
1b9c8377ae Hash existing API keys, and do checks against the hash 2024-11-05 23:27:39 +01:00
Alejandro Celaya
9f6975119e Show only API key name in short URLs list 2024-11-05 22:52:01 +01:00
Alejandro Celaya
a094be2b9e Fall back API key names to auto-generated keys 2024-11-05 11:26:39 +01:00
Alejandro Celaya
819a535bfe Create migration to set API keys in name column 2024-11-05 11:08:11 +01:00
Alejandro Celaya
e4fe7adf00
Merge pull request #2248 from acelaya-forks/feature/api-key-simplification
Simplify ApiKey entity by exposing key as a readonly prop
2024-11-04 23:17:17 +01:00
Alejandro Celaya
79c5418ac2 Simplify ApiKey entity by exposing key as a readonly prop 2024-11-04 14:22:39 +01:00
Alejandro Celaya
b5010e4d8c
Merge pull request #2246 from acelaya-forks/feature/nanoid-2
Update to hidehalo/nanoid-php 2.0
2024-11-04 08:55:17 +01:00
Alejandro Celaya
3085fa76cf Update to hidehalo/nanoid-php 2.0 2024-11-04 08:50:58 +01:00
Alejandro Celaya
1fd7d58084 Update Bluesky handle 2024-11-03 11:38:31 +01:00
Alejandro Celaya
eae001a34a Rename ShortUrlWithVisitsSummary to ShortUrlWithDeps 2024-11-03 11:38:31 +01:00
Alejandro Celaya
d7ecef94f2 Avoid selecting domains for every short URL in list 2024-11-03 11:38:31 +01:00
Alejandro Celaya
98364a1aae Update to mlocati/ip-lib 1.18.1 2024-11-03 11:38:31 +01:00
Alejandro Celaya
9ccb866e5e Display warnings and deprecations in all test suites 2024-11-03 11:38:31 +01:00
Alejandro Celaya
3f1d61e01e Update to PHP coding standard 2.4.0 2024-11-03 11:38:31 +01:00
Alejandro Celaya
93a277a94d Allow short URLs to be filtered by domain from the command line 2024-11-03 11:38:30 +01:00
Alejandro Celaya
a10ca655a2 Cover domain filtering in ListShortUrls API test 2024-11-03 11:37:59 +01:00
Alejandro Celaya
bb270396b6 Allow short URLs list to be filtered by domain authority 2024-11-03 11:37:59 +01:00
Alejandro Celaya
525a306ec6 Create constant representing default domain identifier 2024-11-03 11:37:59 +01:00
Alejandro Celaya
1dd71d2ee7 Update changelog 2024-11-03 11:37:59 +01:00
Alejandro Celaya
ac2e249746 Update swagger Short URL examples to include forwardQuery and hasRedirectRules 2024-11-03 11:37:16 +01:00
Alejandro Celaya
af569ad7a5 Fix PHPStan rules 2024-11-03 11:37:16 +01:00
Alejandro Celaya
bf121c58ba Fix API tests 2024-11-03 11:37:16 +01:00
Alejandro Celaya
d2403367b5 Fix PublishingUpdatesGeneratorTest 2024-11-03 11:37:16 +01:00
Alejandro Celaya
84a187a26f Include left join with domains when listing short URLs to avoid N+1 SELECT problem 2024-11-03 11:37:15 +01:00
Alejandro Celaya
3149adebdb Expose the fact that a short URL has redirect rules attached to it 2024-11-03 11:36:50 +01:00
Alejandro Celaya
228bf093d3
Merge pull request #2245 from acelaya-forks/feature/fix-redis-7.4-scan
Update to shlink-common 6.5 to fix integration with redis 7.4
2024-11-03 11:35:50 +01:00
Alejandro Celaya
26589e6126 Update to shlink-common 6.5 to fix integration with redis 7.4 2024-11-03 11:32:44 +01:00
Alejandro Celaya
02e48ae665
Merge pull request #2237 from shlinkio/develop
Release 4.2.4
2024-10-27 08:48:05 +01:00
Alejandro Celaya
0d627ce808 Set user to 0 in database containers when running in CI 2024-10-27 08:45:11 +01:00
Alejandro Celaya
99639b9844 Depend on actual versions for shlink packages 2024-10-27 08:36:57 +01:00
Alejandro Celaya
0c3c7ff3b2 Add v4.2.4 to changelog 2024-10-27 08:23:38 +01:00
Alejandro Celaya
d7423585ff Build docker image in CI using reusable workflow 2024-10-26 10:25:11 +02:00
Alejandro Celaya
7de07a9cd4
Merge pull request #2236 from acelaya-forks/feature/normalize-composer-json
Feature/normalize composer json
2024-10-24 14:25:01 +02:00
Alejandro Celaya
2a734b5d89 Ensure proper env vars are promoted for dev and test envs 2024-10-24 14:20:49 +02:00
Alejandro Celaya
4520afb271 Normalize composer.json scripts with composer capabilities 2024-10-24 14:08:48 +02:00
Alejandro Celaya
e7a9ad1db0
Merge pull request #2224 from acelaya-forks/feature/dev-config-as-env
Migrate dev-specific configuration to env vars via .env file
2024-10-24 12:01:13 +02:00
Alejandro Celaya
84860539ce Ensure dev env files are not accidentally leaked to locally built docker images 2024-10-24 11:58:04 +02:00
Alejandro Celaya
2901fe8b7b Reduce duplication in CLI tests 2024-10-24 11:50:06 +02:00
Alejandro Celaya
f9694333c5 Add ADR for transition to env vars for dev configs 2024-10-24 11:44:05 +02:00
Alejandro Celaya
fc1f35ad59 Update CONTRIBUTING file removing references to old local config files 2024-10-24 10:12:34 +02:00
Alejandro Celaya
9a58748581 Get LC_ALL env var back to docker compose 2024-10-24 10:00:57 +02:00
Alejandro Celaya
45e108d21e Load dev env as a PHP array instead of an env file 2024-10-24 09:59:13 +02:00
Alejandro Celaya
f4da9c1fcc Update dependencies to stop using cuyz/valinor 2024-10-24 09:22:44 +02:00
Alejandro Celaya
a3ea8f56dd Remove app_options config 2024-10-24 08:49:58 +02:00
Alejandro Celaya
f3244b35e3 Remove remaining local config files 2024-10-23 10:53:09 +02:00
Alejandro Celaya
442eea0ea7 Add script to run CLI tests that loads and exports test env vars 2024-10-23 10:16:38 +02:00
Alejandro Celaya
46601443f5 Load specific env file when running API tests 2024-10-23 09:17:00 +02:00
Alejandro Celaya
c0200317dd Load dev env vars via roadrunner instead of docker compose 2024-10-22 15:31:53 +02:00
Alejandro Celaya
c8e5196aab Remove dependencies on url_shortener raw config 2024-10-22 15:15:41 +02:00
Alejandro Celaya
b991b1699e Define unique dev .env file 2024-10-22 15:15:41 +02:00
Alejandro Celaya
582033ceb3 Migrate dev-specific configuration to env vars via .env file 2024-10-22 15:15:41 +02:00
Alejandro Celaya
549a8d8837
Merge pull request #2233 from acelaya-forks/feature/endroid-qr-code-6
Update to endroid/qr-code 6.0
2024-10-22 09:06:30 +02:00
Alejandro Celaya
5fb6c8708c Update to endroid/qr-code 6.0 2024-10-22 09:02:32 +02:00
Alejandro Celaya
7ee757243a
Merge pull request #2230 from acelaya-forks/feature/xdebug-coverage
Switch to xdebug for code coverage reports
2024-10-21 12:01:29 +02:00
Alejandro Celaya
044efe6ee4 Switch to xdebug for code coverage reports 2024-10-21 11:54:45 +02:00
Alejandro Celaya
9b16749737 Remove twitter badge from readme 2024-10-17 16:27:38 +02:00
Alejandro Celaya
6d51ff831f
Merge pull request #2228 from acelaya-forks/feature/docker-signals
Feature/docker signals
2024-10-17 15:09:08 +02:00
Alejandro Celaya
0635615149 Run RoadRunner in docker with exec to ensure signals are properly handled 2024-10-17 15:03:55 +02:00
Alejandro Celaya
51de4b17c0
Merge pull request #2227 from shlinkio/develop
Release 4.2.3
2024-10-17 09:41:21 +02:00
Alejandro Celaya
615b443652
Merge pull request #2226 from acelaya-forks/feature/fix-qr-codes
Update to shlink-config 3.2.1, which fixes skipping config options with null value
2024-10-17 09:37:21 +02:00
Alejandro Celaya
4b7b530f49 Update to shlink-config 3.2.1, which fixes skipping config options with null value 2024-10-17 09:33:53 +02:00
Alejandro Celaya
fa7969c746
Merge pull request #2222 from shlinkio/develop
Release 4.2.2
2024-10-14 09:50:13 +02:00
Alejandro Celaya
aef04af4f0
Merge pull request #2220 from acelaya-forks/feature/env-var-command
Feature/env var command
2024-10-14 09:45:48 +02:00
Alejandro Celaya
f118ea252c Depend on shlink-config 3.2 2024-10-14 09:41:47 +02:00
Alejandro Celaya
d514f39a82 Update changelog 2024-10-14 09:41:46 +02:00
Alejandro Celaya
e17556a7ae Add ReadEnvVarCommand test 2024-10-14 09:41:22 +02:00
Alejandro Celaya
d79f11eeb8 Add missing default value for DEFAULT_QR_CODE_BG_COLOR env var 2024-10-14 09:41:22 +02:00
Alejandro Celaya
1ec950ee1e Fix tests not properly unsetting env vars 2024-10-14 09:41:22 +02:00
Alejandro Celaya
14ba9fd6a4 Create command to return the value of an env var for current env 2024-10-14 09:41:22 +02:00
Alejandro Celaya
83e8801827 Move env var default values to EnvVars enum 2024-10-14 09:41:22 +02:00
Alejandro Celaya
be822646e4 Update changelog 2024-10-13 09:49:34 +02:00
Alejandro Celaya
3a4a27a60c
Merge pull request #2214 from acelaya-forks/feature/fix-query-params
Ensure query parameters are preserved verbatim when forwarded to long URL
2024-10-10 11:38:15 +02:00
Alejandro Celaya
1773e6ecae Ensure query parameters are preserved verbatim when forwarded to long URL 2024-10-10 11:35:29 +02:00
Alejandro Celaya
a8e4b2fceb
Merge pull request #2211 from acelaya-forks/feature/explicit-env-from-config
Promote installer config options as env vars explicitly
2024-10-08 09:07:11 +02:00
Alejandro Celaya
15b53ef43c Update changelog 2024-10-08 09:04:30 +02:00
Alejandro Celaya
11a4702b10 Promote installer config options as env vars explicitly 2024-10-08 08:57:51 +02:00
Alejandro Celaya
6b15cd6d51
Merge pull request #2204 from shlinkio/develop
Release 4.2.1
2024-10-04 12:53:11 +02:00
Alejandro Celaya
00169a5729 Require shlink-common 6.3 2024-10-04 12:48:19 +02:00
Alejandro Celaya
94702791d9
Merge pull request #2203 from acelaya-forks/feature/fix-memory-limit
Fix `MEMORY_LIMIT` being ignored when provided as installer config option
2024-10-04 12:43:38 +02:00
Alejandro Celaya
447cccacdf Update changelog 2024-10-04 12:41:02 +02:00
Alejandro Celaya
0413399102 Make sure MEMORY_LIMIT env var is read after config options have been promoted to env vars 2024-10-04 12:33:27 +02:00
Alejandro Celaya
9afc7876c4
Merge pull request #2184 from acelaya-forks/feature/redis-db-index
Allow specifying the redis database index to be used
2024-08-26 10:00:05 +02:00
Alejandro Celaya
187c17319a Take all Postgres platform classes into consideration 2024-08-26 09:57:17 +02:00
Alejandro Celaya
7310ecd886 Allow specifying the redis database index to be used 2024-08-25 12:51:34 +02:00
Alejandro Celaya
620cd92d11
Merge pull request #2172 from shlinkio/develop
Release v4.2.0
2024-08-11 18:33:09 +02:00
Alejandro Celaya
f9658c8da1 Add v4.2.0 to changelog 2024-08-11 18:30:06 +02:00
Alejandro Celaya
613b1d3045 Update changelog 2024-08-06 10:13:55 +02:00
Alejandro Celaya
d39711ec51
Merge pull request #2170 from acelaya-forks/feature/testdox-summary
Add --testdox-summary flag to phpunit executions
2024-08-04 13:16:32 +02:00
Alejandro Celaya
69dcab96f8 Add --testdox-summary flag to phpunit executions 2024-08-04 13:13:03 +02:00
Alejandro Celaya
d76c96ad41 Fix coding standard 2024-08-01 08:38:49 +02:00
Alejandro Celaya
133efff2cd Improve PHPStan config 2024-07-31 19:53:05 +02:00
Alejandro Celaya
c10f0db170
Merge pull request #2168 from acelaya-forks/feature/update-common
Update to latest shlink-common and remove deprecation references
2024-07-29 20:47:04 +02:00
Alejandro Celaya
037cd8a389 Add missing generic tyoes annotations 2024-07-29 20:43:52 +02:00
Alejandro Celaya
1d24750f43 Fix phpstan checks 2024-07-29 19:59:46 +02:00
Alejandro Celaya
b52ceaff9a Update to latest shlink-common and remove deprecation references 2024-07-29 19:41:40 +02:00
Alejandro Celaya
6b0b52853c
Improve repro steps description in bug issue template 2024-07-28 10:49:24 +02:00
Alejandro Celaya
64d7ac7093
Merge pull request #2166 from acelaya-forks/feature/options-enum
Reduce hardcoded options in ShortUrlDataInput
2024-07-27 09:15:16 +02:00
Alejandro Celaya
b9ba1246d4 Reduce hardcoded options in ShortUrlDataInput 2024-07-27 09:12:54 +02:00
Alejandro Celaya
7f9dc10f6a
Merge pull request #2164 from acelaya-forks/feature/update-url-cli
Add command to update short URLs
2024-07-26 20:14:02 +02:00
Alejandro Celaya
a1afc90150 Fix sqlcmd path 2024-07-26 20:09:59 +02:00
Alejandro Celaya
df94c68e2e Add unit test for EditShortUrlCommand 2024-07-26 19:54:39 +02:00
Alejandro Celaya
65ea1e00a6 Prevent resetting of non-providen params in EditShortUrlCommand 2024-07-26 19:26:48 +02:00
Alejandro Celaya
5bccdded8a Create command to edit existing short URLs 2024-07-26 09:21:00 +02:00
Alejandro Celaya
8917ed5c2e Create command to edit existing short URLs 2024-07-26 00:01:40 +02:00
Alejandro Celaya
fabc752398 Extract reading and parsing of arguments for short URLs data in commands 2024-07-25 23:44:46 +02:00
Alejandro Celaya
38d8086516
Merge pull request #2161 from acelaya-forks/feature/php-8.4-ci
Add PHP 8.4 to CI
2024-07-23 20:06:09 +02:00
Alejandro Celaya
ae0ff5f23c Add PHP 8.4 to CI 2024-07-23 20:02:49 +02:00
Alejandro Celaya
7c659699f3
Merge pull request #2151 from acelaya-forks/feature/ip-dynamic-redirects
Add logic for IP-based dynamic redirects
2024-07-18 21:32:24 +02:00
Alejandro Celaya
9e6cdcb838 Update changelog 2024-07-18 21:26:28 +02:00
Alejandro Celaya
7e2f755dfd Validate IP address patterns when creating ip-address redirect conditions 2024-07-18 21:23:48 +02:00
Alejandro Celaya
ce2ed237c7 Add ip-address condition type to redirect rules API spec docs 2024-07-17 20:23:58 +02:00
Alejandro Celaya
626caa4afa Add API test for dynamic IP-based redirects 2024-07-17 20:13:46 +02:00
Alejandro Celaya
f4a7712ded Add InvalidIpFormatExceptionTest 2024-07-17 19:59:13 +02:00
Alejandro Celaya
bab6a3951e Add missing unit test 2024-07-17 19:56:53 +02:00
Alejandro Celaya
f49d98f2ea Add logic for IP-based dynamic redirects 2024-07-17 19:51:13 +02:00
Alejandro Celaya
1312ea61f4 Add new IP address redirect condition 2024-07-06 10:35:33 +02:00
Alejandro Celaya
8d90661d0a Extract logic to match IP address against list of groups 2024-07-06 10:12:05 +02:00
Alejandro Celaya
b6b2530cb6
Merge pull request #2149 from acelaya-forks/feature/robots-user-agents
Add option to customize user agents in robots.txt
2024-07-06 10:08:03 +02:00
Alejandro Celaya
e4f66b7ce6 Update installer 2024-07-05 09:41:26 +02:00
Alejandro Celaya
4b52c92e97 Add option to customize user agents in robots.txt 2024-07-05 08:54:54 +02:00
Alejandro Celaya
76c42bc17c
Merge pull request #2148 from acelaya-forks/feature/roadrunner-2024
Update to RoadRunner 2024
2024-07-03 19:56:36 +02:00
Alejandro Celaya
c4f8da5f02 Fix phpstan error definition 2024-07-03 19:53:26 +02:00
Alejandro Celaya
80bdeb280a Update to RoadRunner 2024 2024-07-03 19:52:06 +02:00
Alejandro Celaya
99010b6eae Fix merge conflicts 2024-05-23 09:26:27 +02:00
Alejandro Celaya
b2dabf06bf
Merge pull request #2136 from acelaya-forks/release/4.1.1
Release/4.1.1
2024-05-23 09:21:56 +02:00
Alejandro Celaya
67ae05f473 Add v4.1.1 to changelog 2024-05-23 09:18:58 +02:00
Alejandro Celaya
fb4fecf411 Merge pull request #2135 from acelaya-forks/feature/non-utf8-titles
Convert encoding of resolved titles based on page encoding
2024-05-23 09:17:49 +02:00
Alejandro Celaya
c855f011d1 Merge pull request #2132 from acelaya-forks/feature/update-phpstan
Update to latest phpstan
2024-05-23 09:17:37 +02:00
Alejandro Celaya
02717eb2fb Merge pull request #2130 from marijnvandevoorde/nanoid
Replaces short-id by nano-id
2024-05-23 09:17:26 +02:00
Alejandro Celaya
de70ebe769 Merge pull request #2125 from acelaya-forks/feature/phpunit-11
Update to PHPUnit 11
2024-05-23 09:16:56 +02:00
Alejandro Celaya
c6109fd396 Merge pull request #2115 from acelaya-forks/feature/fix-oas-docs
Fix typo in OAS docs
2024-05-23 09:16:24 +02:00
Alejandro Celaya
83584a3175 Link crchived changelogs from main one 2024-05-23 09:15:40 +02:00
Alejandro Celaya
f5dcc52b3b Migrate to new docker-publish-image reusable workflow 2024-05-23 09:15:16 +02:00
Alejandro Celaya
1901964de1
Merge pull request #2135 from acelaya-forks/feature/non-utf8-titles
Convert encoding of resolved titles based on page encoding
2024-05-22 18:14:56 +02:00
Alejandro Celaya
80e9c2452b Convert encoding of resolved titles based on page encoding 2024-05-22 18:11:55 +02:00
Alejandro Celaya
5ad4b39160
Merge pull request #2132 from acelaya-forks/feature/update-phpstan
Update to latest phpstan
2024-05-21 19:05:39 +02:00
Alejandro Celaya
89b73a9cfa Update to latest phpstan 2024-05-21 18:09:45 +02:00
Alejandro Celaya
e2d8334d69
Merge pull request #2130 from marijnvandevoorde/nanoid
Replaces short-id by nano-id
2024-05-21 17:58:53 +02:00
Marijn Vandevoorde
9b16d7acc0 Replaces short-id by nano-id 2024-05-16 14:00:39 +02:00
Alejandro Celaya
6836840746
Merge pull request #2125 from acelaya-forks/feature/phpunit-11
Update to PHPUnit 11
2024-05-12 13:22:26 +02:00
Alejandro Celaya
4084d301ca Update to PHPUnit 11 2024-05-12 12:49:53 +02:00
Alejandro Celaya
added21b18
Merge pull request #2118 from shlinkio/revert-2117-feature/superfluous-distinct
Revert "Remove unneeded DISTINCT from list short URLs query"
2024-05-09 10:00:29 +02:00
Alejandro Celaya
8cd77391cc
Revert "Remove unneeded DISTINCT from list short URLs query" 2024-05-09 09:43:55 +02:00
Alejandro Celaya
05ebfccc63
Merge pull request #2117 from acelaya-forks/feature/superfluous-distinct
Remove unneeded DISTINCT from list short URLs query
2024-05-06 18:54:01 +02:00
Alejandro Celaya
cb3a690294 Remove unneeded DISTINCT from list short URLs query 2024-05-06 18:50:10 +02:00
Alejandro Celaya
194a7b0e57
Merge pull request #2115 from acelaya-forks/feature/fix-oas-docs
Fix typo in OAS docs
2024-04-29 15:22:32 +02:00
Alejandro Celaya
98e4d01feb Fix typo in OAS docs 2024-04-29 15:18:54 +02:00
Alejandro Celaya
c22e3895b5 Allow more dev hosts in dev mercure 2024-04-29 08:52:18 +02:00
Alejandro Celaya
9a76c19615 Migrate to new docker-publish-image reusable workflow 2024-04-26 09:27:21 +02:00
Alejandro Celaya
59fa088975
Merge pull request #2107 from acelaya-forks/feature/robots-allow-all
Add option to allow all URLs to be crawlable via robots.txt
2024-04-22 09:23:34 +02:00
Alejandro Celaya
163244f40f Add option to allow all URLs to be crawlable via robots.txt 2024-04-22 09:16:44 +02:00
Alejandro Celaya
a89b53af4f Link crchived changelogs from main one 2024-04-21 16:46:24 +02:00
Alejandro Celaya
35508e253d
Merge pull request #2099 from shlinkio/develop
Release 4.1.0
2024-04-14 09:12:56 +02:00
Alejandro Celaya
e586fec338 Rearrange changelog 2024-04-14 08:53:31 +02:00
Alejandro Celaya
93fa27bdba Add v4.1.0 to changelog 2024-04-14 08:40:52 +02:00
Alejandro Celaya
048856c333
Merge pull request #2098 from acelaya-forks/feature/matomo-command
Create console command to send visits to matomo
2024-04-13 20:59:57 +02:00
Alejandro Celaya
986f1162dd Set COLUMNS env var when running unit tests 2024-04-13 20:56:59 +02:00
Alejandro Celaya
dc8dfa9f0c Update changelog 2024-04-13 20:49:34 +02:00
Alejandro Celaya
82e7094f3a Fix VisitIterationRepositoryTest for MS SQL 2024-04-13 20:48:03 +02:00
Alejandro Celaya
f0e62004d5 Add unit test to MatomoSendVisitsCommand 2024-04-13 20:30:31 +02:00
Alejandro Celaya
bbdbafd8db Test MatomoVisitSender::sendVisitsInDateRange 2024-04-13 19:27:03 +02:00
Alejandro Celaya
6121efec59 Create command to send visits to matomo 2024-04-13 18:59:09 +02:00
Alejandro Celaya
4fdbcc25a0 Pass visit date to matomo when tracking 2024-04-13 18:59:09 +02:00
Alejandro Celaya
ca42425b33 Make Visit::date field readonly 2024-04-13 18:59:09 +02:00
Alejandro Celaya
ce0f61b66d Allow filtering by date in VisitIterationRepository 2024-04-13 18:59:09 +02:00
Alejandro Celaya
13ee71f351 Move allowed HTTP methods definition to RedirectStatus enum 2024-04-13 18:59:09 +02:00
Alejandro Celaya
c57494d7cd Extract logic to send visits to Matomo to its own service 2024-04-13 18:59:09 +02:00
Alejandro Celaya
d2e74ab330
Merge pull request #2097 from acelaya-forks/feature/bitly-custom-slugs
Fix custom slugs not being properly imported from bitly
2024-04-12 22:31:12 +02:00
Alejandro Celaya
850dde1a06 Fix custom slugs not being properly imported from bitly 2024-04-12 22:28:13 +02:00
Alejandro Celaya
5e83f301ff
Merge pull request #2092 from acelaya-forks/customizable-memory-limit
Allow memory_limit to be configurable
2024-04-09 09:45:08 +02:00
Alejandro Celaya
5e74dd7a6d Update to installer version with support for memory limit option 2024-04-09 09:40:08 +02:00
Alejandro Celaya
8a273e01e9 Allow memory_limit to be configurable 2024-04-09 08:47:01 +02:00
Alejandro Celaya
75f6f8dd18
Merge pull request #2090 from acelaya-forks/feature/propagate-job-request-id
Forward request ID from sync request process to async job processes
2024-04-07 11:30:47 +02:00
Alejandro Celaya
e1cf0c4ea7 Forward request ID from sync request process to async job processes 2024-04-07 11:26:17 +02:00
Alejandro Celaya
cc134abd12
Merge pull request #2086 from acelaya-forks/feature/delete-expired
Feature/delete expired
2024-04-03 19:27:17 +02:00
Alejandro Celaya
b7db676cba Test non-interactivity on DeleteExpiredShortUrlsCommand 2024-04-03 19:24:08 +02:00
Alejandro Celaya
3881996560 Migrate from docker-compose to docker compose in CI pipelines 2024-04-03 19:20:38 +02:00
Alejandro Celaya
527d28ad81 Add DeleteExpiredShortUrlsCommand test 2024-04-03 19:18:56 +02:00
Alejandro Celaya
f2371e8a80 Add command to delete expired short URLs 2024-04-03 18:57:09 +02:00
Alejandro Celaya
fd882834d3 Create repository to handle expired short URLs deletion 2024-04-03 09:52:38 +02:00
Alejandro Celaya
f92a720d63 Use short_url_visits_counts table when excluding short URLs which reached max visits 2024-04-03 09:06:43 +02:00
Alejandro Celaya
d6f58698b7
Merge pull request #2082 from acelaya-forks/feature/orphan-visits-counts
Track orphan visits counts
2024-04-01 10:28:05 +02:00
Alejandro Celaya
d090260b17 Track orphan visits counts 2024-04-01 10:22:51 +02:00
Alejandro Celaya
cd43c1c01f
Merge pull request #2083 from acelaya-forks/feature/matomo-title
Track short URL title as document title when sending visits to matomo
2024-03-31 13:53:58 +02:00
Alejandro Celaya
284b28e8d9 Track short URL title as document title when sending visits to matomo 2024-03-31 13:51:03 +02:00
Alejandro Celaya
b50547d868 Create new orphan_visits_counts table 2024-03-31 13:18:44 +02:00
Alejandro Celaya
401046fbe5
Merge pull request #2081 from acelaya-forks/feature/performant-count-visits
Load non-orphan visits overview via short url visits counts
2024-03-31 13:07:52 +02:00
Alejandro Celaya
6e82509964 Update changelog 2024-03-31 13:04:58 +02:00
Alejandro Celaya
ab6fa490e5 Test ShortUrlVisitsCountRepository via VisitRepositoryTest 2024-03-31 12:37:22 +02:00
Alejandro Celaya
55e2780f50 Load non-orphan visits overview via short url visits counts 2024-03-31 12:27:20 +02:00
Alejandro Celaya
f4803c675c
Merge pull request #2079 from acelaya-forks/feature/fix-order-by-title
Ensure ordering by title is consistent between database engines
2024-03-29 09:38:25 +01:00
Alejandro Celaya
90514c603f Ensure ordering by title is consistent between database engines 2024-03-29 09:35:54 +01:00
Alejandro Celaya
7f4137e7cc
Merge pull request #2078 from acelaya-forks/feature/tags-stats-improvements
Improve tags stats performance by using the new short_url_visits_counts table
2024-03-28 19:26:33 +01:00
Alejandro Celaya
071cb9af2b Improve tags stats performance by using the new short_url_visits_counts table 2024-03-28 19:17:37 +01:00
Alejandro Celaya
6ce1550457
Merge pull request #2074 from acelaya-forks/feature/slotted-counts
Feature/slotted counts
2024-03-28 17:44:31 +01:00
Alejandro Celaya
8cb5d44dc9 Update changelog 2024-03-28 17:27:49 +01:00
Alejandro Celaya
1331b3f87c Fix RabbitMQ dev port 2024-03-28 17:24:00 +01:00
Alejandro Celaya
ab96297e58 Make sure VisitsTracker wraps as little operations as possible in the transaction 2024-03-28 17:06:18 +01:00
Alejandro Celaya
c4fd3a74c5 Fix type hint in migration 2024-03-28 16:10:56 +01:00
Alejandro Celaya
da922fb2a7 Add ShortUrlVisitsCountTrackerTest 2024-03-28 09:43:58 +01:00
Alejandro Celaya
4a05c4be40 Wrap visits tracking in transaction 2024-03-27 19:14:41 +01:00
Alejandro Celaya
cef30c8e2d Fix type in Version20240318084804 2024-03-27 19:08:25 +01:00
Alejandro Celaya
8417498f08 Fixes on static check and unit tests 2024-03-27 19:08:25 +01:00
Alejandro Celaya
10e941cea6 Add missing COALESCE when summing visits counts 2024-03-27 19:08:25 +01:00
Alejandro Celaya
3d7b1ca799 Move from preFlush to onFlush + postFlush 2024-03-27 19:08:25 +01:00
Alejandro Celaya
b236354fc7 Fix order in which entities are flushed in ShortUrlListRepositoryTest 2024-03-27 19:08:25 +01:00
Alejandro Celaya
6fbb5a380d Add missing default value for short url visits count 2024-03-27 19:08:25 +01:00
Alejandro Celaya
054eb42613 Remove no-longer used methods in OrderableField enum 2024-03-27 19:08:25 +01:00
Alejandro Celaya
6074f4475d Add preFlush listener to track visits counts 2024-03-27 19:08:25 +01:00
Alejandro Celaya
7afd3fd6a2 Load visits and nonBotVisits via sub-queries in ShortUrlListRepository 2024-03-27 19:08:25 +01:00
Alejandro Celaya
7d415e40b2 Add unique index in short_url_visits_counts 2024-03-27 19:08:25 +01:00
Alejandro Celaya
3c89d252d2 Simplify logic to match order by for short URL lists 2024-03-27 19:08:25 +01:00
Alejandro Celaya
f678873e9f Use pre-calculated visits counts when listing short URLs 2024-03-27 19:08:25 +01:00
Alejandro Celaya
17d37a062a Add new table to track short URL visits counts 2024-03-27 19:08:25 +01:00
Alejandro Celaya
14702063f2
Merge pull request #2076 from acelaya-forks/feature/fix-array-inputs
Make sure tags fallback to empty array when null
2024-03-27 19:08:06 +01:00
Alejandro Celaya
c599d8a0ed Make sure tags fallback to empty array when null 2024-03-27 13:04:42 +01:00
Alejandro Celaya
207d5adceb
Merge pull request #2070 from acelaya-forks/feature/visited-url-always
Feature/visited url always
2024-03-24 17:27:57 +01:00
Alejandro Celaya
b4c46ce222 Update changelog 2024-03-24 17:24:46 +01:00
Alejandro Celaya
6fe269193a Expose visitedUrl when serializing any kind of visit, not only orphan visits 2024-03-24 17:20:41 +01:00
Alejandro Celaya
d948543d5c Wrap JSON serialization for any kind of visit in Visit entity itself 2024-03-24 17:06:11 +01:00
Alejandro Celaya
a327e6c0a7 Make Visit::jsonSerialize() return different props for orphan visits 2024-03-24 16:54:49 +01:00
Alejandro Celaya
fbd35b7974 Add more named constructors to Ordering class 2024-03-20 09:15:45 +01:00
Alejandro Celaya
b94a22e6a7 Rename Ordering::emptyInstance to Ordering::none to make it more clear 2024-03-20 09:06:35 +01:00
Alejandro Celaya
63ea9e4a21
Merge pull request #2069 from acelaya-forks/feature/short-url-simplification
Move logic to serialize ShortUrls to entity itself
2024-03-19 07:34:59 +01:00
Alejandro Celaya
e028d8ea31 Move logic to serialize ShortUrls to entity itself 2024-03-18 22:09:15 +01:00
Alejandro Celaya
457a7a14e5
Merge pull request #2068 from acelaya-forks/feature/modernize-entities
Feature/modernize entities
2024-03-18 20:26:47 +01:00
Alejandro Celaya
cd387328be Update changelog 2024-03-18 20:22:54 +01:00
Alejandro Celaya
5524476787 Modernize ShortUrl entity 2024-03-18 20:21:26 +01:00
Alejandro Celaya
78526fb405 Modernize Visit entity 2024-03-18 19:57:30 +01:00
Alejandro Celaya
b2dee43bb0 Modernize VisitLocation entity 2024-03-18 19:11:42 +01:00
Alejandro Celaya
60e9443b12 Modernize ApiKey entity 2024-03-18 18:33:56 +01:00
Alejandro Celaya
ab8fa52ca4 Modernize Domain entity 2024-03-18 18:15:05 +01:00
631 changed files with 14514 additions and 8761 deletions

View File

@ -1,5 +1,6 @@
bin/rr
config/autoload/*local*
config/params/shlink_dev_env.*
data/infra
data/cache/*
data/log/*

1
.gitattributes vendored
View File

@ -13,7 +13,6 @@
.travis.yml export-ignore
build.sh export-ignore
CHANGELOG.md export-ignore
docker-compose.override.yml.dist export-ignore
docker-compose.yml export-ignore
indocker export-ignore
phpcs.xml export-ignore

View File

@ -1,49 +0,0 @@
title: 'Help wanted'
body:
- type: input
validations:
required: true
attributes:
label: Shlink version
placeholder: x.y.z
- type: input
validations:
required: true
attributes:
label: PHP version
placeholder: x.y.z
- type: dropdown
validations:
required: true
attributes:
label: How do you serve Shlink
options:
- Self-hosted Apache
- Self-hosted nginx
- Self-hosted RoadRunner
- Docker image
- Other (explain in summary)
- type: dropdown
validations:
required: true
attributes:
label: Database engine
options:
- MySQL
- MariaDB
- PostgreSQL
- MicrosoftSQL
- SQLite
- type: input
validations:
required: true
attributes:
label: Database version
placeholder: x.y.z
- type: textarea
validations:
required: true
attributes:
label: Summary
value: '<!-- Describe your issue, question or request here. -->'

View File

@ -1,7 +0,0 @@
<!--
Before opening an issue, just take into account that this is a completely free of charge and open source project.
I'm always happy to help and provide support, but some understanding will be expected.
I do this in my own free time, so expect some delays when implementing new features and fixing bugs, and don't take it personally if an issue gets eventually closed.
You may also be asked to provide tests or ways to reproduce reported bugs.
Try to be polite, and understand it is impossible for an OSS project to cover all use cases.
-->

View File

@ -61,7 +61,11 @@ body:
label: Minimum steps to reproduce
value: |
<!--
Emphasis in MINIMUM: What is the simplest way to reproduce the bug?
Avoid things like "Create a kubernetes cluster", or anything related with cloud providers, as that is rarely the root cause and the bug may be closed as "not reproducible".
If you can provide a simple docker compose config, that's even better.
Simple but detailed way to reproduce the bug:
* Avoid things like "create a kubernetes cluster", or anything related with cloud providers, as that is rarely the root cause.
* Avoid too vague steps or one-liners like "Update from v1 to v2".
* Providing the reproduction in the form of a self-contained docker-composer is desirable.
Failing in any of these will cause the issue to be closed as "not reproducible".
-->

View File

@ -2,4 +2,4 @@ blank_issues_enabled: true
contact_links:
- name: Question - Support
about: Do you need help setting up or using Shlink?
url: https://github.com/shlinkio/shlink/discussions/new?category=help-wanted
url: https://github.com/orgs/shlinkio/discussions/new?category=help-wanted

View File

@ -40,8 +40,7 @@ runs:
php-version: ${{ inputs.php-version }}
tools: composer
extensions: ${{ inputs.php-extensions }}
coverage: pcov
ini-values: pcov.directory=module
coverage: xdebug
- name: Install dependencies
if: ${{ inputs.install-deps == 'yes' }}
run: composer install --no-interaction --prefer-dist

View File

@ -10,20 +10,20 @@ on:
jobs:
db-tests:
runs-on: ubuntu-22.04
runs-on: ubuntu-24.04
strategy:
matrix:
php-version: ['8.2', '8.3']
php-version: ['8.4', '8.5']
env:
LC_ALL: C
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- name: Install MSSQL ODBC
if: ${{ inputs.platform == 'ms' }}
run: sudo ./data/infra/ci/install-ms-odbc.sh
- name: Start database server
if: ${{ inputs.platform != 'sqlite:ci' }}
run: docker-compose -f docker-compose.yml -f docker-compose.ci.yml up -d shlink_db_${{ inputs.platform }}
run: docker compose -f docker-compose.yml -f docker-compose.ci.yml up -d shlink_db_${{ inputs.platform }}
- uses: './.github/actions/ci-setup'
with:
php-version: ${{ matrix.php-version }}
@ -31,12 +31,12 @@ jobs:
extensions-cache-key: db-tests-extensions-${{ matrix.php-version }}-${{ inputs.platform }}
- name: Create test database
if: ${{ inputs.platform == 'ms' }}
run: docker-compose exec -T shlink_db_ms /opt/mssql-tools/bin/sqlcmd -S localhost -U sa -P 'Passw0rd!' -Q "CREATE DATABASE shlink_test;"
run: docker compose exec -T shlink_db_ms /opt/mssql-tools18/bin/sqlcmd -C -S localhost -U sa -P 'Passw0rd!' -Q "CREATE DATABASE shlink_test;"
- name: Run tests
run: composer test:db:${{ inputs.platform }}
- name: Upload code coverage
uses: actions/upload-artifact@v4
if: ${{ matrix.php-version == '8.2' && inputs.platform == 'sqlite:ci' }}
uses: actions/upload-artifact@v5
if: ${{ matrix.php-version == '8.4' && inputs.platform == 'sqlite:ci' }}
with:
name: coverage-db
path: |

View File

@ -1,4 +1,4 @@
name: Build docker image
name: Test docker image build
on:
pull_request:
@ -7,8 +7,6 @@ on:
jobs:
build-docker-image:
runs-on: ubuntu-22.04
steps:
- name: Checkout code
uses: actions/checkout@v4
- run: docker build -t shlink-docker-image:temp .
uses: shlinkio/github-actions/.github/workflows/docker-image-build-ci.yml@main
with:
platforms: 'linux/arm64/v8,linux/amd64'

View File

@ -10,20 +10,20 @@ on:
jobs:
tests:
runs-on: ubuntu-22.04
runs-on: ubuntu-24.04
strategy:
matrix:
php-version: ['8.2', '8.3']
php-version: ['8.4', '8.5']
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # rr get-binary picks this env automatically
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- name: Start postgres database server
if: ${{ inputs.test-group == 'api' }}
run: docker-compose -f docker-compose.yml -f docker-compose.ci.yml up -d shlink_db_postgres
run: docker compose -f docker-compose.yml -f docker-compose.ci.yml up -d shlink_db_postgres
- name: Start maria database server
if: ${{ inputs.test-group == 'cli' }}
run: docker-compose -f docker-compose.yml -f docker-compose.ci.yml up -d shlink_db_maria
run: docker compose -f docker-compose.yml -f docker-compose.ci.yml up -d shlink_db_maria
- uses: './.github/actions/ci-setup'
with:
php-version: ${{ matrix.php-version }}
@ -32,8 +32,8 @@ jobs:
if: ${{ inputs.test-group == 'api' }}
run: ./vendor/bin/rr get --no-interaction --no-config --location bin/ && chmod +x bin/rr
- run: composer test:${{ inputs.test-group }}:ci
- uses: actions/upload-artifact@v4
if: ${{ matrix.php-version == '8.2' }}
- uses: actions/upload-artifact@v5
if: ${{ matrix.php-version == '8.4' }}
with:
name: coverage-${{ inputs.test-group }}
path: |

View File

@ -24,13 +24,13 @@ on:
jobs:
static-analysis:
runs-on: ubuntu-22.04
runs-on: ubuntu-24.04
strategy:
matrix:
php-version: ['8.2']
command: ['cs', 'stan', 'swagger:validate']
php-version: ['8.4']
command: ['cs', 'stan', 'openapi:validate']
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- uses: './.github/actions/ci-setup'
with:
php-version: ${{ matrix.php-version }}
@ -66,19 +66,18 @@ jobs:
- api-tests
- cli-tests
- db-tests
runs-on: ubuntu-22.04
runs-on: ubuntu-24.04
strategy:
matrix:
php-version: ['8.2']
php-version: ['8.4']
steps:
- name: Checkout code
uses: actions/checkout@v4
- uses: actions/checkout@v5
- name: Use PHP
uses: './.github/actions/ci-setup'
with:
php-version: ${{ matrix.php-version }}
extensions-cache-key: tests-extensions-${{ matrix.php-version }}
- uses: actions/download-artifact@v4
- uses: actions/download-artifact@v6
with:
path: build
- run: mv build/coverage-unit/coverage-unit.cov build/coverage-unit.cov
@ -87,16 +86,16 @@ jobs:
- run: mv build/coverage-cli/coverage-cli.cov build/coverage-cli.cov
- run: vendor/bin/phpcov merge build --clover build/clover.xml
- name: Publish coverage
uses: codecov/codecov-action@v4
uses: codecov/codecov-action@v5
with:
file: ./build/clover.xml
files: ./build/clover.xml
delete-artifacts:
needs:
- upload-coverage
runs-on: ubuntu-22.04
runs-on: ubuntu-24.04
steps:
- uses: geekyeggo/delete-artifact@v2
- uses: geekyeggo/delete-artifact@v5
with:
name: |
coverage-*

View File

@ -15,7 +15,7 @@ jobs:
- runtime: 'rr'
tag-suffix: 'roadrunner'
platforms: 'linux/arm64/v8,linux/amd64'
uses: shlinkio/github-actions/.github/workflows/docker-build-and-publish.yml@main
uses: shlinkio/github-actions/.github/workflows/docker-publish-image.yml@main
secrets: inherit
with:
image-name: shlinkio/shlink

View File

@ -1,4 +1,4 @@
name: Publish swagger spec
name: Publish openapi spec
on:
push:
@ -7,12 +7,12 @@ on:
jobs:
build:
runs-on: ubuntu-22.04
runs-on: ubuntu-24.04
strategy:
matrix:
php-version: ['8.2']
php-version: ['8.4']
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- name: Determine version
id: determine_version
run: echo "version=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT
@ -20,10 +20,10 @@ jobs:
- uses: './.github/actions/ci-setup'
with:
php-version: ${{ matrix.php-version }}
extensions-cache-key: publish-swagger-spec-extensions-${{ matrix.php-version }}
- run: composer swagger:inline
extensions-cache-key: publish-openapi-spec-extensions-${{ matrix.php-version }}
- run: composer openapi:inline
- run: mkdir ${{ steps.determine_version.outputs.version }}
- run: mv docs/swagger/swagger-inlined.json ${{ steps.determine_version.outputs.version }}/open-api-spec.json
- run: mv docs/swagger/openapi-inlined.json ${{ steps.determine_version.outputs.version }}/open-api-spec.json
- name: Publish spec
uses: JamesIves/github-pages-deploy-action@v4
with:

View File

@ -7,29 +7,29 @@ on:
jobs:
build:
runs-on: ubuntu-22.04
runs-on: ubuntu-24.04
strategy:
matrix:
php-version: ['8.2', '8.3']
php-version: ['8.4', '8.4', '8.5']
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- uses: './.github/actions/ci-setup'
with:
php-version: ${{ matrix.php-version }}
extensions-cache-key: publish-swagger-spec-extensions-${{ matrix.php-version }}
install-deps: 'no'
- run: ./build.sh ${GITHUB_REF#refs/tags/v}
- uses: actions/upload-artifact@v4
- uses: actions/upload-artifact@v5
with:
name: dist-files-${{ matrix.php-version }}
path: build
publish:
needs: ['build']
runs-on: ubuntu-22.04
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
- uses: actions/download-artifact@v4
- uses: actions/checkout@v5
- uses: actions/download-artifact@v6
with:
path: build
- name: Publish release with assets
@ -43,8 +43,8 @@ jobs:
delete-artifacts:
needs: ['publish']
runs-on: ubuntu-22.04
runs-on: ubuntu-24.04
steps:
- uses: geekyeggo/delete-artifact@v2
- uses: geekyeggo/delete-artifact@v5
with:
name: dist-files-*

5
.gitignore vendored
View File

@ -10,9 +10,6 @@ data/database.sqlite
data/shlink-tests.db
data/GeoLite2-City.*
data/infra/matomo
docs/swagger-ui*
docs/mercure.html
docker-compose.override.yml
.phpunit.result.cache
docs/swagger/swagger-inlined.json
phpcov*
docs/swagger/openapi-inlined.json

File diff suppressed because it is too large Load Diff

View File

@ -16,11 +16,14 @@ The first thing you need to do is fork the repository, and clone it in your loca
Then you will have to follow these steps:
* Copy all files with `.local.php.dist` extension from `config/autoload` by removing the dist extension.
* Copy the `config/params/shlink_dev_env.php.dist` in the same directory, but removing the `.dist` extension:
For example the `common.local.php.dist` file should be copied as `common.local.php`.
```
cp config/params/shlink_dev_env.php.dist config/params/shlink_dev_env.php
```
The `shlink_dev_env.php` file is gitignored, so you can customize it as you want. For example, by adding your own GeoLite license key.
* Copy the file `docker-compose.override.yml.dist` by also removing the `dist` extension.
* Start-up the project by running `docker compose up`.
The first time this command is run, it will create several containers that are used during development, so it may take some time.

View File

@ -1,23 +1,23 @@
FROM php:8.3-alpine3.19 as base
FROM php:8.4-alpine3.21 AS base
ARG SHLINK_VERSION=latest
ENV SHLINK_VERSION ${SHLINK_VERSION}
ENV SHLINK_VERSION=${SHLINK_VERSION}
ARG SHLINK_RUNTIME=rr
ENV SHLINK_RUNTIME ${SHLINK_RUNTIME}
ENV SHLINK_RUNTIME=${SHLINK_RUNTIME}
ENV USER_ID '1001'
ENV PDO_SQLSRV_VERSION 5.12.0
ENV MS_ODBC_DOWNLOAD 'b/9/f/b9f3cce4-3925-46d4-9f46-da08869c6486'
ENV MS_ODBC_SQL_VERSION 18_18.1.1.1
ENV LC_ALL 'C'
ENV USER_ID='1001'
ENV PDO_SQLSRV_VERSION='5.12.0'
ENV MS_ODBC_DOWNLOAD='7/6/d/76de322a-d860-4894-9945-f0cc5d6a45f8'
ENV MS_ODBC_SQL_VERSION='18_18.4.1.1'
ENV LC_ALL='C'
WORKDIR /etc/shlink
# Install required PHP extensions
RUN \
# Temp install dev dependencies needed to compile the extensions
apk add --no-cache --virtual .dev-deps sqlite-dev postgresql-dev icu-dev libzip-dev zlib-dev libpng-dev linux-headers && \
docker-php-ext-install -j"$(nproc)" pdo_mysql pdo_pgsql intl calendar sockets bcmath zip gd && \
apk add --no-cache --virtual .dev-deps sqlite-dev postgresql-dev icu-dev libzip-dev zlib-dev linux-headers && \
docker-php-ext-install -j"$(nproc)" pdo_mysql pdo_pgsql intl calendar sockets bcmath zip && \
apk add --no-cache sqlite-libs && \
docker-php-ext-install -j"$(nproc)" pdo_sqlite && \
# Remove temp dev extensions, and install prod equivalents that are required at runtime
@ -36,14 +36,14 @@ RUN apk add --no-cache --virtual .phpize-deps ${PHPIZE_DEPS} unixodbc-dev && \
apk del .phpize-deps
# Install shlink
FROM base as builder
FROM base AS builder
COPY . .
COPY --from=composer:2 /usr/bin/composer ./composer.phar
RUN apk add --no-cache git && \
php composer.phar install --no-dev --prefer-dist --optimize-autoloader --no-progress --no-interaction && \
php composer.phar clear-cache && \
rm -r docker composer.* && \
sed -i "s/%SHLINK_VERSION%/${SHLINK_VERSION}/g" config/autoload/app_options.global.php
sed -i "s/%SHLINK_VERSION%/${SHLINK_VERSION}/g" module/Core/src/Config/Options/AppOptions.php
# Prepare final image
@ -61,7 +61,6 @@ EXPOSE 8080
# Copy config specific for the image
COPY docker/docker-entrypoint.sh docker-entrypoint.sh
COPY docker/config/shlink_in_docker.local.php config/autoload/shlink_in_docker.local.php
COPY docker/config/php.ini ${PHP_INI_DIR}/conf.d/
USER ${USER_ID}

View File

@ -7,8 +7,7 @@
[![License](https://img.shields.io/github/license/shlinkio/shlink.svg?style=flat-square)](https://github.com/shlinkio/shlink/blob/main/LICENSE)
[![Mastodon](https://img.shields.io/mastodon/follow/109329425426175098?color=%236364ff&domain=https%3A%2F%2Ffosstodon.org&label=follow&logo=mastodon&logoColor=white&style=flat-square)](https://fosstodon.org/@shlinkio)
[![Bluesky](https://img.shields.io/badge/follow-shlinkio-0285FF.svg?style=flat-square&logo=bluesky&logoColor=white)](https://bsky.app/profile/shlinkio.bsky.social)
[![Twitter](https://img.shields.io/badge/follow-shlinkio-blue.svg?style=flat-square&logo=x&color=black)](https://twitter.com/shlinkio)
[![Bluesky](https://img.shields.io/badge/follow-shlinkio-0285FF.svg?style=flat-square&logo=bluesky&logoColor=white)](https://bsky.app/profile/shlink.io)
[![Paypal donate](https://img.shields.io/badge/Donate-paypal-blue.svg?style=flat-square&logo=paypal&colorA=aaaaaa)](https://slnk.to/donate)
A PHP-based self-hosted URL shortener that can be used to serve shortened URLs under your own domain.
@ -37,10 +36,9 @@ The idea is that you can just generate a container using the image and provide t
First, make sure the host where you are going to run shlink fulfills these requirements:
* PHP 8.2 or 8.3
* PHP 8.4 or 8.5
* The next PHP extensions: json, curl, pdo, intl, gd and gmp/bcmath.
* apcu extension is recommended if you don't plan to use RoadRunner.
* xml extension is required if you want to generate QR codes in svg format.
* sockets and bcmath extensions are required if you want to integrate with a RabbitMQ instance.
* MySQL, MariaDB, PostgreSQL, MicrosoftSQL or SQLite.
* You will also need the corresponding pdo variation for the database you are planning to use: `pdo_mysql`, `pdo_pgsql`, `pdo_sqlsrv` or `pdo_sqlite`.
@ -100,6 +98,12 @@ Both the API and CLI allow you to do mostly the same operations, except for API
If you are trying to find out how to run the project in development mode or how to provide contributions, read the [CONTRIBUTING](CONTRIBUTING.md) doc.
## Powered by
Thanks to [JetBrains](https://www.jetbrains.com/) for their continuous support to this project in the form of IDE licenses.
![JetBrains logo](https://resources.jetbrains.com/storage/products/company/brand/logos/jetbrains.svg)
---
> This product includes GeoLite2 data created by MaxMind, available from [https://www.maxmind.com](https://www.maxmind.com)

36
bin/frankenphp-worker.php Normal file
View File

@ -0,0 +1,36 @@
<?php
declare(strict_types=1);
namespace Shlinkio\Shlink;
use Laminas\Diactoros\ServerRequestFactory;
use Laminas\HttpHandlerRunner\Emitter\EmitterInterface;
use Mezzio\Application;
use Psr\Container\ContainerInterface;
use function frankenphp_handle_request;
use function gc_collect_cycles;
(static function (): void {
/** @var ContainerInterface $container */
$container = include __DIR__ . '/../config/container.php';
$app = $container->get(Application::class);
$responseEmitter = $container->get(EmitterInterface::class);
$handler = static function () use ($app, $responseEmitter): void {
$response = $app->handle(ServerRequestFactory::fromGlobals());
$responseEmitter->emit($response);
};
$maxRequests = (int) ($_SERVER['MAX_REQUESTS'] ?? 0);
for ($nbRequests = 0; !$maxRequests || $nbRequests < $maxRequests; ++$nbRequests) {
$keepRunning = frankenphp_handle_request($handler);
// Call the garbage collector to reduce the chances of it being triggered in the middle of a page generation
gc_collect_cycles();
if (! $keepRunning) {
break;
}
}
})();

View File

@ -2,17 +2,22 @@
declare(strict_types=1);
namespace Shlinkio\Shlink;
use Mezzio\Application;
use Psr\Container\ContainerInterface;
use Shlinkio\Shlink\Common\Middleware\RequestIdMiddleware;
use Shlinkio\Shlink\EventDispatcher\RoadRunner\RoadRunnerTaskConsumerToListener;
use Spiral\RoadRunner\Http\PSR7Worker;
use function gc_collect_cycles;
use function Shlinkio\Shlink\Config\env;
(static function (): void {
/** @var ContainerInterface $container */
$container = include __DIR__ . '/../config/container.php';
$rrMode = env('RR_MODE');
$gcCollectCycles = env('GC_COLLECT_CYCLES', default: false);
if ($rrMode === 'http') {
// This was spin-up as a web worker
@ -24,9 +29,16 @@ use function Shlinkio\Shlink\Config\env;
$worker->respond($app->handle($req));
} catch (Throwable $e) {
$worker->getWorker()->error((string) $e);
} finally {
if ($gcCollectCycles) {
gc_collect_cycles();
}
}
}
} else {
$container->get(RoadRunnerTaskConsumerToListener::class)->listenForTasks();
$requestIdMiddleware = $container->get(RequestIdMiddleware::class);
$container->get(RoadRunnerTaskConsumerToListener::class)->listenForTasks(
fn (string $requestId) => $requestIdMiddleware->setCurrentRequestId($requestId),
);
}
})();

View File

@ -6,6 +6,8 @@ export TEST_RUNTIME="${TEST_RUNTIME:-"rr"}" # rr is the only runtime currently s
export DB_DRIVER="${DB_DRIVER:-"postgres"}"
export GENERATE_COVERAGE="${GENERATE_COVERAGE:-"no"}"
[ "$GENERATE_COVERAGE" != 'no' ] && export XDEBUG_MODE=coverage
# Reset logs
OUTPUT_LOGS=data/log/api-tests/output.log
rm -rf data/log/api-tests
@ -22,7 +24,7 @@ echo 'Starting server...'
-o=logs.channels.server.output="${PWD}/${OUTPUT_LOGS}" &
sleep 2 # Let's give the server a couple of seconds to start
vendor/bin/phpunit --order-by=random -c phpunit-api.xml --testdox --colors=always $*
vendor/bin/phpunit --order-by=random -c phpunit-api.xml --testdox --testdox-summary $*
TESTS_EXIT_CODE=$?
[ "$TEST_RUNTIME" = 'rr' ] && bin/rr stop -w .

14
bin/test/run-cli-tests.sh Executable file
View File

@ -0,0 +1,14 @@
#!/usr/bin/env sh
export APP_ENV=test
export TEST_ENV=cli
export DB_DRIVER="${DB_DRIVER:-"maria"}"
export GENERATE_COVERAGE="${GENERATE_COVERAGE:-"no"}"
[ "$GENERATE_COVERAGE" != 'no' ] && export XDEBUG_MODE=coverage
vendor/bin/phpunit --order-by=random --testdox --testdox-summary -c phpunit-cli.xml $*
TESTS_EXIT_CODE=$?
# Exit this script with the same code as the tests. If tests failed, this script has to fail
exit $TESTS_EXIT_CODE

View File

@ -35,8 +35,8 @@ ${composerBin} install --no-dev --prefer-dist --optimize-autoloader --no-progres
echo 'Deleting dev files...'
rm composer.*
# Update Shlink version in config
sed -i "s/%SHLINK_VERSION%/${version}/g" config/autoload/app_options.global.php
# Update Shlink version
sed -i "s/%SHLINK_VERSION%/${version}/g" module/Core/src/Config/Options/AppOptions.php
# Compressing file
echo 'Compressing files...'

View File

@ -12,69 +12,67 @@
}
],
"require": {
"php": "^8.2",
"php": "^8.4",
"ext-curl": "*",
"ext-gd": "*",
"ext-json": "*",
"ext-mbstring": "*",
"ext-pdo": "*",
"akrabat/ip-address-middleware": "^2.1",
"cakephp/chronos": "^3.0.2",
"doctrine/dbal": "^4.0",
"doctrine/migrations": "^3.6",
"doctrine/orm": "^3.0",
"endroid/qr-code": "^5.0",
"akrabat/ip-address-middleware": "^2.6",
"cakephp/chronos": "^3.1",
"doctrine/dbal": "^4.3",
"doctrine/migrations": "^3.9",
"doctrine/orm": "^3.5",
"donatj/phpuseragentparser": "^1.10",
"friendsofphp/proxy-manager-lts": "^1.0",
"geoip2/geoip2": "^3.0",
"guzzlehttp/guzzle": "^7.5",
"jaybizzle/crawler-detect": "^1.2.116",
"laminas/laminas-config": "^3.8",
"laminas/laminas-config-aggregator": "^1.13",
"laminas/laminas-diactoros": "^3.3",
"laminas/laminas-inputfilter": "^2.27",
"laminas/laminas-servicemanager": "^3.21",
"laminas/laminas-stdlib": "^3.17",
"matomo/matomo-php-tracker": "^3.2",
"mezzio/mezzio": "^3.17",
"mezzio/mezzio-fastroute": "^3.11",
"mezzio/mezzio-problem-details": "^1.13",
"mlocati/ip-lib": "^1.18",
"mobiledetect/mobiledetectlib": "^4.8",
"geoip2/geoip2": "^3.1",
"guzzlehttp/guzzle": "^7.9",
"hidehalo/nanoid-php": "^2.0",
"jaybizzle/crawler-detect": "^1.3",
"laminas/laminas-config-aggregator": "^1.17",
"laminas/laminas-diactoros": "^3.5",
"laminas/laminas-inputfilter": "^2.31",
"laminas/laminas-servicemanager": "^3.23",
"laminas/laminas-stdlib": "^3.20",
"matomo/matomo-php-tracker": "^3.3",
"mezzio/mezzio": "^3.20",
"mezzio/mezzio-fastroute": "^3.12",
"mezzio/mezzio-problem-details": "^1.15",
"mlocati/ip-lib": "^1.18.1",
"pagerfanta/core": "^3.8",
"pugx/shortid-php": "^1.1",
"ramsey/uuid": "^4.7",
"shlinkio/doctrine-specification": "^2.1.1",
"shlinkio/shlink-common": "^6.0",
"shlinkio/shlink-config": "^3.0",
"shlinkio/shlink-event-dispatcher": "^4.0",
"shlinkio/shlink-importer": "^5.3",
"shlinkio/shlink-installer": "^9.0",
"shlinkio/shlink-ip-geolocation": "^4.0",
"shlinkio/shlink-json": "^1.1",
"spiral/roadrunner": "^2023.3",
"spiral/roadrunner-cli": "^2.6",
"spiral/roadrunner-http": "^3.3",
"spiral/roadrunner-jobs": "^4.3",
"symfony/console": "^7.0",
"symfony/filesystem": "^7.0",
"symfony/lock": "^7.0",
"symfony/process": "^7.0",
"symfony/string": "^7.0"
"shlinkio/doctrine-specification": "^2.2",
"shlinkio/shlink-common": "dev-main#f2550b5 as 7.3.0",
"shlinkio/shlink-config": "dev-main#fb186e4 as 4.1.0",
"shlinkio/shlink-event-dispatcher": "dev-main#54d4701 as 4.4.0",
"shlinkio/shlink-importer": "dev-main#4498f0a as 5.7.0",
"shlinkio/shlink-installer": "dev-develop#40e08cb as 10.0.0",
"shlinkio/shlink-ip-geolocation": "dev-main#e0c45b2 as 5.0.0",
"shlinkio/shlink-json": "dev-main#7c096d6 as 1.3.0",
"spiral/roadrunner": "^2025.1",
"spiral/roadrunner-cli": "^2.7",
"spiral/roadrunner-http": "^3.5",
"spiral/roadrunner-jobs": "^4.6",
"symfony/console": "^8.0 || ^7.4",
"symfony/filesystem": "^8.0",
"symfony/lock": "^8.0",
"symfony/process": "^8.0",
"symfony/string": "^8.0"
},
"require-dev": {
"devizzent/cebe-php-openapi": "^1.0.1",
"devizzent/cebe-php-openapi": "^1.1.2",
"devster/ubench": "^2.1",
"phpstan/phpstan": "^1.10",
"phpstan/phpstan-doctrine": "^1.3",
"phpstan/phpstan-phpunit": "^1.3",
"phpstan/phpstan-symfony": "^1.3",
"phpunit/php-code-coverage": "^10.1",
"phpunit/phpcov": "^9.0",
"phpunit/phpunit": "^10.4",
"phpstan/phpstan": "^2.1",
"phpstan/phpstan-doctrine": "^2.0",
"phpstan/phpstan-phpunit": "^2.0.5",
"phpstan/phpstan-symfony": "^2.0",
"phpunit/php-code-coverage": "^12.0",
"phpunit/phpcov": "^11.0",
"phpunit/phpunit": "^12.0.10",
"roave/security-advisories": "dev-master",
"shlinkio/php-coding-standard": "~2.3.0",
"shlinkio/shlink-test-utils": "^4.1",
"symfony/var-dumper": "^7.0",
"veewee/composer-run-parallel": "^1.3"
"shlinkio/php-coding-standard": "~2.5.0",
"shlinkio/shlink-test-utils": "^4.4",
"symfony/var-dumper": "^8.0",
"veewee/composer-run-parallel": "^1.4"
},
"conflict": {
"symfony/var-exporter": ">=6.3.9,<=6.4.0"
@ -108,62 +106,56 @@
},
"scripts": {
"ci": [
"@parallel cs stan swagger:validate test:unit:ci test:db:sqlite:ci test:db:postgres test:db:mysql test:db:maria test:db:ms",
"@parallel cs stan openapi:validate test:unit:ci test:db:sqlite:ci test:db:postgres test:db:mysql test:db:maria test:db:ms",
"@parallel test:api:ci test:cli:ci"
],
"cs": "phpcs -s",
"cs:fix": "phpcbf",
"stan": "APP_ENV=test php vendor/bin/phpstan analyse module/*/src module/*/test* module/*/config module/*/migrations config docker/config --level=8",
"stan": ["@putenv APP_ENV=test", "phpstan analyse"],
"test": [
"@parallel test:unit test:db",
"@parallel test:api test:cli"
],
"test:unit": "@php vendor/bin/phpunit --order-by=random --colors=always --testdox",
"test:unit:ci": "@test:unit --coverage-php=build/coverage-unit.cov",
"test:unit:pretty": "@test:unit --coverage-html build/coverage-unit/coverage-html",
"test:unit": ["@putenv COLUMNS=120", "phpunit --order-by=random --testdox --testdox-summary"],
"test:unit:ci": ["@putenv XDEBUG_MODE=coverage", "@test:unit --coverage-php=build/coverage-unit.cov"],
"test:unit:pretty": ["@putenv XDEBUG_MODE=coverage", "@test:unit --coverage-html build/coverage-unit/coverage-html"],
"test:db": "@parallel test:db:sqlite:ci test:db:mysql test:db:maria test:db:postgres test:db:ms",
"test:db:sqlite": "APP_ENV=test php vendor/bin/phpunit --order-by=random --colors=always --testdox -c phpunit-db.xml",
"test:db:sqlite:ci": "@test:db:sqlite --coverage-php build/coverage-db.cov",
"test:db:mysql": "DB_DRIVER=mysql composer test:db:sqlite",
"test:db:maria": "DB_DRIVER=maria composer test:db:sqlite",
"test:db:postgres": "DB_DRIVER=postgres composer test:db:sqlite",
"test:db:ms": "DB_DRIVER=mssql composer test:db:sqlite",
"test:db:sqlite": ["@putenv APP_ENV=test", "phpunit --order-by=random --testdox --testdox-summary -c phpunit-db.xml"],
"test:db:sqlite:ci": ["@putenv XDEBUG_MODE=coverage", "@test:db:sqlite --coverage-php build/coverage-db.cov"],
"test:db:mysql": ["@putenv DB_DRIVER=mysql", "@test:db:sqlite"],
"test:db:maria": ["@putenv DB_DRIVER=maria", "@test:db:sqlite"],
"test:db:postgres": ["@putenv DB_DRIVER=postgres", "@test:db:sqlite"],
"test:db:ms": ["@putenv DB_DRIVER=mssql", "@test:db:sqlite"],
"test:api": "bin/test/run-api-tests.sh",
"test:api:ci": "GENERATE_COVERAGE=yes composer test:api && vendor/bin/phpcov merge build/coverage-api --php build/coverage-api.cov && rm build/coverage-api/*.cov",
"test:api:pretty": "GENERATE_COVERAGE=yes composer test:api && vendor/bin/phpcov merge build/coverage-api --html build/coverage-api/coverage-html && rm build/coverage-api/*.cov",
"test:cli": "APP_ENV=test DB_DRIVER=maria TEST_ENV=cli php vendor/bin/phpunit --order-by=random --colors=always --testdox -c phpunit-cli.xml",
"test:cli:ci": "GENERATE_COVERAGE=yes composer test:cli && vendor/bin/phpcov merge build/coverage-cli --php build/coverage-cli.cov && rm build/coverage-cli/*.cov",
"test:cli:pretty": "GENERATE_COVERAGE=yes composer test:cli && vendor/bin/phpcov merge build/coverage-cli --html build/coverage-cli/coverage-html && rm build/coverage-cli/*.cov",
"swagger:validate": "php-openapi validate docs/swagger/swagger.json",
"swagger:inline": "php-openapi inline docs/swagger/swagger.json docs/swagger/swagger-inlined.json",
"test:api:sqlite": ["@putenv DB_DRIVER=sqlite", "@test:api"],
"test:api:mysql": ["@putenv DB_DRIVER=mysql", "@test:api"],
"test:api:maria": ["@putenv DB_DRIVER=maria", "@test:api"],
"test:api:mssql": ["@putenv DB_DRIVER=mssql", "@test:api"],
"test:api:ci": [
"@putenv GENERATE_COVERAGE=yes",
"@test:api",
"phpcov merge build/coverage-api --php build/coverage-api.cov && rm build/coverage-api/*.cov"
],
"test:api:pretty": [
"@putenv GENERATE_COVERAGE=yes",
"@test:api",
"phpcov merge build/coverage-api --html build/coverage-api/coverage-html && rm build/coverage-api/*.cov"
],
"test:cli": "bin/test/run-cli-tests.sh",
"test:cli:ci": [
"@putenv GENERATE_COVERAGE=yes",
"@test:cli",
"@php -d memory_limit=-1 vendor/bin/phpcov merge build/coverage-cli --php build/coverage-cli.cov && rm build/coverage-cli/*.cov"
],
"test:cli:pretty": [
"@putenv GENERATE_COVERAGE=yes",
"@test:cli",
"@php -d memory_limit=-1 phpcov merge build/coverage-cli --html build/coverage-cli/coverage-html && rm build/coverage-cli/*.cov"
],
"openapi:validate": "php-openapi validate docs/swagger/swagger.json",
"openapi:inline": "php-openapi inline docs/swagger/swagger.json docs/swagger/openapi-inlined.json",
"clean:dev": "rm -f data/database.sqlite && rm -f config/params/generated_config.php"
},
"scripts-descriptions": {
"ci": "<fg=blue;options=bold>Alias for \"cs\", \"stan\", \"swagger:validate\" and \"test:ci\"</>",
"cs": "<fg=blue;options=bold>Checks coding styles</>",
"cs:fix": "<fg=blue;options=bold>Fixes coding styles, when possible</>",
"stan": "<fg=blue;options=bold>Inspects code with phpstan</>",
"test": "<fg=blue;options=bold>Runs all test suites</>",
"test:unit": "<fg=blue;options=bold>Runs unit test suites</>",
"test:unit:ci": "<fg=blue;options=bold>Runs unit test suites, generating all needed reports and logs for CI envs</>",
"test:unit:pretty": "<fg=blue;options=bold>Runs unit test suites and generates an HTML code coverage report</>",
"test:db": "<fg=blue;options=bold>Runs database test suites on a SQLite, MySQL, MariaDB, PostgreSQL and MsSQL</>",
"test:db:sqlite": "<fg=blue;options=bold>Runs database test suites on a SQLite database</>",
"test:db:sqlite:ci": "<fg=blue;options=bold>Runs database test suites on a SQLite database, generating all needed reports and logs for CI envs</>",
"test:db:mysql": "<fg=blue;options=bold>Runs database test suites on a MySQL database</>",
"test:db:maria": "<fg=blue;options=bold>Runs database test suites on a MariaDB database</>",
"test:db:postgres": "<fg=blue;options=bold>Runs database test suites on a PostgreSQL database</>",
"test:db:ms": "<fg=blue;options=bold>Runs database test suites on a Microsoft SQL Server database</>",
"test:api": "<fg=blue;options=bold>Runs API test suites</>",
"test:api:ci": "<fg=blue;options=bold>Runs API test suites, and generates code coverage for CI</>",
"test:api:pretty": "<fg=blue;options=bold>Runs API test suites, and generates code coverage in HTML format</>",
"test:cli": "<fg=blue;options=bold>Runs CLI test suites</>",
"test:cli:ci": "<fg=blue;options=bold>Runs CLI test suites, and generates code coverage for CI</>",
"test:cli:pretty": "<fg=blue;options=bold>Runs CLI test suites, and generates code coverage in HTML format</>",
"swagger:validate": "<fg=blue;options=bold>Validates the swagger docs, making sure they fulfil the spec</>",
"swagger:inline": "<fg=blue;options=bold>Inlines swagger docs in a single file</>",
"clean:dev": "<fg=blue;options=bold>Deletes artifacts which are gitignored and could affect dev env</>"
},
"config": {
"sort-packages": true,
"platform-check": false,

View File

@ -1,2 +0,0 @@
local.php
*.local.php

View File

@ -1,12 +0,0 @@
<?php
declare(strict_types=1);
return [
'app_options' => [
'name' => 'Shlink',
'version' => '%SHLINK_VERSION%',
],
];

View File

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

View File

@ -6,17 +6,19 @@ use Shlinkio\Shlink\Core\Config\EnvVars;
return (static function (): array {
$redisServers = EnvVars::REDIS_SERVERS->loadFromEnv();
$redis = ['pub_sub_enabled' => $redisServers !== null && EnvVars::REDIS_PUB_SUB_ENABLED->loadFromEnv(false)];
$redis = ['pub_sub_enabled' => $redisServers !== null && EnvVars::REDIS_PUB_SUB_ENABLED->loadFromEnv()];
$cacheRedisBlock = $redisServers === null ? [] : [
'redis' => [
'servers' => $redisServers,
'sentinel_service' => EnvVars::REDIS_SENTINEL_SERVICE->loadFromEnv(),
'username' => EnvVars::REDIS_SERVERS_USER->loadFromEnv(),
'password' => EnvVars::REDIS_SERVERS_PASSWORD->loadFromEnv(),
],
];
return [
'cache' => [
'namespace' => EnvVars::CACHE_NAMESPACE->loadFromEnv('Shlink'),
'namespace' => EnvVars::CACHE_NAMESPACE->loadFromEnv(),
...$cacheRedisBlock,
],
'redis' => $redis,

View File

@ -1,20 +0,0 @@
<?php
declare(strict_types=1);
return [
'ip_address_resolution' => [
'headers_to_inspect' => [
'CF-Connecting-IP',
'X-Forwarded-For',
'X-Forwarded',
'Forwarded',
'True-Client-IP',
'X-Real-IP',
'X-Cluster-Client-Ip',
'Client-Ip',
],
],
];

View File

@ -3,13 +3,18 @@
declare(strict_types=1);
use Laminas\ConfigAggregator\ConfigAggregator;
use Shlinkio\Shlink\Core\Config\EnvVars;
return [
return (function () {
$isDev = EnvVars::isDevEnv();
'debug' => false,
return [
// Disabling config cache for cli, ensures it's never used for RoadRunner, and also that console
// commands don't generate a cache file that's then used by php-fpm web executions
ConfigAggregator::ENABLE_CACHE => PHP_SAPI !== 'cli',
'debug' => $isDev,
];
// Disabling config cache for cli, ensures it's never used for RoadRunner, and also that console
// commands don't generate a cache file that's then used by php-fpm web executions
ConfigAggregator::ENABLE_CACHE => ! $isDev && PHP_SAPI !== 'cli',
];
})();

View File

@ -1,12 +0,0 @@
<?php
declare(strict_types=1);
use Laminas\ConfigAggregator\ConfigAggregator;
return [
'debug' => true,
ConfigAggregator::ENABLE_CACHE => false,
];

View File

@ -1,11 +0,0 @@
<?php
declare(strict_types=1);
return [
'cors' => [
'max_age' => 3600,
],
];

View File

@ -1,20 +0,0 @@
<?php
declare(strict_types=1);
namespace Shlinkio\Shlink;
use Shlinkio\Shlink\Core\Config\EnvVars;
return (static function (): array {
$threshold = EnvVars::DELETE_SHORT_URL_THRESHOLD->loadFromEnv();
return [
'delete_short_urls' => [
'check_visits_threshold' => $threshold !== null,
'visits_threshold' => (int) ($threshold ?? DEFAULT_DELETE_SHORT_URL_THRESHOLD),
],
];
})();

View File

@ -11,6 +11,7 @@ use Psr\Http\Client\ClientInterface;
use Psr\Http\Message\ServerRequestFactoryInterface;
use Psr\Http\Message\StreamFactoryInterface;
use Psr\Http\Message\UploadedFileFactoryInterface;
use Shlinkio\Shlink\Core\Config\EnvVars;
use Spiral\RoadRunner\Http\PSR7Worker;
use Spiral\RoadRunner\WorkerInterface;
use Symfony\Component\Filesystem\Filesystem;
@ -36,7 +37,7 @@ return [
'lazy_services' => [
'proxies_target_dir' => 'data/proxies',
'proxies_namespace' => 'ShlinkProxy',
'write_proxy_files' => true,
'write_proxy_files' => EnvVars::isProdEnv(),
],
],

View File

@ -1,24 +0,0 @@
<?php
declare(strict_types=1);
use Psr\Container\ContainerInterface;
use Psr\Log;
return [
'dependencies' => [
'lazy_services' => [
'write_proxy_files' => false,
],
'initializers' => [
function (ContainerInterface $container, $instance): void {
if ($instance instanceof Log\LoggerAwareInterface) {
$instance->setLogger($container->get(Log\LoggerInterface::class));
}
},
],
],
];

View File

@ -2,16 +2,20 @@
declare(strict_types=1);
use Doctrine\ORM\Events;
use Happyr\DoctrineSpecification\Repository\EntitySpecificationRepository;
use Shlinkio\Shlink\Core\Config\EnvVars;
use Shlinkio\Shlink\Core\Visit\Listener\OrphanVisitsCountTracker;
use Shlinkio\Shlink\Core\Visit\Listener\ShortUrlVisitsCountTracker;
use function Shlinkio\Shlink\Core\ArrayUtils\contains;
return (static function (): array {
$driver = EnvVars::DB_DRIVER->loadFromEnv();
$useEncryption = (bool) EnvVars::DB_USE_ENCRYPTION->loadFromEnv();
$isMysqlCompatible = contains($driver, ['maria', 'mysql']);
$resolveDriver = static fn () => match ($driver) {
$doctrineDriver = match ($driver) {
'postgres' => 'pdo_pgsql',
'mssql' => 'pdo_sqlsrv',
default => 'pdo_mysql',
@ -20,36 +24,40 @@ return (static function (): array {
$value = $envVar->loadFromEnv();
return $value === null ? null : (string) $value;
};
$resolveDefaultPort = static fn () => match ($driver) {
'postgres' => '5432',
'mssql' => '1433',
default => '3306',
};
$resolveCharset = static fn () => match ($driver) {
$charset = match ($driver) {
// This does not determine charsets or collations in tables or columns, but the charset used in the data
// flowing in the connection, so it has to match what has been set in the database.
'maria', 'mysql' => 'utf8mb4',
'postgres' => 'utf8',
default => null,
};
$resolveConnection = static fn () => match ($driver) {
$driverOptions = match ($driver) {
'mssql' => ['TrustServerCertificate' => 'true'],
'maria', 'mysql' => ! $useEncryption ? [] : [
1007 => true, // PDO::MYSQL_ATTR_SSL_KEY: Require using SSL
1014 => false, // PDO::MYSQL_ATTR_SSL_VERIFY_SERVER_CERT: Trust any certificate
],
'postgres' => ! $useEncryption ? [] : [
'sslmode' => 'require', // Require connections to be encrypted
'sslrootcert' => '', // Allow any certificate
],
default => [],
};
$connection = match ($driver) {
null, 'sqlite' => [
'driver' => 'pdo_sqlite',
'path' => 'data/database.sqlite',
],
default => [
'driver' => $resolveDriver(),
'dbname' => EnvVars::DB_NAME->loadFromEnv('shlink'),
'driver' => $doctrineDriver,
'dbname' => EnvVars::DB_NAME->loadFromEnv(),
'user' => $readCredentialAsString(EnvVars::DB_USER),
'password' => $readCredentialAsString(EnvVars::DB_PASSWORD),
'host' => EnvVars::DB_HOST->loadFromEnv(EnvVars::DB_UNIX_SOCKET->loadFromEnv()),
'port' => EnvVars::DB_PORT->loadFromEnv($resolveDefaultPort()),
'host' => EnvVars::DB_HOST->loadFromEnv(),
'port' => EnvVars::DB_PORT->loadFromEnv(),
'unix_socket' => $isMysqlCompatible ? EnvVars::DB_UNIX_SOCKET->loadFromEnv() : null,
'charset' => $resolveCharset(),
'driverOptions' => $driver !== 'mssql' ? [] : [
'TrustServerCertificate' => 'true',
],
'charset' => $charset,
'driverOptions' => $driverOptions,
],
};
@ -60,8 +68,12 @@ return (static function (): array {
'proxies_dir' => 'data/proxies',
'load_mappings_using_functional_style' => true,
'default_repository_classname' => EntitySpecificationRepository::class,
'listeners' => [
Events::onFlush => [ShortUrlVisitsCountTracker::class, OrphanVisitsCountTracker::class],
Events::postFlush => [ShortUrlVisitsCountTracker::class, OrphanVisitsCountTracker::class],
],
],
'connection' => $resolveConnection(),
'connection' => $connection,
],
];

View File

@ -1,46 +0,0 @@
<?php
declare(strict_types=1);
return [
'entity_manager' => [
'connection' => [
// MySQL
'user' => 'root',
'password' => 'root',
'driver' => 'pdo_mysql',
'host' => 'shlink_db_mysql',
'dbname' => 'shlink',
// 'dbname' => 'shlink_foo',
'charset' => 'utf8mb4',
// MariaDB
// 'user' => 'root',
// 'password' => 'root',
// 'driver' => 'pdo_mysql',
// 'host' => 'shlink_db_maria',
// 'dbname' => 'shlink_foo',
// 'charset' => 'utf8mb4',
// Postgres
// 'user' => 'postgres',
// 'password' => 'root',
// 'driver' => 'pdo_pgsql',
// 'host' => 'shlink_db_postgres',
// 'dbname' => 'shlink_foo',
// 'charset' => 'utf8',
// MSSQL
// 'user' => 'sa',
// 'password' => 'Passw0rd!',
// 'driver' => 'pdo_sqlsrv',
// 'host' => 'shlink_db_ms',
// 'dbname' => 'shlink_foo',
// 'driverOptions' => [
// 'TrustServerCertificate' => 'true',
// ],
],
],
];

View File

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

View File

@ -12,6 +12,8 @@ return [
'installer' => [
'enabled_options' => [
Option\Server\RuntimeConfigOption::class,
Option\Server\MemoryLimitConfigOption::class,
Option\Server\LogsFormatConfigOption::class,
Option\Database\DatabaseDriverConfigOption::class,
Option\Database\DatabaseNameConfigOption::class,
Option\Database\DatabaseHostConfigOption::class,
@ -19,6 +21,7 @@ return [
Option\Database\DatabaseUserConfigOption::class,
Option\Database\DatabasePasswordConfigOption::class,
Option\Database\DatabaseUnixSocketConfigOption::class,
Option\Database\DatabaseUseEncryptionConfigOption::class,
Option\UrlShortener\ShortDomainHostConfigOption::class,
Option\UrlShortener\ShortDomainSchemaConfigOption::class,
Option\Redirect\BaseUrlRedirectConfigOption::class,
@ -30,6 +33,8 @@ return [
Option\Cache\CacheNamespaceConfigOption::class,
Option\Redis\RedisServersConfigOption::class,
Option\Redis\RedisSentinelServiceConfigOption::class,
Option\Redis\RedisServersUserConfigOption::class,
Option\Redis\RedisServersPasswordConfigOption::class,
Option\Redis\RedisPubSubConfigOption::class,
Option\UrlShortener\ShortCodeLengthOption::class,
Option\Mercure\EnableMercureConfigOption::class,
@ -39,11 +44,14 @@ return [
Option\UrlShortener\GeoLiteLicenseKeyConfigOption::class,
Option\UrlShortener\RedirectStatusCodeConfigOption::class,
Option\UrlShortener\RedirectCacheLifeTimeConfigOption::class,
Option\UrlShortener\RedirectCacheVisibilityConfigOption::class,
Option\UrlShortener\AutoResolveTitlesConfigOption::class,
Option\UrlShortener\AppendExtraPathConfigOption::class,
Option\UrlShortener\ExtraPathModeConfigOption::class,
Option\UrlShortener\EnableMultiSegmentSlugsConfigOption::class,
Option\UrlShortener\EnableTrailingSlashConfigOption::class,
Option\UrlShortener\ShortUrlModeConfigOption::class,
Option\Robots\RobotsAllowAllShortUrlsConfigOption::class,
Option\Robots\RobotsUserAgentsConfigOption::class,
Option\Tracking\IpAnonymizationConfigOption::class,
Option\Tracking\OrphanVisitsTrackingConfigOption::class,
Option\Tracking\DisableTrackParamConfigOption::class,
@ -52,15 +60,6 @@ return [
Option\Tracking\DisableIpTrackingConfigOption::class,
Option\Tracking\DisableReferrerTrackingConfigOption::class,
Option\Tracking\DisableUaTrackingConfigOption::class,
Option\QrCode\DefaultSizeConfigOption::class,
Option\QrCode\DefaultMarginConfigOption::class,
Option\QrCode\DefaultFormatConfigOption::class,
Option\QrCode\DefaultErrorCorrectionConfigOption::class,
Option\QrCode\DefaultRoundBlockSizeConfigOption::class,
Option\QrCode\DefaultColorConfigOption::class,
Option\QrCode\DefaultBgColorConfigOption::class,
Option\QrCode\DefaultLogoUrlConfigOption::class,
Option\QrCode\EnabledForDisabledShortUrlsConfigOption::class,
Option\RabbitMq\RabbitMqEnabledConfigOption::class,
Option\RabbitMq\RabbitMqHostConfigOption::class,
Option\RabbitMq\RabbitMqUseSslConfigOption::class,
@ -72,6 +71,11 @@ return [
Option\Matomo\MatomoBaseUrlConfigOption::class,
Option\Matomo\MatomoSiteIdConfigOption::class,
Option\Matomo\MatomoApiTokenConfigOption::class,
Option\RealTimeUpdates\RealTimeUpdatesTopicsConfigOption::class,
Option\Cors\CorsAllowOriginConfigOption::class,
Option\Cors\CorsAllowCredentialsConfigOption::class,
Option\Cors\CorsMaxAgeConfigOption::class,
Option\TrustedProxiesConfigOption::class,
],
'installation_commands' => [

View File

@ -0,0 +1,48 @@
<?php
declare(strict_types=1);
use RKA\Middleware\IpAddress;
use RKA\Middleware\Mezzio\IpAddressFactory;
use Shlinkio\Shlink\Core\Config\EnvVars;
use function Shlinkio\Shlink\Core\splitByComma;
use const Shlinkio\Shlink\IP_ADDRESS_REQUEST_ATTRIBUTE;
return (static function (): array {
$trustedProxies = EnvVars::TRUSTED_PROXIES->loadFromEnv();
$proxiesIsHopCount = is_numeric($trustedProxies);
return [
// Configuration for RKA\Middleware\IpAddress
'rka' => [
'ip_address' => [
'attribute_name' => IP_ADDRESS_REQUEST_ATTRIBUTE,
'check_proxy_headers' => true,
// List of trusted proxies
'trusted_proxies' => $proxiesIsHopCount ? [] : splitByComma($trustedProxies),
// Amount of addresses to skip from the right, before finding the visitor IP address
'hop_count' => $proxiesIsHopCount ? (int) $trustedProxies : 0,
'headers_to_inspect' => [
'CF-Connecting-IP',
'X-Forwarded-For',
'X-Forwarded',
'Forwarded',
'True-Client-IP',
'X-Real-IP',
'X-Cluster-Client-Ip',
'Client-Ip',
],
],
],
'dependencies' => [
'factories' => [
IpAddress::class => IpAddressFactory::class,
],
],
];
})();

View File

@ -4,6 +4,7 @@ declare(strict_types=1);
namespace Shlinkio\Shlink;
use Laminas\ServiceManager\AbstractFactory\ConfigAbstractFactory;
use Laminas\ServiceManager\Factory\InvokableFactory;
use Monolog\Level;
use Monolog\Logger;
@ -13,29 +14,45 @@ use Shlinkio\Shlink\Common\Logger\LoggerFactory;
use Shlinkio\Shlink\Common\Logger\LoggerType;
use Shlinkio\Shlink\Common\Middleware\AccessLogMiddleware;
use Shlinkio\Shlink\Common\Middleware\RequestIdMiddleware;
use Shlinkio\Shlink\Core\Config\EnvVars;
use Shlinkio\Shlink\Core\EventDispatcher\Helper\RequestIdProvider;
use Shlinkio\Shlink\EventDispatcher\Util\RequestIdProviderInterface;
use function Shlinkio\Shlink\Config\env;
use function Shlinkio\Shlink\Config\runningInRoadRunner;
return (static function (): array {
$common = [
'level' => Level::Info->value,
$isDev = EnvVars::isDevEnv();
$format = EnvVars::LOGS_FORMAT->loadFromEnv();
$buildCommonConfig = static fn (bool $addNewLine = false) => [
'level' => $isDev ? Level::Debug->value : Level::Info->value,
'processors' => [RequestIdMiddleware::class],
'line_format' =>
'[%datetime%] [%extra.' . RequestIdMiddleware::ATTRIBUTE . '%] %channel%.%level_name% - %message%',
'formatter' => [
'type' => $format,
'add_new_line' => $addNewLine,
'line_format' =>
'[%datetime%] [%extra.' . RequestIdMiddleware::ATTRIBUTE . '%] %channel%.%level_name% - %message%',
],
];
// In dev env or the docker container, stream Shlink logs to stderr, otherwise send them to a file
$useStreamForShlinkLogger = $isDev || env('SHLINK_RUNTIME') !== null;
return [
'logger' => [
'Shlink' => [
'Shlink' => $useStreamForShlinkLogger ? [
'type' => LoggerType::STREAM->value,
'destination' => 'php://stderr',
...$buildCommonConfig(),
] : [
'type' => LoggerType::FILE->value,
...$common,
...$buildCommonConfig(),
],
'Access' => [
'type' => LoggerType::STREAM->value,
'destination' => 'php://stderr',
'add_new_line' => ! runningInRoadRunner(),
...$common,
...$buildCommonConfig(! runningInRoadRunner()),
],
],
@ -44,14 +61,20 @@ return (static function (): array {
'Logger_Shlink' => [LoggerFactory::class, 'Shlink'],
'Logger_Access' => [LoggerFactory::class, 'Access'],
NullLogger::class => InvokableFactory::class,
RequestIdProvider::class => ConfigAbstractFactory::class,
],
'aliases' => [
'logger' => 'Logger_Shlink',
Logger::class => 'Logger_Shlink',
LoggerInterface::class => 'Logger_Shlink',
AccessLogMiddleware::LOGGER_SERVICE_NAME => 'Logger_Access',
RequestIdProviderInterface::class => RequestIdProvider::class,
],
],
ConfigAbstractFactory::class => [
RequestIdProvider::class => [RequestIdMiddleware::class],
],
];
})();

View File

@ -1,18 +0,0 @@
<?php
declare(strict_types=1);
use Monolog\Level;
use Shlinkio\Shlink\Common\Logger\LoggerType;
return [
'logger' => [
'Shlink' => [
'type' => LoggerType::STREAM->value,
'destination' => 'php://stderr',
'level' => Level::Debug->value,
],
],
];

View File

@ -1,16 +0,0 @@
<?php
declare(strict_types=1);
use Shlinkio\Shlink\Core\Config\EnvVars;
return [
'matomo' => [
'enabled' => (bool) EnvVars::MATOMO_ENABLED->loadFromEnv(false),
'base_url' => EnvVars::MATOMO_BASE_URL->loadFromEnv(),
'site_id' => EnvVars::MATOMO_SITE_ID->loadFromEnv(),
'api_token' => EnvVars::MATOMO_API_TOKEN->loadFromEnv(),
],
];

View File

@ -1,26 +0,0 @@
<?php
declare(strict_types=1);
/*
* Dev matomo instance needs to be manually configured once before enabling the configuration below.
*
* 1. Go to http://localhost:8003 and follow the installation instructions.
* 2. Open data/infra/matomo/config/config.ini.php and replace `trusted_hosts[] = "localhost"` with
* `trusted_hosts[] = "localhost:8003"` (see https://github.com/matomo-org/matomo/issues/9549)
* 3. Go to http://localhost:8003/index.php?module=SitesManager&action=index and paste the ID for the site you just
* created into the `site_id` field below.
* 4. Go to http://localhost:8003/index.php?module=UsersManager&action=userSecurity, scroll down, click
* "Create new token" and once generated, paste the token into the `api_token` field below.
*/
return [
'matomo' => [
// 'enabled' => true,
// 'base_url' => 'http://shlink_matomo',
// 'site_id' => '...',
// 'api_token' => '...',
],
];

View File

@ -8,34 +8,32 @@ use Shlinkio\Shlink\Core\Config\EnvVars;
use Symfony\Component\Mercure\Hub;
use Symfony\Component\Mercure\HubInterface;
return (static function (): array {
$publicUrl = EnvVars::MERCURE_PUBLIC_HUB_URL->loadFromEnv();
return [
return [
// This config is used by shlink-common. Do not delete
'mercure' => [
'enabled' => EnvVars::MERCURE_ENABLED->loadFromEnv(),
'public_hub_url' => EnvVars::MERCURE_PUBLIC_HUB_URL->loadFromEnv(),
'internal_hub_url' => EnvVars::MERCURE_INTERNAL_HUB_URL->loadFromEnv(),
'jwt_secret' => EnvVars::MERCURE_JWT_SECRET->loadFromEnv(),
'jwt_issuer' => 'Shlink',
],
'mercure' => [
'public_hub_url' => $publicUrl,
'internal_hub_url' => EnvVars::MERCURE_INTERNAL_HUB_URL->loadFromEnv($publicUrl),
'jwt_secret' => EnvVars::MERCURE_JWT_SECRET->loadFromEnv(),
'jwt_issuer' => 'Shlink',
],
'dependencies' => [
'delegators' => [
LcobucciJwtProvider::class => [
LazyServiceFactory::class,
],
Hub::class => [
LazyServiceFactory::class,
],
'dependencies' => [
'delegators' => [
LcobucciJwtProvider::class => [
LazyServiceFactory::class,
],
'lazy_services' => [
'class_map' => [
LcobucciJwtProvider::class => LcobucciJwtProvider::class,
Hub::class => HubInterface::class,
],
Hub::class => [
LazyServiceFactory::class,
],
],
'lazy_services' => [
'class_map' => [
LcobucciJwtProvider::class => LcobucciJwtProvider::class,
Hub::class => HubInterface::class,
],
],
],
];
})();
];

View File

@ -1,13 +0,0 @@
<?php
declare(strict_types=1);
return [
'mercure' => [
'public_hub_url' => 'http://localhost:8002',
'internal_hub_url' => 'http://shlink_mercure_proxy',
'jwt_secret' => 'mercure_jwt_key_long_enough_to_avoid_error',
],
];

View File

@ -11,6 +11,7 @@ use RKA\Middleware\IpAddress;
use Shlinkio\Shlink\Common\Middleware\AccessLogMiddleware;
use Shlinkio\Shlink\Common\Middleware\ContentLengthMiddleware;
use Shlinkio\Shlink\Common\Middleware\RequestIdMiddleware;
use Shlinkio\Shlink\Core\Geolocation\Middleware\IpGeolocationMiddleware;
return [
@ -67,8 +68,11 @@ return [
],
'not-found' => [
'middleware' => [
// This middleware is in front of tracking actions explicitly. Putting here for orphan visits tracking
// These two middlewares are in front of other tracking actions.
// Putting them here for orphan visits tracking
IpAddress::class,
IpGeolocationMiddleware::class,
Core\ErrorHandler\NotFoundTypeResolverMiddleware::class,
Core\ShortUrl\Middleware\ExtraPathRedirectMiddleware::class,
Core\ErrorHandler\NotFoundTrackerMiddleware::class,

View File

@ -1,36 +0,0 @@
<?php
declare(strict_types=1);
use Shlinkio\Shlink\Core\Config\EnvVars;
use const Shlinkio\Shlink\DEFAULT_QR_CODE_BG_COLOR;
use const Shlinkio\Shlink\DEFAULT_QR_CODE_COLOR;
use const Shlinkio\Shlink\DEFAULT_QR_CODE_ENABLED_FOR_DISABLED_SHORT_URLS;
use const Shlinkio\Shlink\DEFAULT_QR_CODE_ERROR_CORRECTION;
use const Shlinkio\Shlink\DEFAULT_QR_CODE_FORMAT;
use const Shlinkio\Shlink\DEFAULT_QR_CODE_MARGIN;
use const Shlinkio\Shlink\DEFAULT_QR_CODE_ROUND_BLOCK_SIZE;
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(
DEFAULT_QR_CODE_ERROR_CORRECTION,
),
'round_block_size' => (bool) EnvVars::DEFAULT_QR_CODE_ROUND_BLOCK_SIZE->loadFromEnv(
DEFAULT_QR_CODE_ROUND_BLOCK_SIZE,
),
'enabled_for_disabled_short_urls' => (bool) EnvVars::QR_CODE_FOR_DISABLED_SHORT_URLS->loadFromEnv(
DEFAULT_QR_CODE_ENABLED_FOR_DISABLED_SHORT_URLS,
),
'color' => EnvVars::DEFAULT_QR_CODE_COLOR->loadFromEnv(DEFAULT_QR_CODE_COLOR),
'bg_color' => EnvVars::DEFAULT_QR_CODE_BG_COLOR->loadFromEnv(DEFAULT_QR_CODE_BG_COLOR),
'logo_url' => EnvVars::DEFAULT_QR_CODE_LOGO_URL->loadFromEnv(),
],
];

View File

@ -6,14 +6,15 @@ use Shlinkio\Shlink\Core\Config\EnvVars;
return [
// This config is used by shlink-common. Do not delete
'rabbitmq' => [
'enabled' => (bool) EnvVars::RABBITMQ_ENABLED->loadFromEnv(false),
'enabled' => (bool) EnvVars::RABBITMQ_ENABLED->loadFromEnv(),
'host' => EnvVars::RABBITMQ_HOST->loadFromEnv(),
'use_ssl' => (bool) EnvVars::RABBITMQ_USE_SSL->loadFromEnv(false),
'port' => (int) EnvVars::RABBITMQ_PORT->loadFromEnv('5672'),
'use_ssl' => (bool) EnvVars::RABBITMQ_USE_SSL->loadFromEnv(),
'port' => (int) EnvVars::RABBITMQ_PORT->loadFromEnv(),
'user' => EnvVars::RABBITMQ_USER->loadFromEnv(),
'password' => EnvVars::RABBITMQ_PASSWORD->loadFromEnv(),
'vhost' => EnvVars::RABBITMQ_VHOST->loadFromEnv('/'),
'vhost' => EnvVars::RABBITMQ_VHOST->loadFromEnv(),
],
];

View File

@ -1,15 +0,0 @@
<?php
declare(strict_types=1);
return [
'rabbitmq' => [
'enabled' => true,
'host' => 'shlink_rabbitmq',
'port' => '5673',
'user' => 'rabbit',
'password' => 'rabbit',
],
];

View File

@ -1,25 +0,0 @@
<?php
declare(strict_types=1);
use Shlinkio\Shlink\Core\Config\EnvVars;
use const Shlinkio\Shlink\DEFAULT_REDIRECT_CACHE_LIFETIME;
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(),
],
'redirects' => [
'redirect_status_code' => (int) EnvVars::REDIRECT_STATUS_CODE->loadFromEnv(DEFAULT_REDIRECT_STATUS_CODE->value),
'redirect_cache_lifetime' => (int) EnvVars::REDIRECT_CACHE_LIFETIME->loadFromEnv(
DEFAULT_REDIRECT_CACHE_LIFETIME,
),
],
];

View File

@ -1,26 +0,0 @@
<?php
declare(strict_types=1);
return [
'cache' => [
'redis' => [
'servers' => 'tcp://shlink_redis:6379',
// 'servers' => 'tcp://barbar@shlink_redis_acl:6379',
// 'servers' => 'tcp://foo:bar@shlink_redis_acl:6379',
],
],
'redis' => [
'pub_sub_enabled' => true,
],
'dependencies' => [
'aliases' => [
// With this config, a user could alias 'lock_store' => 'redis_lock_store' to override the default
// 'lock_store' => 'redis_lock_store',
],
],
];

View File

@ -8,12 +8,12 @@ use Shlinkio\Shlink\Core\Config\EnvVars;
return [
'router' => [
'base_path' => EnvVars::BASE_PATH->loadFromEnv(''),
'base_path' => EnvVars::BASE_PATH->loadFromEnv(),
'fastroute' => [
// Disabling config cache for cli, ensures it's never used for RoadRunner, and also that console
// commands don't generate a cache file that's then used by php-fpm web executions
FastRouteRouter::CONFIG_CACHE_ENABLED => PHP_SAPI !== 'cli',
FastRouteRouter::CONFIG_CACHE_ENABLED => EnvVars::isProdEnv() && PHP_SAPI !== 'cli',
FastRouteRouter::CONFIG_CACHE_FILE => 'data/cache/fastroute_cached_routes.php',
],
],

View File

@ -1,16 +0,0 @@
<?php
declare(strict_types=1);
use Mezzio\Router\FastRouteRouter;
return [
'router' => [
// 'base_path' => '',
'fastroute' => [
FastRouteRouter::CONFIG_CACHE_ENABLED => false,
],
],
];

View File

@ -8,6 +8,7 @@ use Fig\Http\Message\RequestMethodInterface;
use RKA\Middleware\IpAddress;
use Shlinkio\Shlink\Core\Action as CoreAction;
use Shlinkio\Shlink\Core\Config\EnvVars;
use Shlinkio\Shlink\Core\Geolocation\Middleware\IpGeolocationMiddleware;
use Shlinkio\Shlink\Core\ShortUrl\Middleware\TrimTrailingSlashMiddleware;
use Shlinkio\Shlink\Rest\Action;
use Shlinkio\Shlink\Rest\ConfigProvider;
@ -19,9 +20,7 @@ use function sprintf;
return (static function (): array {
$dropDomainMiddleware = Middleware\ShortUrl\DropDefaultDomainFromRequestMiddleware::class;
$overrideDomainMiddleware = Middleware\ShortUrl\OverrideDomainMiddleware::class;
// TODO This should be based on config, not the env var
$shortUrlRouteSuffix = EnvVars::SHORT_URL_TRAILING_SLASH->loadFromEnv(false) ? '[/]' : '';
$shortUrlRouteSuffix = EnvVars::SHORT_URL_TRAILING_SLASH->loadFromEnv() ? '[/]' : '';
return [
@ -90,23 +89,17 @@ return (static function (): array {
'path' => '/{shortCode}/track',
'middleware' => [
IpAddress::class,
IpGeolocationMiddleware::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' => sprintf('/{shortCode}%s', $shortUrlRouteSuffix),
'middleware' => [
IpAddress::class,
IpGeolocationMiddleware::class,
TrimTrailingSlashMiddleware::class,
CoreAction\RedirectAction::class,
],

View File

@ -1,43 +0,0 @@
<?php
declare(strict_types=1);
use Shlinkio\Shlink\Core\Config\EnvVars;
return (static function (): array {
/** @var string|null $disableTrackingFrom */
$disableTrackingFrom = EnvVars::DISABLE_TRACKING_FROM->loadFromEnv();
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),
// 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),
// A query param that, if provided, will disable tracking of one particular visit. Always takes precedence
'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),
// 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),
// If true, the referrer will not be tracked
'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),
// A list of IP addresses, patterns or CIDR blocks from which tracking is disabled by default
'disable_tracking_from' => $disableTrackingFrom === null
? []
: array_map(trim(...), explode(',', $disableTrackingFrom)),
],
];
})();

View File

@ -1,35 +0,0 @@
<?php
declare(strict_types=1);
use Shlinkio\Shlink\Core\Config\EnvVars;
use Shlinkio\Shlink\Core\ShortUrl\Model\ShortUrlMode;
use const Shlinkio\Shlink\DEFAULT_SHORT_CODES_LENGTH;
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),
MIN_SHORT_CODES_LENGTH,
);
$modeFromEnv = EnvVars::SHORT_URL_MODE->loadFromEnv(ShortUrlMode::STRICT->value);
$mode = ShortUrlMode::tryFrom($modeFromEnv) ?? ShortUrlMode::STRICT;
return [
'url_shortener' => [
'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(true),
'append_extra_path' => (bool) EnvVars::REDIRECT_APPEND_EXTRA_PATH->loadFromEnv(false),
'multi_segment_slugs_enabled' => (bool) EnvVars::MULTI_SEGMENT_SLUGS_ENABLED->loadFromEnv(false),
'trailing_slash_enabled' => (bool) EnvVars::SHORT_URL_TRAILING_SLASH->loadFromEnv(false),
'mode' => $mode,
],
];
})();

View File

@ -1,21 +0,0 @@
<?php
declare(strict_types=1);
use function Shlinkio\Shlink\Config\runningInRoadRunner;
return [
'url_shortener' => [
'domain' => [
'schema' => 'http',
'hostname' => sprintf('localhost:%s', match (true) {
runningInRoadRunner() => '8800',
default => '8000',
}),
],
// 'multi_segment_slugs_enabled' => true,
// 'trailing_slash_enabled' => true,
],
];

View File

@ -8,18 +8,10 @@ use Laminas\ConfigAggregator;
use Laminas\Diactoros;
use Mezzio;
use Mezzio\ProblemDetails;
use Shlinkio\Shlink\Config\ConfigAggregator\EnvVarLoaderProvider;
use Shlinkio\Shlink\Core\Config\EnvVars;
use function Shlinkio\Shlink\Config\env;
use function Shlinkio\Shlink\Core\enumValues;
$isTestEnv = env('APP_ENV') === 'test';
return (new ConfigAggregator\ConfigAggregator(
return new ConfigAggregator\ConfigAggregator(
providers: [
! $isTestEnv
? new EnvVarLoaderProvider('config/params/generated_config.php', enumValues(Core\Config\EnvVars::class))
: new ConfigAggregator\ArrayProvider([]),
Mezzio\ConfigProvider::class,
Mezzio\Router\ConfigProvider::class,
Mezzio\Router\FastRouteRouter\ConfigProvider::class,
@ -34,10 +26,10 @@ return (new ConfigAggregator\ConfigAggregator(
CLI\ConfigProvider::class,
Rest\ConfigProvider::class,
new ConfigAggregator\PhpFileProvider('config/autoload/{,*.}global.php'),
// Local config should not be loaded during tests, whereas test config should be loaded ONLY during tests
new ConfigAggregator\PhpFileProvider(
$isTestEnv ? 'config/test/*.global.php' : 'config/autoload/{,*.}local.php',
),
// Test config should be loaded ONLY during tests
EnvVars::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'),
],
@ -47,4 +39,4 @@ return (new ConfigAggregator\ConfigAggregator(
Core\Config\PostProcessor\MultiSegmentSlugProcessor::class,
Core\Config\PostProcessor\ShortUrlMethodsProcessor::class,
],
))->getMergedConfig();
)->getMergedConfig();

View File

@ -11,14 +11,30 @@ const DEFAULT_SHORT_CODES_LENGTH = 5;
const MIN_SHORT_CODES_LENGTH = 4;
const DEFAULT_REDIRECT_STATUS_CODE = RedirectStatus::STATUS_302;
const DEFAULT_REDIRECT_CACHE_LIFETIME = 30;
const DEFAULT_REDIRECT_CACHE_VISIBILITY = 'private';
const LOCAL_LOCK_FACTORY = 'Shlinkio\Shlink\LocalLockFactory';
const TITLE_TAG_VALUE = '/<title[^>]*>(.*?)<\/title>/i'; // Matches the value inside a html title tag
const LOOSE_URI_MATCHER = '/(.+)\:(.+)/i'; // Matches anything starting with a schema.
const DEFAULT_QR_CODE_SIZE = 300;
const DEFAULT_QR_CODE_MARGIN = 0;
const DEFAULT_QR_CODE_FORMAT = 'png';
const DEFAULT_QR_CODE_ERROR_CORRECTION = 'l';
const DEFAULT_QR_CODE_ROUND_BLOCK_SIZE = true;
const DEFAULT_QR_CODE_ENABLED_FOR_DISABLED_SHORT_URLS = true;
const DEFAULT_QR_CODE_COLOR = '#000000'; // Black
const DEFAULT_QR_CODE_BG_COLOR = '#ffffff'; // White
const IP_ADDRESS_REQUEST_ATTRIBUTE = 'remote_address';
const REDIRECT_URL_REQUEST_ATTRIBUTE = 'redirect_url';
/**
* List of ISO 3166-1 alpha-2 two-letter country codes https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2
*/
const ISO_COUNTRY_CODES = [
'AF', 'AX', 'AL', 'DZ', 'AS', 'AD', 'AO', 'AI', 'AQ', 'AG', 'AR', 'AM', 'AW', 'AU', 'AT', 'AZ',
'BS', 'BH', 'BD', 'BB', 'BY', 'BE', 'BZ', 'BJ', 'BM', 'BT', 'BO', 'BQ', 'BA', 'BW', 'BV', 'BR',
'IO', 'BN', 'BG', 'BF', 'BI', 'CV', 'KH', 'CM', 'CA', 'KY', 'CF', 'TD', 'CL', 'CN', 'CX', 'CC',
'CO', 'KM', 'CG', 'CD', 'CK', 'CR', 'CI', 'HR', 'CU', 'CW', 'CY', 'CZ', 'DK', 'DJ', 'DM', 'DO',
'EC', 'EG', 'SV', 'GQ', 'ER', 'EE', 'SZ', 'ET', 'FK', 'FO', 'FJ', 'FI', 'FR', 'GF', 'PF', 'TF',
'GA', 'GM', 'GE', 'DE', 'GH', 'GI', 'GR', 'GL', 'GD', 'GP', 'GU', 'GT', 'GG', 'GN', 'GW', 'GY',
'HT', 'HM', 'VA', 'HN', 'HK', 'HU', 'IS', 'IN', 'ID', 'IR', 'IQ', 'IE', 'IM', 'IL', 'IT', 'JM',
'JP', 'JE', 'JO', 'KZ', 'KE', 'KI', 'KP', 'KR', 'KW', 'KG', 'LA', 'LV', 'LB', 'LS', 'LR', 'LY',
'LI', 'LT', 'LU', 'MO', 'MG', 'MW', 'MY', 'MV', 'ML', 'MT', 'MH', 'MQ', 'MR', 'MU', 'YT', 'MX',
'FM', 'MD', 'MC', 'MN', 'ME', 'MS', 'MA', 'MZ', 'MM', 'NA', 'NR', 'NP', 'NL', 'NC', 'NZ', 'NI',
'NE', 'NG', 'NU', 'NF', 'MK', 'MP', 'NO', 'OM', 'PK', 'PW', 'PS', 'PA', 'PG', 'PY', 'PE', 'PH',
'PN', 'PL', 'PT', 'PR', 'QA', 'RE', 'RO', 'RU', 'RW', 'BL', 'SH', 'KN', 'LC', 'MF', 'PM', 'VC',
'WS', 'SM', 'ST', 'SA', 'SN', 'RS', 'SC', 'SL', 'SG', 'SX', 'SK', 'SI', 'SB', 'SO', 'ZA', 'GS',
'SS', 'ES', 'LK', 'SD', 'SR', 'SJ', 'SE', 'CH', 'SY', 'TW', 'TJ', 'TZ', 'TH', 'TL', 'TG', 'TK',
'TO', 'TT', 'TN', 'TR', 'TM', 'TC', 'TV', 'UG', 'UA', 'AE', 'GB', 'US', 'UM', 'UY', 'UZ', 'VU',
'VE', 'VN', 'VG', 'VI', 'WF', 'EH', 'YE', 'ZM', 'ZW',
];

View File

@ -6,14 +6,29 @@ use Laminas\ServiceManager\ServiceManager;
use Shlinkio\Shlink\Core\Config\EnvVars;
use Symfony\Component\Lock;
use function Shlinkio\Shlink\Config\loadEnvVarsFromConfig;
use function Shlinkio\Shlink\Core\enumValues;
use const Shlinkio\Shlink\LOCAL_LOCK_FACTORY;
// Set current directory to the project's root directory
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()));
// Promote env vars from installer, dev config or test config
loadEnvVarsFromConfig(
EnvVars::isTestEnv() ? 'config/test/shlink_test_env.php' : 'config/params/*.php',
enumValues(EnvVars::class),
);
// This is one of the first files loaded. Set global configuration here
error_reporting(
// Set a less strict error reporting for prod, where deprecation warnings should be ignored
EnvVars::isProdEnv() ? E_ALL & ~E_DEPRECATED & ~E_USER_DEPRECATED : E_ALL,
);
ini_set('memory_limit', EnvVars::MEMORY_LIMIT->loadFromEnv());
date_default_timezone_set(EnvVars::TIMEZONE->loadFromEnv());
// 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

View File

@ -1,2 +1,3 @@
*
!.gitignore
!*.dist

View File

@ -0,0 +1,79 @@
<?php
declare(strict_types=1);
use Shlinkio\Shlink\Core\Config\EnvVars;
use function Shlinkio\Shlink\Config\runningInRoadRunner;
return [
EnvVars::APP_ENV->value => 'dev',
// EnvVars::GEOLITE_LICENSE_KEY->value => '',
// URL shortener
EnvVars::DEFAULT_DOMAIN->value => runningInRoadRunner() ? 'localhost:8800' : 'localhost:8008',
EnvVars::IS_HTTPS_ENABLED->value => false,
// Database - MySQL
EnvVars::DB_DRIVER->value => 'mysql',
EnvVars::DB_USER->value => 'root',
EnvVars::DB_PASSWORD->value => 'root',
EnvVars::DB_NAME->value => 'shlink',
// EnvVars::DB_NAME->value => 'shlink_foo',
EnvVars::DB_HOST->value => 'shlink_db_mysql',
// Database - Maria
// EnvVars::DB_DRIVER->value => 'maria',
// EnvVars::DB_USER->value => 'root',
// EnvVars::DB_PASSWORD->value => 'root',
// EnvVars::DB_NAME->value => 'shlink_foo',
// EnvVars::DB_HOST->value => 'shlink_db_maria',
// Database - Postgres
// EnvVars::DB_DRIVER->value => 'postgres',
// EnvVars::DB_USER->value => 'postgres',
// EnvVars::DB_PASSWORD->value => 'root',
// EnvVars::DB_NAME->value => 'shlink_foo',
// EnvVars::DB_HOST->value => 'shlink_db_postgres',
// Database - MSSQL
// EnvVars::DB_DRIVER->value => 'mssql',
// EnvVars::DB_USER->value => 'sa',
// EnvVars::DB_PASSWORD->value => 'Passw0rd!',
// EnvVars::DB_NAME->value => 'shlink_foo',
// EnvVars::DB_HOST->value => 'shlink_db_ms',
// Matomo
// Dev matomo instance needs to be manually configured once before enabling the configuration below:
// 1. Go to http://localhost:8003 and follow the installation instructions.
// 2. Open data/infra/matomo/config/config.ini.php and replace `trusted_hosts[] = "localhost"` with
// `trusted_hosts[] = "localhost:8003"` (see https://github.com/matomo-org/matomo/issues/9549)
// 3. Go to http://localhost:8003/index.php?module=SitesManager&action=index and paste the ID for the site you just
// created into the `MATOMO_SITE_ID` var below.
// 4. Go to http://localhost:8003/index.php?module=UsersManager&action=userSecurity, scroll down, click
// "Create new token" and once generated, paste the token into the `MATOMO_API_TOKEN` var below.
// 5. Copy the config below and paste it in a new shlink-dev.local.env file.
EnvVars::MATOMO_ENABLED->value => false,
EnvVars::MATOMO_BASE_URL->value => 'http://shlink_matomo',
// EnvVars::MATOMO_SITE_ID->value => ,
// EnvVars::MATOMO_API_TOKEN->value => ,
// Mercure
EnvVars::MERCURE_ENABLED->value => true,
EnvVars::MERCURE_PUBLIC_HUB_URL->value => 'http://localhost:8002',
EnvVars::MERCURE_INTERNAL_HUB_URL->value => 'http://shlink_mercure_proxy',
EnvVars::MERCURE_JWT_SECRET->value => 'mercure_jwt_key_long_enough_to_avoid_error',
// RabbitMQ
EnvVars::RABBITMQ_ENABLED->value => true,
EnvVars::RABBITMQ_HOST->value => 'shlink_rabbitmq',
EnvVars::RABBITMQ_PORT->value => 5672,
EnvVars::RABBITMQ_USER->value => 'rabbit',
EnvVars::RABBITMQ_PASSWORD->value => 'rabbit',
// Redis
EnvVars::REDIS_PUB_SUB_ENABLED->value => true,
EnvVars::REDIS_SERVERS->value => 'tcp://shlink_redis:6379',
];

View File

@ -30,13 +30,17 @@ jobs:
prefetch: 10
logs:
encoding: console
mode: development
channels:
http:
mode: 'off' # Disable logging as Shlink handles it internally
server:
encoding: console
level: info
metrics:
encoding: console
level: debug
jobs:
encoding: console
level: debug

View File

@ -35,15 +35,16 @@ jobs:
prefetch: 10
logs:
encoding: json
encoding: console
mode: development
channels:
http:
mode: 'off' # Disable logging as Shlink handles it internally
server:
encoding: json
encoding: console
level: info
metrics:
level: panic
jobs:
encoding: console
level: panic

View File

@ -7,18 +7,20 @@ server:
command: 'php -dopcache.enable_cli=1 -dopcache.validate_timestamps=0 ../../bin/roadrunner-worker.php'
http:
address: '0.0.0.0:${PORT:-8080}'
address: '${ADDRESS:-0.0.0.0}:${PORT:-8080}'
middleware: ['static']
static:
dir: '../../public'
forbid: ['.php', '.htaccess']
pool:
num_workers: ${WEB_WORKER_NUM:-0}
max_jobs: 250 # Restart worker after processing this amount of requests to mitigate memory leaks
jobs:
timeout: 300 # 5 minutes
pool:
num_workers: ${TASK_WORKER_NUM:-0}
max_jobs: 250 # Restart worker after processing this amount of jobs to mitigate memory leaks
consume: ['shlink']
pipelines:
shlink:
@ -28,11 +30,14 @@ jobs:
prefetch: 10
logs:
encoding: ${LOGS_FORMAT:-console}
mode: production
channels:
http:
mode: 'off' # Disable logging as Shlink handles it internally
server:
encoding: ${LOGS_FORMAT:-console}
level: info
jobs:
encoding: ${LOGS_FORMAT:-console}
level: debug

View File

@ -11,5 +11,11 @@ const ANDROID_USER_AGENT = 'Mozilla/5.0 (Linux; Android 13) AppleWebKit/537.36 (
. 'Chrome/109.0.5414.86 Mobile Safari/537.36';
const IOS_USER_AGENT = 'Mozilla/5.0 (iPhone; CPU iPhone OS 16_2 like Mac OS X) AppleWebKit/605.1.15 '
. '(KHTML, like Gecko) FxiOS/109.0 Mobile/15E148 Safari/605.1.15';
const DESKTOP_USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like '
. 'Gecko) Chrome/109.0.0.0 Safari/537.36 Edg/109.0.1518.61';
const WINDOWS_USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) '
. 'Chrome/138.0.0.0 Safari/537.36 Edg/138.0.3351.95';
const LINUX_USER_AGENT = 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) '
. 'HeadlessChrome/81.0.4044.113 Safari/537.36';
const MACOS_USER_AGENT = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 15_5) AppleWebKit/605.1.15 (KHTML, like Gecko) '
. 'Version/18.4 Safari/605.1.15';
const CHROMEOS_USER_AGENT = 'Mozilla/5.0 (X11; CrOS x86_64 16181.61.0) AppleWebKit/537.36 (KHTML, like Gecko) '
. 'Chrome/134.0.6998.198 Safari/537.36';

View File

@ -0,0 +1,15 @@
<?php
declare(strict_types=1);
use Shlinkio\Shlink\Core\Config\EnvVars;
return [
EnvVars::APP_ENV->value => 'test',
// URL shortener
EnvVars::DEFAULT_DOMAIN->value => 's.test',
EnvVars::IS_HTTPS_ENABLED->value => false,
];

View File

@ -93,13 +93,6 @@ return [
ConfigAggregator::ENABLE_CACHE => false,
FastRouteRouter::CONFIG_CACHE_ENABLED => false,
'url_shortener' => [
'domain' => [
'schema' => 'http',
'hostname' => 's.test',
],
],
'routes' => [
// This route is used to test that title resolution is skipped if the long URL times out
[
@ -120,13 +113,6 @@ return [
],
],
// Disable mercure integration during E2E tests
'mercure' => [
'public_hub_url' => null,
'internal_hub_url' => null,
'jwt_secret' => null,
],
'dependencies' => [
'services' => [
'shlink_test_api_client' => new Client([

View File

@ -3,7 +3,7 @@
set -ex
curl https://packages.microsoft.com/keys/microsoft.asc | apt-key add -
curl https://packages.microsoft.com/config/ubuntu/22.04/prod.list > /etc/apt/sources.list.d/mssql-release.list
curl https://packages.microsoft.com/config/ubuntu/24.04/prod.list > /etc/apt/sources.list.d/mssql-release.list
apt-get update
ACCEPT_EULA=Y apt-get install msodbcsql18
# apt-get install unixodbc-dev

View File

@ -11,7 +11,7 @@ server {
location ~ \.php$ {
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass unix:/var/run/php/php8.3-fpm.sock;
fastcgi_pass unix:/var/run/php/php8.4-fpm.sock;
fastcgi_index index.php;
include fastcgi.conf;
}

View File

@ -0,0 +1,52 @@
FROM dunglas/frankenphp:1-php8.4-alpine
MAINTAINER Alejandro Celaya <alejandro@alejandrocelaya.com>
ENV PDO_SQLSRV_VERSION='5.12.0'
ENV MS_ODBC_DOWNLOAD='7/6/d/76de322a-d860-4894-9945-f0cc5d6a45f8'
ENV MS_ODBC_SQL_VERSION='18_18.4.1.1'
RUN apk update
# Install common php extensions
RUN docker-php-ext-install pdo_mysql
RUN docker-php-ext-install calendar
RUN apk add --no-cache oniguruma-dev
RUN docker-php-ext-install mbstring
RUN apk add --no-cache sqlite-libs
RUN apk add --no-cache sqlite-dev
RUN docker-php-ext-install pdo_sqlite
RUN apk add --no-cache icu-dev
RUN docker-php-ext-install intl
RUN apk add --no-cache libzip-dev zlib-dev
RUN docker-php-ext-install zip
RUN apk add --no-cache postgresql-dev
RUN docker-php-ext-install pdo_pgsql
RUN apk add --no-cache --virtual .phpize-deps $PHPIZE_DEPS linux-headers && \
docker-php-ext-install sockets && \
apk del .phpize-deps
RUN docker-php-ext-install bcmath
# Install xdebug and sqlsrv driver
RUN apk add --update linux-headers && \
wget https://download.microsoft.com/download/${MS_ODBC_DOWNLOAD}/msodbcsql${MS_ODBC_SQL_VERSION}-1_amd64.apk && \
apk add --allow-untrusted msodbcsql${MS_ODBC_SQL_VERSION}-1_amd64.apk && \
apk add --no-cache --virtual .phpize-deps $PHPIZE_DEPS unixodbc-dev && \
pecl install pdo_sqlsrv-${PDO_SQLSRV_VERSION} xdebug && \
docker-php-ext-enable pdo_sqlsrv xdebug && \
apk del .phpize-deps && \
rm msodbcsql${MS_ODBC_SQL_VERSION}-1_amd64.apk
# Install composer
COPY --from=composer:2 /usr/bin/composer /usr/local/bin/composer
# Make home directory writable by anyone
RUN chmod 777 /home
VOLUME /home/shlink
WORKDIR /home/shlink

View File

@ -0,0 +1,2 @@
*
!.gitignore

2
data/infra/frankenphp_caddy_data/.gitignore vendored Executable file
View File

@ -0,0 +1,2 @@
*
!.gitignore

View File

@ -1,10 +1,10 @@
FROM php:8.3-fpm-alpine3.19
FROM php:8.4-fpm-alpine3.21
MAINTAINER Alejandro Celaya <alejandro@alejandrocelaya.com>
ENV APCU_VERSION 5.1.23
ENV PDO_SQLSRV_VERSION 5.12.0
ENV MS_ODBC_DOWNLOAD 'b/9/f/b9f3cce4-3925-46d4-9f46-da08869c6486'
ENV MS_ODBC_SQL_VERSION 18_18.1.1.1
ENV APCU_VERSION='5.1.24'
ENV PDO_SQLSRV_VERSION='5.12.0'
ENV MS_ODBC_DOWNLOAD='7/6/d/76de322a-d860-4894-9945-f0cc5d6a45f8'
ENV MS_ODBC_SQL_VERSION='18_18.4.1.1'
RUN apk update
@ -25,9 +25,6 @@ RUN docker-php-ext-install intl
RUN apk add --no-cache libzip-dev zlib-dev
RUN docker-php-ext-install zip
RUN apk add --no-cache libpng-dev
RUN docker-php-ext-install gd
RUN apk add --no-cache postgresql-dev
RUN docker-php-ext-install pdo_pgsql
@ -46,12 +43,13 @@ RUN mkdir -p /usr/src/php/ext/apcu \
&& rm /usr/local/etc/php/conf.d/docker-php-ext-apcu.ini \
&& echo extension=apcu.so > /usr/local/etc/php/conf.d/20-php-ext-apcu.ini
# Install pcov and sqlsrv driver
RUN wget https://download.microsoft.com/download/${MS_ODBC_DOWNLOAD}/msodbcsql${MS_ODBC_SQL_VERSION}-1_amd64.apk && \
# Install xdebug and sqlsrv driver
RUN apk add --update linux-headers && \
wget https://download.microsoft.com/download/${MS_ODBC_DOWNLOAD}/msodbcsql${MS_ODBC_SQL_VERSION}-1_amd64.apk && \
apk add --allow-untrusted msodbcsql${MS_ODBC_SQL_VERSION}-1_amd64.apk && \
apk add --no-cache --virtual .phpize-deps $PHPIZE_DEPS unixodbc-dev && \
pecl install pdo_sqlsrv-${PDO_SQLSRV_VERSION} pcov && \
docker-php-ext-enable pdo_sqlsrv pcov && \
pecl install pdo_sqlsrv-${PDO_SQLSRV_VERSION} xdebug && \
docker-php-ext-enable pdo_sqlsrv xdebug && \
apk del .phpize-deps && \
rm msodbcsql${MS_ODBC_SQL_VERSION}-1_amd64.apk

View File

@ -1,8 +1,4 @@
display_errors=On
error_reporting=-1
memory_limit=-1
log_errors_max_len=0
zend.assertions=1
assert.exception=1
pcov.enabled=1
pcov.directory=module

View File

@ -1,10 +1,9 @@
FROM php:8.3-alpine3.19
FROM php:8.4-alpine3.21
MAINTAINER Alejandro Celaya <alejandro@alejandrocelaya.com>
ENV APCU_VERSION 5.1.23
ENV PDO_SQLSRV_VERSION 5.12.0
ENV MS_ODBC_DOWNLOAD 'b/9/f/b9f3cce4-3925-46d4-9f46-da08869c6486'
ENV MS_ODBC_SQL_VERSION 18_18.1.1.1
ENV PDO_SQLSRV_VERSION='5.12.0'
ENV MS_ODBC_DOWNLOAD='7/6/d/76de322a-d860-4894-9945-f0cc5d6a45f8'
ENV MS_ODBC_SQL_VERSION='18_18.4.1.1'
RUN apk update
@ -25,9 +24,6 @@ RUN docker-php-ext-install intl
RUN apk add --no-cache libzip-dev zlib-dev
RUN docker-php-ext-install zip
RUN apk add --no-cache libpng-dev
RUN docker-php-ext-install gd
RUN apk add --no-cache postgresql-dev
RUN docker-php-ext-install pdo_pgsql
@ -36,22 +32,13 @@ RUN apk add --no-cache --virtual .phpize-deps $PHPIZE_DEPS linux-headers && \
apk del .phpize-deps
RUN docker-php-ext-install bcmath
# Install APCu extension
ADD https://pecl.php.net/get/apcu-$APCU_VERSION.tgz /tmp/apcu.tar.gz
RUN mkdir -p /usr/src/php/ext/apcu \
&& tar xf /tmp/apcu.tar.gz -C /usr/src/php/ext/apcu --strip-components=1 \
&& docker-php-ext-configure apcu \
&& docker-php-ext-install apcu \
&& rm /tmp/apcu.tar.gz \
&& rm /usr/local/etc/php/conf.d/docker-php-ext-apcu.ini \
&& echo extension=apcu.so > /usr/local/etc/php/conf.d/20-php-ext-apcu.ini
# Install pcov and sqlsrv driver
RUN wget https://download.microsoft.com/download/${MS_ODBC_DOWNLOAD}/msodbcsql${MS_ODBC_SQL_VERSION}-1_amd64.apk && \
# Install xdebug and sqlsrv driver
RUN apk add --update linux-headers && \
wget https://download.microsoft.com/download/${MS_ODBC_DOWNLOAD}/msodbcsql${MS_ODBC_SQL_VERSION}-1_amd64.apk && \
apk add --allow-untrusted msodbcsql${MS_ODBC_SQL_VERSION}-1_amd64.apk && \
apk add --no-cache --virtual .phpize-deps $PHPIZE_DEPS unixodbc-dev && \
pecl install pdo_sqlsrv-${PDO_SQLSRV_VERSION} pcov && \
docker-php-ext-enable pdo_sqlsrv pcov && \
pecl install pdo_sqlsrv-${PDO_SQLSRV_VERSION} xdebug && \
docker-php-ext-enable pdo_sqlsrv xdebug && \
apk del .phpize-deps && \
rm msodbcsql${MS_ODBC_SQL_VERSION}-1_amd64.apk
@ -72,5 +59,7 @@ CMD \
if [[ ! -d "./vendor" ]]; then /usr/local/bin/composer install ; fi && \
# Download roadrunner binary
if [[ ! -f "./bin/rr" ]]; then ./vendor/bin/rr get --no-interaction --no-config --location bin/ && chmod +x bin/rr ; fi && \
# This forces the app to be started every second until the exit code is 0
until ./bin/rr serve -c config/roadrunner/.rr.dev.yml; do sleep 1 ; done
# Create env file if it does not exist yet
if [[ ! -f "./config/params/shlink_dev_env.php" ]]; then cp ./config/params/shlink_dev_env.php.dist ./config/params/shlink_dev_env.php ; fi && \
# Run with `exec` so that signals are properly handled
exec ./bin/rr serve -c config/roadrunner/.rr.dev.yml

2
data/temp-geolite/.gitignore vendored Executable file
View File

@ -0,0 +1,2 @@
*
!.gitignore

View File

@ -1,14 +1,15 @@
version: '3'
services:
shlink_db_mysql:
user: '0'
environment:
MYSQL_DATABASE: shlink_test
shlink_db_postgres:
user: '0'
environment:
POSTGRES_DB: shlink_test
shlink_db_maria:
user: '0'
environment:
MYSQL_DATABASE: shlink_test

View File

@ -1,32 +0,0 @@
version: '3'
services:
shlink_php:
user: 1000:1000
volumes:
- /etc/passwd:/etc/passwd:ro
- /etc/group:/etc/group:ro
shlink_roadrunner:
user: 1000:1000
volumes:
- /etc/passwd:/etc/passwd:ro
- /etc/group:/etc/group:ro
shlink_db_mysql:
user: 1000:1000
volumes:
- /etc/passwd:/etc/passwd:ro
- /etc/group:/etc/group:ro
shlink_db_postgres:
user: 1000:1000
volumes:
- /etc/passwd:/etc/passwd:ro
- /etc/group:/etc/group:ro
shlink_db_maria:
user: 1000:1000
volumes:
- /etc/passwd:/etc/passwd:ro
- /etc/group:/etc/group:ro

View File

@ -1,5 +1,3 @@
version: '3'
services:
shlink_nginx:
container_name: shlink_nginx
@ -15,6 +13,7 @@ services:
shlink_php:
container_name: shlink_php
user: 1000:1000
build:
context: .
dockerfile: ./data/infra/php.Dockerfile
@ -36,11 +35,13 @@ services:
- shlink_matomo
environment:
LC_ALL: C
DEFAULT_DOMAIN: localhost:8000
extra_hosts:
- 'host.docker.internal:host-gateway'
shlink_roadrunner:
container_name: shlink_roadrunner
user: 1000:1000
build:
context: .
dockerfile: ./data/infra/roadrunner.Dockerfile
@ -65,8 +66,40 @@ services:
extra_hosts:
- 'host.docker.internal:host-gateway'
shlink_frankenphp:
container_name: shlink_frankenphp
user: 1000:1000
build:
context: .
dockerfile: ./data/infra/frankenphp.Dockerfile
ports:
- "8008:8008"
volumes:
- ./:/home/shlink
- ./data/infra/php.ini:/usr/local/etc/php/php.ini
- ./data/infra/frankenphp_caddy_data:/data
- ./data/infra/frankenphp_caddy_config:/config
links:
- shlink_db_mysql
- shlink_db_postgres
- shlink_db_maria
- shlink_db_ms
- shlink_redis
- shlink_redis_acl
- shlink_mercure
- shlink_mercure_proxy
- shlink_rabbitmq
- shlink_matomo
environment:
FRANKENPHP_CONFIG: 'worker /home/shlink/bin/frankenphp-worker.php'
SERVER_NAME: ':8008 https:8009'
extra_hosts:
- 'host.docker.internal:host-gateway'
tty: true
shlink_db_mysql:
container_name: shlink_db_mysql
user: 1000:1000
image: mysql:8.0
ports:
- "3307:3306"
@ -79,7 +112,8 @@ services:
shlink_db_postgres:
container_name: shlink_db_postgres
image: postgres:12.2-alpine
user: 1000:1000
image: postgres:16.3-alpine
ports:
- "5434:5432"
volumes:
@ -92,6 +126,7 @@ services:
shlink_db_maria:
container_name: shlink_db_maria
user: 1000:1000
image: mariadb:10.7
ports:
- "3308:3306"
@ -105,7 +140,7 @@ services:
shlink_db_ms:
container_name: shlink_db_ms
image: mcr.microsoft.com/mssql/server:2019-latest
image: mcr.microsoft.com/mssql/server:2022-latest
ports:
- "1433:1433"
environment:
@ -114,13 +149,13 @@ services:
shlink_redis:
container_name: shlink_redis
image: redis:6.2-alpine
image: redis:7.4-alpine
ports:
- "6380:6379"
shlink_redis_acl:
container_name: shlink_redis_acl
image: redis:6.2-alpine
image: redis:7.4-alpine
command: ["redis-server", "/usr/local/etc/redis/redis.conf"]
ports:
- "6382:6379"
@ -140,14 +175,14 @@ services:
shlink_mercure:
container_name: shlink_mercure
image: dunglas/mercure:v0.15
image: dunglas/mercure:v0.18
ports:
- "3080:80"
environment:
SERVER_NAME: ":80"
MERCURE_PUBLISHER_JWT_KEY: mercure_jwt_key_long_enough_to_avoid_error
MERCURE_SUBSCRIBER_JWT_KEY: mercure_jwt_key_long_enough_to_avoid_error
MERCURE_EXTRA_DIRECTIVES: "cors_origins https://app.shlink.io http://localhost:3000 http://127.0.0.1:3000"
MERCURE_EXTRA_DIRECTIVES: "cors_origins https://app.shlink.io http://localhost:3000 http://127.0.0.1:3000 http://localhost:3002 http://127.0.0.1:3002 http://localhost:3005 http://127.0.0.1:3005"
shlink_rabbitmq:
container_name: shlink_rabbitmq
@ -169,7 +204,7 @@ services:
shlink_matomo:
container_name: shlink_matomo
image: matomo:4.15-apache
image: matomo:5.0-apache
ports:
- "8003:80"
volumes:

View File

@ -1,4 +1,3 @@
log_errors_max_len=0
zend.assertions=1
assert.exception=1
memory_limit=512M

View File

@ -1,18 +0,0 @@
<?php
declare(strict_types=1);
namespace Shlinkio\Shlink;
use Shlinkio\Shlink\Common\Logger\LoggerType;
return [
'logger' => [
'Shlink' => [
'type' => LoggerType::STREAM->value,
'destination' => 'php://stderr',
],
],
];

View File

@ -4,22 +4,28 @@ set -e
cd /etc/shlink
# Create data directories if they do not exist. This allows data dir to be mounted as an empty dir if needed
mkdir -p data/cache data/locks data/log data/proxies
mkdir -p data/cache data/locks data/log data/proxies data/temp-geolite
flags="--no-interaction --clear-db-cache"
# Read env vars through Shlink command, so that it applies the `_FILE` env var fallback logic
geolite_license_key=$(bin/cli env-var:read GEOLITE_LICENSE_KEY)
skip_initial_geolite_download=$(bin/cli env-var:read SKIP_INITIAL_GEOLITE_DOWNLOAD)
initial_api_key=$(bin/cli env-var:read INITIAL_API_KEY)
# Skip downloading GeoLite2 db file if the license key env var was not defined or skipping was explicitly set
if [ -z "${GEOLITE_LICENSE_KEY}" ] || [ "${SKIP_INITIAL_GEOLITE_DOWNLOAD}" = "true" ]; then
if [ -z "${geolite_license_key}" ] || [ "${skip_initial_geolite_download}" = "true" ]; then
flags="${flags} --skip-download-geolite"
fi
# If INITIAL_API_KEY was provided, create an initial API key
if [ -n "${INITIAL_API_KEY}" ]; then
flags="${flags} --initial-api-key=${INITIAL_API_KEY}"
if [ -n "${initial_api_key}" ]; then
flags="${flags} --initial-api-key=${initial_api_key}"
fi
php vendor/bin/shlink-installer init ${flags}
if [ "$SHLINK_RUNTIME" = 'rr' ]; then
./bin/rr serve -c config/roadrunner/.rr.yml
# Run with `exec` so that signals are properly handled
exec ./bin/rr serve -c config/roadrunner/.rr.yml
fi

View File

@ -0,0 +1,61 @@
# Handle dev and tests config via env vars instead of local config files
* Status: Accepted
* Date: 2024-10-24
## Context and problem statement
Due to the tools used by Shlink (Zend Expressive first and Mezzio later), configuration has always been handled via the config aggregator, which is a package that continues with Zend Framework 2 config management philosophy:
1. Define multiple config files, scoped to their own context, that are merge at runtime.
2. Overwrite with so-called "local" config files, which define values used only during development, and should not be shipped to production.
However, since Shlink started to support other runtimes and added an official docker image, env vars have started to become a central part of the config definition system.
That has evolved into a system where production config can be read from env vars, but dev config is expected to be defined via local config files, forcing to maintain two approaches to load config that need to coexist.
On top of that, keeping dev configs in multiple files makes it harder to keep track of everything.
Because of that, I'm proposing to switch to an env-var-based approach for dev custom configs, and get rid of local config files.
## Considered options
1. Define dev env vars in a single `.env` file which is loaded to containers via docker compose `env-file` option.
2. Define dev env vars in a single `.env` file which is loaded via RoadRunner config.
3. Define dev env vars in a single PHP file returning a map that's then loaded with `loadEnvVarsFromConfig`.
4. Keep local config files and don't change anything.
## Decision outcome
Defining env vars in a PHP file has the benefit that any change will take effect immediately, so the decision is to go with option 3.
## Pros and Cons of the Options
### 1 - .env file via docker compose
* Good: because it does not require any special mechanism to feed the env vars into the app.
* Good: because it's a standard format known by many.
* Bad: because dev config gets leaked to tests when run inside the container, breaking some existing ones, and forcing to remember this for future tests.
* Bad: because any change to the env file requires the containers to be manually restarted, or putting some new mechanism in place to restart them automatically.
### 2 - .env file via RoadRunner
* Good: because it does not require any special mechanism to feed the env vars into the app.
* Good: because it's a standard format known by many.
* Good: because dev config does not get leaked into tests.
* Bad: because any change to the env file requires the containers to be manually restarted, or putting some new mechanism in place to restart them automatically.
### 3 - PHP file via `loadEnvVarsFromConfig`
* Good: because the existing call to `loadEnvVarsFromConfig` can be reused by tweaking a bit the glob pattern, so no new dependencies are needed.
* Good: because dev config does not get leaked into tests, and test-specific env vars can be fed using the same mechanism.
* Good: because changes are picked up instantly by both RoadRunner and php-fpm.
* Good: because env vars can be imported from `EnvVars` class, removing the chances of human mistakes and typos.
* Bad: because people not familiar with the project may not expect env vars to be defined in that format.
### 4 - keep local config
* Good: because no changes are needed in the project.
* Bad: because managing multiple local config files makes things harder to maintain.
* Bad: because setting-up the project from scratch requires more steps, or an external package to handle config files.
* Bad: because the project needs to keep two ways to load dev configs, and reading an env var does not warranty you are getting the single source of truth.

View File

@ -2,7 +2,8 @@
Here listed you will find the different architectural decisions taken in the project, including all the reasoning behind it, options considered, and final outcome.
* [2023-07-09Build `latest` docker image only for actual releases](2023-07-09-build-latest-docker-image-only-for-actual-releases.md)
* [2024-10-24 Handle dev and tests config via env vars instead of local config files](2024-10-24-handle-dev-and-tests-config-via-env-vars-instead-of-local-config-files.md)
* [2023-07-09 Build `latest` docker image only for actual releases](2023-07-09-build-latest-docker-image-only-for-actual-releases.md)
* [2023-01-06 Support any HTTP method in short URLs](2023-01-06-support-any-http-method-in-short-urls.md)
* [2022-08-05 Support multi-segment custom slugs](2022-08-05-support-multi-segment-custom-slugs.md)
* [2022-01-15 Update env vars behavior to have precedence over installer options](2022-01-15-update-env-vars-behavior-to-have-precedence-over-installer-options.md)

View File

@ -141,6 +141,14 @@
"crawlable": {
"type": "boolean",
"description": "Tells if this URL will be included as 'Allow' in Shlink's robots.txt."
},
"forwardQuery": {
"type": "boolean",
"description": "Tells if this URL will forward the query params to the long URL when visited, as explained in [the docs](https://shlink.io/documentation/some-features/#query-params-forwarding)."
},
"hasRedirectRules": {
"type": "boolean",
"description": "Whether this short URL has redirect rules attached to it or not. Use [this endpoint](https://api-spec.shlink.io/#/Redirect%20rules/listShortUrlRedirectRules) to get the actual list of rules."
}
},
"example": {
@ -164,7 +172,9 @@
},
"domain": "example.com",
"title": "The title",
"crawlable": false
"crawlable": false,
"forwardQuery": false,
"hasRedirectRules": true
}
},
"ShortUrlMeta": {
@ -232,6 +242,16 @@
"potentialBot": {
"type": "boolean",
"description": "Tells if Shlink thinks this visit comes potentially from a bot or crawler"
},
"visitedUrl": {
"type": "string",
"nullable": true,
"description": "The originally visited URL that triggered the tracking of this visit"
},
"redirectUrl": {
"type": "string",
"nullable": true,
"description": "The URL to which the visitor was redirected, or null if a redirect did not occur, like for 404 requests or pixel tracking"
}
},
"example": {
@ -247,7 +267,8 @@
"regionName": "California",
"timezone": "America/Los_Angeles"
},
"potentialBot": false
"potentialBot": false,
"visitedUrl": "https://s.test"
}
},
"OrphanVisit": {
@ -256,11 +277,6 @@
{
"type": "object",
"properties": {
"visitedUrl": {
"type": "string",
"nullable": true,
"description": "The originally visited URL that triggered the tracking of this visit"
},
"type": {
"type": "string",
"enum": [

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,912 @@
# CHANGELOG 2.x
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com), and this project adheres to [Semantic Versioning](https://semver.org).
## [2.10.3] - 2022-01-23
### Added
* *Nothing*
### Changed
* *Nothing*
### Deprecated
* *Nothing*
### Removed
* *Nothing*
### Fixed
* [#1349](https://github.com/shlinkio/shlink/issues/1349) Fixed memory leak in cache implementation.
## [2.10.2] - 2022-01-07
### Added
* *Nothing*
### Changed
* *Nothing*
### Deprecated
* *Nothing*
### Removed
* *Nothing*
### Fixed
* [#1293](https://github.com/shlinkio/shlink/issues/1293) Fixed error when trying to create/import short URLs with a too long title.
* [#1306](https://github.com/shlinkio/shlink/issues/1306) Ensured remote IP address is not logged when using swoole/openswoole.
* [#1308](https://github.com/shlinkio/shlink/issues/1308) Fixed memory leak when using redis due to the amount of non-expiring keys created by doctrine. Now they have a 24h expiration by default.
## [2.10.1] - 2021-12-21
### Added
* *Nothing*
### Changed
* *Nothing*
### Deprecated
* *Nothing*
### Removed
* *Nothing*
### Fixed
* [#1285](https://github.com/shlinkio/shlink/issues/1285) Fixed error caused by database connections expiring after some hours of inactivity.
* [#1286](https://github.com/shlinkio/shlink/issues/1286) Fixed `x-request-id` header not being generated during non-rest requests.
## [2.10.0] - 2021-12-12
### Added
* [#1163](https://github.com/shlinkio/shlink/issues/1163) Allowed setting not-found redirects for default domain in the same way it's done for any other domain.
This implies a few non-breaking changes:
* The domains list no longer has the values of `INVALID_SHORT_URL_REDIRECT_TO`, `REGULAR_404_REDIRECT_TO` and `BASE_URL_REDIRECT_TO` on the default domain redirects.
* The `GET /domains` endpoint includes a new `defaultRedirects` property in the response, with the default redirects set via config or env vars.
* The `INVALID_SHORT_URL_REDIRECT_TO`, `REGULAR_404_REDIRECT_TO` and `BASE_URL_REDIRECT_TO` env vars are now deprecated, and should be replaced by `DEFAULT_INVALID_SHORT_URL_REDIRECT`, `DEFAULT_REGULAR_404_REDIRECT` and `DEFAULT_BASE_URL_REDIRECT` respectively. Deprecated ones will continue to work until v3.0.0, where they will be removed.
* [#868](https://github.com/shlinkio/shlink/issues/868) Added support to publish real-time updates in a RabbitMQ server.
Shlink will create new exchanges and queues for every topic documented in the [Async API spec](https://api-spec.shlink.io/async-api/), meaning, you will have one queue for orphan visits, one for regular visits, and one queue for every short URL with its visits.
The RabbitMQ server config can be provided via installer config options, or via environment variables.
* [#1204](https://github.com/shlinkio/shlink/issues/1204) Added support for `openswoole` and migrated official docker image to `openswoole`.
* [#1242](https://github.com/shlinkio/shlink/issues/1242) Added support to import urls and visits from YOURLS.
In order to do it, you need to first install this [dedicated plugin](https://slnk.to/yourls-import) in YOURLS, and then run the `short-url:import yourls` command, as with any other source.
* [#1235](https://github.com/shlinkio/shlink/issues/1235) Added support to disable rounding QR codes block sizing via config option, env var or query param.
* [#1188](https://github.com/shlinkio/shlink/issues/1188) Added support for PHP 8.1.
The official docker image has also been updated to use PHP 8.1 by default.
### Changed
* [#844](https://github.com/shlinkio/shlink/issues/844) Added mutation checks to API tests.
* [#1218](https://github.com/shlinkio/shlink/issues/1218) Updated to symfony/mercure 0.6.
* [#1223](https://github.com/shlinkio/shlink/issues/1223) Updated to phpstan 1.0.
* [#1258](https://github.com/shlinkio/shlink/issues/1258) Updated to Symfony 6 components, except symfony/console.
* Added `domain` field to `DeleteShortUrlException` exception.
### Deprecated
* [#1260](https://github.com/shlinkio/shlink/issues/1260) Deprecated `USE_HTTPS` env var that was added in previous release, in favor of the new `IS_HTTPS_ENABLED`.
The old one proved to be confusing and misleading, making people think it was used to actually enable HTTPS transparently, instead of its actual purpose, which is just telling Shlink it is being served with HTTPS.
### Removed
* *Nothing*
### Fixed
* [#1206](https://github.com/shlinkio/shlink/issues/1206) Fixed debugging of the docker image, so that it does not run the commands with `-q` when the `SHELL_VERBOSITY` env var has been provided.
* [#1254](https://github.com/shlinkio/shlink/issues/1254) Fixed examples in swagger docs.
## [2.9.3] - 2021-11-15
### Added
* *Nothing*
### Changed
* *Nothing*
### Deprecated
* *Nothing*
### Removed
* *Nothing*
### Fixed
* [#1232](https://github.com/shlinkio/shlink/issues/1232) Solved potential SQL injection by enforcing `doctrine/dbal` 3.1.4.
## [2.9.2] - 2021-10-23
### Added
* *Nothing*
### Changed
* *Nothing*
### Deprecated
* *Nothing*
### Removed
* *Nothing*
### Fixed
* [#1210](https://github.com/shlinkio/shlink/issues/1210) Fixed real time updates not being notified due to an incorrect handling of db transactions on multi-process tasks.
* [#1211](https://github.com/shlinkio/shlink/issues/1211) Fixed `There is no active transaction` error when running migrations in MySQL/Mariadb after updating to doctrine-migrations 3.3.
* [#1197](https://github.com/shlinkio/shlink/issues/1197) Fixed amount of task workers provided via config option or env var not being validated to ensure enough workers to process all parallel tasks.
## [2.9.1] - 2021-10-11
### Added
* *Nothing*
### Changed
* *Nothing*
### Deprecated
* *Nothing*
### Removed
* *Nothing*
### Fixed
* [#1201](https://github.com/shlinkio/shlink/issues/1201) Fixed crash when using the new `USE_HTTPS`, as it's boolean raw value was being used instead of resolving "https" or "http".
## [2.9.0] - 2021-10-10
### Added
* [#1015](https://github.com/shlinkio/shlink/issues/1015) Shlink now accepts configuration via env vars even when not using docker.
The config generated with the installing tool still has precedence over the env vars, so it cannot be combined. Either you use the tool, or use env vars.
* [#1149](https://github.com/shlinkio/shlink/issues/1149) Allowed to set custom defaults for the QR codes.
* [#1112](https://github.com/shlinkio/shlink/issues/1112) Added new option to define if the query string should be forwarded on a per-short URL basis.
The new `forwardQuery=true|false` param can be provided during short URL creation or edition, via REST API or CLI command, allowing to override the default behavior which makes the query string to always be forwarded.
* [#1105](https://github.com/shlinkio/shlink/issues/1105) Added support to define placeholders on not-found redirects, so that the redirected URL receives the originally visited path and/or domain.
Currently, `{DOMAIN}` and `{ORIGINAL_PATH}` placeholders are supported, and they can be used both in the redirected URL's path or query.
When they are used in the query, the values are URL encoded.
* [#1119](https://github.com/shlinkio/shlink/issues/1119) Added support to provide redis sentinel when using redis cache.
* [#1016](https://github.com/shlinkio/shlink/issues/1016) Added new option to send orphan visits to webhooks, via `NOTIFY_ORPHAN_VISITS_TO_WEBHOOKS` env var or installer tool.
The option is disabled by default, as the payload is backwards incompatible. You will need to adapt your webhooks to treat the `shortUrl` property as optional before enabling this option.
* [#1104](https://github.com/shlinkio/shlink/issues/1104) Added ability to disable tracking based on IP addresses.
IP addresses can be provided in the form of fixed addresses, CIDR blocks, or wildcard patterns (192.168.*.*).
### Changed
* [#1142](https://github.com/shlinkio/shlink/issues/1142) Replaced `doctrine/cache` package with `symfony/cache`.
* [#1157](https://github.com/shlinkio/shlink/issues/1157) All routes now support CORS, not only rest ones.
* [#1144](https://github.com/shlinkio/shlink/issues/1144) Added experimental builds under PHP 8.1.
### Deprecated
* [#1164](https://github.com/shlinkio/shlink/issues/1164) Deprecated `SHORT_DOMAIN_HOST` and `SHORT_DOMAIN_SCHEMA` env vars. Use `DEFAULT_DOMAIN` and `USE_HTTPS=true|false` instead.
### Removed
* *Nothing*
### Fixed
* [#1165](https://github.com/shlinkio/shlink/issues/1165) Fixed warning displayed when trying to locate visits and there are none pending.
* [#1172](https://github.com/shlinkio/shlink/pull/1172) Removed unneeded explicitly defined volumes in docker image.
## [2.8.1] - 2021-08-15
### Added
* *Nothing*
### Changed
* *Nothing*
### Deprecated
* *Nothing*
### Removed
* *Nothing*
### Fixed
* [#1155](https://github.com/shlinkio/shlink/issues/1155) Fixed numeric query params in long URLs being replaced by `0`.
## [2.8.0] - 2021-08-04
### Added
* [#1089](https://github.com/shlinkio/shlink/issues/1089) Added new `ENABLE_PERIODIC_VISIT_LOCATE` env var to docker image which schedules the `visit:locate` command every hour when provided with value `true`.
* [#1082](https://github.com/shlinkio/shlink/issues/1082) Added support for error correction level on QR codes.
Now, when calling the `GET /{shorCode}/qr-code` URL, you can pass the `errorCorrection` query param with values `L` for Low, `M` for Medium, `Q` for Quartile or `H` for High.
* [#1080](https://github.com/shlinkio/shlink/issues/1080) Added support to redirect to URLs as soon as the path starts with a valid short code, appending the rest of the path to the redirected long URL.
With this, if you have the `https://example.com/abc123` short URL redirecting to `https://www.twitter.com`, a visit to `https://example.com/abc123/shlinkio` will take you to `https://www.twitter.com/shlinkio`.
This behavior needs to be actively opted in, via installer config options or env vars.
* [#943](https://github.com/shlinkio/shlink/issues/943) Added support to define different "not-found" redirects for every domain handled by Shlink.
Shlink will continue to allow defining the default values via env vars or config, but afterwards, you can use the `domain:redirects` command or the `PATCH /domains/redirects` REST endpoint to define specific values for every single domain.
### Changed
* [#1118](https://github.com/shlinkio/shlink/issues/1118) Increased phpstan required level to 8.
* [#1127](https://github.com/shlinkio/shlink/issues/1127) Updated to infection 0.24.
* [#1139](https://github.com/shlinkio/shlink/issues/1139) Updated project dependencies, including base docker image to use PHP 8.0.9 and Alpine 3.14.
### Deprecated
* *Nothing*
### Removed
* [#1046](https://github.com/shlinkio/shlink/issues/1046) Dropped support for PHP 7.4.
### Fixed
* [#1098](https://github.com/shlinkio/shlink/issues/1098) Fixed errors when using Redis for caching, caused by some third party lib bug that was fixed on dependencies update.
## [2.7.3] - 2021-08-02
### Added
* *Nothing*
### Changed
* *Nothing*
### Deprecated
* *Nothing*
### Removed
* *Nothing*
### Fixed
* [#1135](https://github.com/shlinkio/shlink/issues/1135) Fixed error when importing short URLs with no visits from another Shlink instance.
* [#1136](https://github.com/shlinkio/shlink/issues/1136) Fixed error when fetching tag/short-url/orphan visits for a page lower than 1.
## [2.7.2] - 2021-07-30
### Added
* *Nothing*
### Changed
* *Nothing*
### Deprecated
* *Nothing*
### Removed
* *Nothing*
### Fixed
* [#1128](https://github.com/shlinkio/shlink/issues/1128) Increased memory limit reserved for the docker image, preventing it from crashing on GeoLite db download.
## [2.7.1] - 2021-05-30
### Added
* *Nothing*
### Changed
* *Nothing*
### Deprecated
* *Nothing*
### Removed
* *Nothing*
### Fixed
* [#1100](https://github.com/shlinkio/shlink/issues/1100) Fixed Shlink trying to download GeoLite2 db files even when tracking has been disabled.
## [2.7.0] - 2021-05-23
### Added
* [#1044](https://github.com/shlinkio/shlink/issues/1044) Added ability to set names on API keys, which helps to identify them when the list grows.
* [#819](https://github.com/shlinkio/shlink/issues/819) Visits are now always located in real time, even when not using swoole.
The only side effect is that a GeoLite2 db file is now installed when the docker image starts or during shlink installation or update.
Also, when using swoole, the file is now updated **after** tracking a visit, which means it will not apply until the next one.
* [#1059](https://github.com/shlinkio/shlink/issues/1059) Added ability to optionally display author API key and its name when listing short URLs from the command line.
* [#1066](https://github.com/shlinkio/shlink/issues/1066) Added support to import short URLs and their visits from another Shlink instance using its API.
* [#898](https://github.com/shlinkio/shlink/issues/898) Improved tracking granularity, allowing to disable visits tracking completely, or just parts of it.
In order to achieve it, Shlink now supports 4 new tracking-related options, that can be customized via env vars for docker, or via installer:
* `disable_tracking`: If true, visits will not be tracked at all.
* `disable_ip_tracking`: If true, visits will be tracked, but neither the IP address, nor the location will be resolved.
* `disable_referrer_tracking`: If true, the referrer will not be tracked.
* `disable_ua_tracking`: If true, the user agent will not be tracked.
* [#955](https://github.com/shlinkio/shlink/issues/955) Added new option to set short URLs as crawlable, making them be listed in the robots.txt as Allowed.
* [#900](https://github.com/shlinkio/shlink/issues/900) Shlink now tries to detect if the visit is coming from a potential bot or crawler, and allows to exclude those visits from visits lists if desired.
### Changed
* [#1036](https://github.com/shlinkio/shlink/issues/1036) Updated to `happyr/doctrine-specification` 2.0.
* [#1039](https://github.com/shlinkio/shlink/issues/1039) Updated to `endroid/qr-code` 4.0.
* [#1008](https://github.com/shlinkio/shlink/issues/1008) Ensured all logs are sent to the filesystem while running API tests, which helps debugging the reason for tests to fail.
### Deprecated
* *Nothing*
### Removed
* *Nothing*
### Fixed
* [#1041](https://github.com/shlinkio/shlink/issues/1041) Ensured the default value for the version while building the docker image is `latest`.
* [#1067](https://github.com/shlinkio/shlink/issues/1067) Fixed exception when persisting multiple short URLs in one batch which include the same new tags/domains. This can potentially happen when importing URLs.
## [2.6.2] - 2021-03-12
### Added
* *Nothing*
### Changed
* *Nothing*
### Deprecated
* *Nothing*
### Removed
* *Nothing*
### Fixed
* [#1047](https://github.com/shlinkio/shlink/issues/1047) Fixed error in migrations when doing a fresh installation using PHP8 and MySQL/Mariadb databases.
## [2.6.1] - 2021-02-22
### Added
* *Nothing*
### Changed
* [#1026](https://github.com/shlinkio/shlink/issues/1026) Removed non-inclusive terms from source code.
### Deprecated
* *Nothing*
### Removed
* *Nothing*
### Fixed
* [#1024](https://github.com/shlinkio/shlink/issues/1024) Fixed migration that is incorrectly skipped due to the wrong condition being used to check it.
* [#1031](https://github.com/shlinkio/shlink/issues/1031) Fixed shortening of twitter URLs with URL validation enabled.
* [#1034](https://github.com/shlinkio/shlink/issues/1034) Fixed warning displayed when shlink is stopped while running it with swoole.
## [2.6.0] - 2021-02-13
### Added
* [#856](https://github.com/shlinkio/shlink/issues/856) Added PHP 8.0 support.
* [#941](https://github.com/shlinkio/shlink/issues/941) Added support to provide a title for every short URL.
The title can also be automatically resolved from the long URL, when no title was explicitly provided, but this option needs to be opted in.
* [#913](https://github.com/shlinkio/shlink/issues/913) Added support to import short URLs from a standard CSV file.
The file requires the `Long URL` and `Short code` columns, and it also accepts the optional `title`, `domain` and `tags` columns.
* [#1000](https://github.com/shlinkio/shlink/issues/1000) Added support to provide a `margin` query param when generating some URL's QR code.
* [#675](https://github.com/shlinkio/shlink/issues/675) Added ability to track visits to the base URL, invalid short URLs or any other "not found" URL, as known as orphan visits.
This behavior is enabled by default, but you can opt out via env vars or config options.
This new orphan visits can be consumed in these ways:
* The `https://shlink.io/new-orphan-visit` mercure topic, which gets notified when an orphan visit occurs.
* The `GET /visits/orphan` REST endpoint, which behaves like the short URL visits and tags visits endpoints, but returns only orphan visits.
### Changed
* [#977](https://github.com/shlinkio/shlink/issues/977) Migrated from `laminas/laminas-paginator` to `pagerfanta/core` to handle pagination.
* [#986](https://github.com/shlinkio/shlink/issues/986) Updated official docker image to use PHP 8.
* [#1010](https://github.com/shlinkio/shlink/issues/1010) Increased timeout for database commands to 10 minutes.
* [#874](https://github.com/shlinkio/shlink/issues/874) Changed how dist files are generated. Now there will be two for every supported PHP version, with and without support for swoole.
The dist files will have been built under the same PHP version they are meant to be run under, ensuring resolved dependencies are the proper ones.
### Deprecated
* [#959](https://github.com/shlinkio/shlink/issues/959) Deprecated all command flags using camelCase format (like `--expirationDate`), adding kebab-case replacements for all of them (like `--expiration-date`).
All the existing camelCase flags will continue working for now, but will be removed in Shlink 3.0.0
* [#862](https://github.com/shlinkio/shlink/issues/862) Deprecated the endpoint to edit tags for a short URL (`PUT /short-urls/{shortCode}/tags`).
The short URL edition endpoint (`PATCH /short-urls/{shortCode}`) now supports setting the tags too. Use it instead.
### Removed
* *Nothing*
### Fixed
* [#988](https://github.com/shlinkio/shlink/issues/988) Fixed serving zero-byte static files in apache and apache-compatible web servers.
* [#990](https://github.com/shlinkio/shlink/issues/990) Fixed short URLs not properly composed in REST API endpoints when both custom domain and custom base path are used.
* [#1002](https://github.com/shlinkio/shlink/issues/1002) Fixed weird behavior in which GeoLite2 metadata's `buildEpoch` is parsed as string instead of int.
* [#851](https://github.com/shlinkio/shlink/issues/851) Fixed error when trying to schedule swoole tasks in ARM architectures (like raspberry).
## [2.5.2] - 2021-01-24
### Added
* [#965](https://github.com/shlinkio/shlink/issues/965) Added docs section for Architectural Decision Records, including the one for API key roles.
### Changed
* *Nothing*
### Deprecated
* *Nothing*
### Removed
* *Nothing*
### Fixed
* [#979](https://github.com/shlinkio/shlink/issues/979) Added missing `itemsPerPage` query param to swagger docs for short URLs list.
* [#980](https://github.com/shlinkio/shlink/issues/980) Fixed value used for `Access-Control-Allow-Origin`, that could not work as expected when including an IP address.
* [#947](https://github.com/shlinkio/shlink/issues/947) Fixed incorrect value returned in `Access-Control-Allow-Methods` header, which always contained all methods.
## [2.5.1] - 2021-01-21
### Added
* *Nothing*
### Changed
* *Nothing*
### Deprecated
* *Nothing*
### Removed
* *Nothing*
### Fixed
* [#968](https://github.com/shlinkio/shlink/issues/968) Fixed index error in MariaDB while updating to v2.5.0.
* [#972](https://github.com/shlinkio/shlink/issues/972) Fixed 500 error when calling single-step short URL creation endpoint.
## [2.5.0] - 2021-01-17
### Added
* [#795](https://github.com/shlinkio/shlink/issues/795) and [#882](https://github.com/shlinkio/shlink/issues/882) Added new roles system to API keys.
API keys can have any combinations of these two roles now, allowing to limit their interactions:
* Can interact only with short URLs created with that API key.
* Can interact only with short URLs for a specific domain.
* [#833](https://github.com/shlinkio/shlink/issues/833) Added support to connect through unix socket when using an external MySQL, MariaDB or Postgres database.
It can be provided during the installation, or as the `DB_UNIX_SOCKET` env var for the docker image.
* [#869](https://github.com/shlinkio/shlink/issues/869) Added support for Mercure Hub 0.10.
* [#896](https://github.com/shlinkio/shlink/issues/896) Added support for unicode characters in custom slugs.
* [#930](https://github.com/shlinkio/shlink/issues/930) Added new `bin/set-option` script that allows changing individual configuration options on existing shlink instances.
* [#877](https://github.com/shlinkio/shlink/issues/877) Improved API tests on CORS, and "refined" middleware handling it.
### Changed
* [#912](https://github.com/shlinkio/shlink/issues/912) Changed error templates to be plain html files, removing the dependency on `league/plates` package.
* [#875](https://github.com/shlinkio/shlink/issues/875) Updated to `mezzio/mezzio-swoole` v3.1.
* [#952](https://github.com/shlinkio/shlink/issues/952) Simplified in-project docs, by keeping only the basics and linking to the websites docs for anything else.
### Deprecated
* [#917](https://github.com/shlinkio/shlink/issues/917) Deprecated `/{shortCode}/qr-code/{size}` URL, in favor of providing the size in the query instead, `/{shortCode}/qr-code?size={size}`.
* [#924](https://github.com/shlinkio/shlink/issues/924) Deprecated mechanism to provide config options to the docker image through volumes. Use the env vars instead as a direct replacement.
### Removed
* *Nothing*
### Fixed
* *Nothing*
## [2.4.2] - 2020-11-22
### Added
* *Nothing*
### Changed
* *Nothing*
### Deprecated
* *Nothing*
### Removed
* *Nothing*
### Fixed
* [#904](https://github.com/shlinkio/shlink/issues/904) Explicitly added missing "Domains" and "Integrations" tags to swagger docs.
* [#901](https://github.com/shlinkio/shlink/issues/901) Ensured domains which are not in use on any short URL are not returned on the list of domains.
* [#899](https://github.com/shlinkio/shlink/issues/899) Avoided filesystem errors produced while downloading geolite DB files on several shlink instances that share the same filesystem.
* [#827](https://github.com/shlinkio/shlink/issues/827) Fixed swoole config getting loaded in config cache if a console command is run before any web execution, when swoole extension is enabled, making subsequent non-swoole web requests fail.
## [2.4.1] - 2020-11-10
### Added
* *Nothing*
### Changed
* *Nothing*
### Deprecated
* *Nothing*
### Removed
* *Nothing*
### Fixed
* [#891](https://github.com/shlinkio/shlink/issues/891) Fixed error when running migrations in postgres due to incorrect return type hint.
* [#846](https://github.com/shlinkio/shlink/issues/846) Fixed base image used for the PHP-FPM dev container.
* [#867](https://github.com/shlinkio/shlink/issues/867) Fixed not-found redirects not using proper status (301 or 302) as configured during installation.
## [2.4.0] - 2020-11-08
### Added
* [#829](https://github.com/shlinkio/shlink/issues/829) Added support for QR codes in SVG format, by passing `?format=svg` to the QR code URL.
* [#820](https://github.com/shlinkio/shlink/issues/820) Added new option to force enabling or disabling URL validation on a per-URL basis.
Currently, there's a global config that tells if long URLs should be validated (by ensuring they are publicly accessible and return a 2xx status). However, this is either always applied or never applied.
Now, it is possible to enforce validation or enforce disabling validation when a new short URL is created or edited:
* On the `POST /short-url` and `PATCH /short-url/{shortCode}` endpoints, you can now pass `validateUrl: true/false` in order to enforce enabling or disabling validation, ignoring the global config. If the value is not provided, the global config is still normally applied.
* On the `short-url:generate` CLI command, you can pass `--validate-url` or `--no-validate-url` flags, in order to enforce enabling or disabling validation. If none of them is provided, the global config is still normally applied.
* [#838](https://github.com/shlinkio/shlink/issues/838) Added new endpoint and CLI command to list existing domains.
It returns both default domain and specific domains that were used for some short URLs.
* REST endpoint: `GET /rest/v2/domains`
* CLI Command: `domain:list`
* [#832](https://github.com/shlinkio/shlink/issues/832) Added support to customize the port in which the docker image listens by using the `PORT` env var or the `port` config option.
* [#860](https://github.com/shlinkio/shlink/issues/860) Added support to import links from bit.ly.
Run the command `short-urls:import bitly` and introduce requested information in order to import all your links.
Other sources will be supported in future releases.
### Changed
* [#836](https://github.com/shlinkio/shlink/issues/836) Added support for the `<field>-<dir>` notation while determining how to order the short URLs list, as in `?orderBy=shortCode-DESC`. This effectively deprecates the array notation (`?orderBy[shortCode]=DESC`), that will be removed in Shlink 3.0.0
* [#782](https://github.com/shlinkio/shlink/issues/782) Added code coverage to API tests.
* [#858](https://github.com/shlinkio/shlink/issues/858) Updated to latest infection version. Updated docker images to PHP 7.4.11 and swoole 4.5.5
* [#887](https://github.com/shlinkio/shlink/pull/887) Started tracking the API key used to create short URLs, in order to allow restrictions in future releases.
### Deprecated
* [#883](https://github.com/shlinkio/shlink/issues/883) Deprecated `POST /tags` endpoint and `tag:create` command, as tags are created automatically while creating short URLs.
### Removed
* *Nothing*
### Fixed
* [#837](https://github.com/shlinkio/shlink/issues/837) Drastically improved performance when creating a new shortUrl and providing `findIfExists = true`.
* [#878](https://github.com/shlinkio/shlink/issues/878) Added missing `gmp` extension to the official docker image.
## [2.3.0] - 2020-08-09
### Added
* [#746](https://github.com/shlinkio/shlink/issues/746) Allowed to configure the kind of redirect you want to use for your short URLs. You can either set:
* `302` redirects: Default behavior. Visitors always hit the server.
* `301` redirects: Better for SEO. Visitors hit the server the first time and then cache the redirect.
When selecting 301 redirects, you can also configure the time redirects are cached, to mitigate deviations in stats.
* [#734](https://github.com/shlinkio/shlink/issues/734) Added support to redirect to deeplinks and other links with schemas different from `http` and `https`.
* [#709](https://github.com/shlinkio/shlink/issues/709) Added multi-architecture builds for the docker image.
* [#707](https://github.com/shlinkio/shlink/issues/707) Added `--all` flag to `short-urls:list` command, which will print all existing URLs in one go, with no pagination.
It has one limitation, though. Because of the way the CLI tooling works, all rows in the table must be loaded in memory. If the amount of URLs is too high, the command may fail due to too much memory usage.
### Changed
* [#508](https://github.com/shlinkio/shlink/issues/508) Added mutation checks to database tests.
* [#790](https://github.com/shlinkio/shlink/issues/790) Updated to doctrine/migrations v3.
* [#798](https://github.com/shlinkio/shlink/issues/798) Updated to guzzlehttp/guzzle v7.
* [#822](https://github.com/shlinkio/shlink/issues/822) Updated docker image to use PHP 7.4.9 with Alpine 3.12 and swoole 4.5.2.
### Deprecated
* *Nothing*
### Removed
* *Nothing*
### Fixed
* *Nothing*
## [2.2.2] - 2020-06-08
### Added
* *Nothing*
### Changed
* *Nothing*
### Deprecated
* *Nothing*
### Removed
* *Nothing*
### Fixed
* [#769](https://github.com/shlinkio/shlink/issues/769) Fixed custom slugs not allowing valid URL characters, like `.`, `_` or `~`.
* [#781](https://github.com/shlinkio/shlink/issues/781) Fixed memory leak when loading visits for a tag which is used for big amounts of short URLs.
## [2.2.1] - 2020-05-11
### Added
* *Nothing*
### Changed
* *Nothing*
### Deprecated
* *Nothing*
### Removed
* *Nothing*
### Fixed
* [#764](https://github.com/shlinkio/shlink/issues/764) Fixed error when trying to match an existing short URL which does not have `validSince` and/or `validUntil`, but you are providing either one of them for the new one.
## [2.2.0] - 2020-05-09
### Added
* [#712](https://github.com/shlinkio/shlink/issues/712) Added support to integrate Shlink with a [mercure hub](https://mercure.rocks/) server.
Thanks to that, Shlink will be able to publish events that can be consumed in real time.
For now, two topics (events) are published, when new visits occur. Both include a payload with the visit and the shortUrl:
* A visit occurs on any short URL: `https://shlink.io/new-visit`.
* A visit occurs on short URLs with a specific short code: `https://shlink.io/new-visit/{shortCode}`.
The updates are only published when serving Shlink with swoole.
Also, Shlink exposes a new endpoint `GET /rest/v2/mercure-info`, which returns the public URL of the mercure hub, and a valid JWT that can be used to subscribe to updates.
* [#673](https://github.com/shlinkio/shlink/issues/673) Added new `[GET /visits]` rest endpoint which returns basic visits stats.
* [#674](https://github.com/shlinkio/shlink/issues/674) Added new `[GET /tags/{tag}/visits]` rest endpoint which returns visits by tag.
It works in the same way as the `[GET /short-urls/{shortCode}/visits]` one, returning the same response payload, and supporting the same query params, but the response is the list of visits in all short URLs which have provided tag.
* [#672](https://github.com/shlinkio/shlink/issues/672) Enhanced `[GET /tags]` rest endpoint so that it is possible to get basic stats info for every tag.
Now, if the `withStats=true` query param is provided, the response payload will include a new `stats` property which is a list with the amount of short URLs and visits for every tag.
Also, the `tag:list` CLI command has been changed and it always behaves like this.
* [#640](https://github.com/shlinkio/shlink/issues/640) Allowed to optionally disable visitors' IP address anonymization. This will make Shlink no longer be GDPR-compliant, but it's OK if you only plan to share your URLs in countries without this regulation.
### Changed
* [#692](https://github.com/shlinkio/shlink/issues/692) Drastically improved performance when loading visits. Specially noticeable when loading big result sets.
* [#657](https://github.com/shlinkio/shlink/issues/657) Updated how DB tests are run in travis by using docker containers which allow all engines to be covered.
* [#751](https://github.com/shlinkio/shlink/issues/751) Updated PHP and swoole versions used in docker image, and removed mssql-tools, as they are not needed.
### Deprecated
* *Nothing*
### Removed
* *Nothing*
### Fixed
* [#729](https://github.com/shlinkio/shlink/issues/729) Fixed weird error when fetching multiple visits result sets concurrently using mariadb or mysql.
* [#735](https://github.com/shlinkio/shlink/issues/735) Fixed error when cleaning metadata cache during installation when APCu is enabled.
* [#677](https://github.com/shlinkio/shlink/issues/677) Fixed `/health` endpoint returning `503` fail responses when the database connection has expired.
* [#732](https://github.com/shlinkio/shlink/issues/732) Fixed wrong client IP in access logs when serving app with swoole behind load balancer.
## [2.1.4] - 2020-04-30
### Added
* *Nothing*
### Changed
* *Nothing*
### Deprecated
* *Nothing*
### Removed
* *Nothing*
### Fixed
* [#742](https://github.com/shlinkio/shlink/issues/742) Allowed a custom GeoLite2 license key to be provided, in order to avoid download limits.
## [2.1.3] - 2020-04-09
### Added
* *Nothing*
### Changed
* *Nothing*
### Deprecated
* *Nothing*
### Removed
* *Nothing*
### Fixed
* [#712](https://github.com/shlinkio/shlink/issues/712) Fixed app set-up not clearing entities metadata cache.
* [#711](https://github.com/shlinkio/shlink/issues/711) Fixed `HEAD` requests returning a duplicated `Content-Length` header.
* [#716](https://github.com/shlinkio/shlink/issues/716) Fixed Twitter not properly displaying preview for final long URL.
* [#717](https://github.com/shlinkio/shlink/issues/717) Fixed DB connection expiring on task workers when using swoole.
* [#705](https://github.com/shlinkio/shlink/issues/705) Fixed how the short URL domain is inferred when generating QR codes, making sure the configured domain is respected even if the request is performed using a different one, and only when a custom domain is used, then that one is used instead.
## [2.1.2] - 2020-03-29
### Added
* *Nothing*
### Changed
* [#696](https://github.com/shlinkio/shlink/issues/696) Updated to infection v0.16.
### Deprecated
* *Nothing*
### Removed
* *Nothing*
### Fixed
* [#700](https://github.com/shlinkio/shlink/issues/700) Fixed migration not working with postgres.
* [#690](https://github.com/shlinkio/shlink/issues/690) Fixed tags being incorrectly sluggified when filtering short URL lists, making results not be the expected.
## [2.1.1] - 2020-03-28
### Added
* *Nothing*
### Changed
* *Nothing*
### Deprecated
* *Nothing*
### Removed
* *Nothing*
### Fixed
* [#697](https://github.com/shlinkio/shlink/issues/697) Recovered `.htaccess` file that was unintentionally removed in v2.1.0, making Shlink unusable with Apache.
## [2.1.0] - 2020-03-28
### Added
* [#626](https://github.com/shlinkio/shlink/issues/626) Added support for Microsoft SQL Server.
* [#556](https://github.com/shlinkio/shlink/issues/556) Short code lengths can now be customized, both globally and on a per-short URL basis.
* [#541](https://github.com/shlinkio/shlink/issues/541) Added a request ID that is returned on `X-Request-Id` header, can be provided from outside and is set in log entries.
* [#642](https://github.com/shlinkio/shlink/issues/642) IP geolocation is now performed over the non-anonymized IP address when using swoole.
* [#521](https://github.com/shlinkio/shlink/issues/521) The long URL for any existing short URL can now be edited using the `PATCH /short-urls/{shortCode}` endpoint.
### Changed
* [#656](https://github.com/shlinkio/shlink/issues/656) Updated to PHPUnit 9.
* [#641](https://github.com/shlinkio/shlink/issues/641) Added two new flags to the `visit:locate` command, `--retry` and `--all`.
* When `--retry` is provided, it will try to re-locate visits which IP address was originally considered not found, in case it was a temporal issue.
* When `--all` is provided together with `--retry`, it will try to re-locate all existing visits. A warning and confirmation are displayed, as this can have side effects.
### Deprecated
* *Nothing*
### Removed
* *Nothing*
### Fixed
* [#665](https://github.com/shlinkio/shlink/issues/665) Fixed `base_url_redirect_to` simplified config option not being properly parsed.
* [#663](https://github.com/shlinkio/shlink/issues/663) Fixed Shlink allowing short URLs to be created with an empty custom slug.
* [#678](https://github.com/shlinkio/shlink/issues/678) Fixed `db` commands not running in a non-interactive way.
## [2.0.5] - 2020-02-09
### Added
* [#651](https://github.com/shlinkio/shlink/issues/651) Documented how Shlink behaves when using multiple domains.
### Changed
* *Nothing*
### Deprecated
* *Nothing*
### Removed
* *Nothing*
### Fixed
* [#648](https://github.com/shlinkio/shlink/issues/648) Ensured any user can write in log files, in case shlink is run by several system users.
* [#650](https://github.com/shlinkio/shlink/issues/650) Ensured default domain is ignored when trying to create a short URL.
## [2.0.4] - 2020-02-02
### Added
* *Nothing*
### Changed
* [#577](https://github.com/shlinkio/shlink/issues/577) Wrapped params used to customize short URL lists into a DTO with implicit validation.
### Deprecated
* *Nothing*
### Removed
* *Nothing*
### Fixed
* [#620](https://github.com/shlinkio/shlink/issues/620) Ensured "controlled" errors (like validation errors and such) won't be logged with error level, preventing logs to be polluted.
* [#637](https://github.com/shlinkio/shlink/issues/637) Fixed several work flows in which short URLs with domain are handled form the API.
* [#644](https://github.com/shlinkio/shlink/issues/644) Fixed visits to short URL on non-default domain being linked to the URL on default domain with the same short code.
* [#643](https://github.com/shlinkio/shlink/issues/643) Fixed searching on short URL lists not taking into consideration the domain name.
## [2.0.3] - 2020-01-27
### Added
* *Nothing*
### Changed
* *Nothing*
### Deprecated
* *Nothing*
### Removed
* *Nothing*
### Fixed
* [#624](https://github.com/shlinkio/shlink/issues/624) Fixed order in which headers for remote IP detection are inspected.
* [#623](https://github.com/shlinkio/shlink/issues/623) Fixed short URLs metadata being impossible to reset.
* [#628](https://github.com/shlinkio/shlink/issues/628) Fixed `GET /short-urls/{shortCode}` REST endpoint returning a 404 for short URLs which are not enabled.
* [#621](https://github.com/shlinkio/shlink/issues/621) Fixed permission denied error when updating same GeoLite file version more than once.
## [2.0.2] - 2020-01-12
### Added
* *Nothing*
### Changed
* *Nothing*
### Deprecated
* *Nothing*
### Removed
* *Nothing*
### Fixed
* [#614](https://github.com/shlinkio/shlink/issues/614) Fixed `OPTIONS` requests including the `Origin` header not always returning an empty body with status 2xx.
* [#615](https://github.com/shlinkio/shlink/issues/615) Fixed query args with no value being lost from the long URL when users are redirected.
## [2.0.1] - 2020-01-10
### Added
* *Nothing*
### Changed
* *Nothing*
### Deprecated
* *Nothing*
### Removed
* *Nothing*
### Fixed
* [#607](https://github.com/shlinkio/shlink/issues/607) Added missing info on UPGRADE.md doc.
* [#610](https://github.com/shlinkio/shlink/issues/610) Fixed use of hardcoded quotes on a database migration which makes it fail on postgres.
* [#605](https://github.com/shlinkio/shlink/issues/605) Fixed crashes occurring when migrating from old Shlink versions with nullable DB columns that are assigned to non-nullable entity typed props.
## [2.0.0] - 2020-01-08
### Added
* [#429](https://github.com/shlinkio/shlink/issues/429) Added support for PHP 7.4
* [#529](https://github.com/shlinkio/shlink/issues/529) Created an UPGRADING.md file explaining how to upgrade from v1.x to v2.x
* [#594](https://github.com/shlinkio/shlink/issues/594) Updated external shlink packages, including installer v4.0, which adds the option to ask for the redis cluster config.
### Changed
* [#592](https://github.com/shlinkio/shlink/issues/592) Updated coding styles to use [shlinkio/php-coding-standard](https://github.com/shlinkio/php-coding-standard) v2.1.0.
* [#530](https://github.com/shlinkio/shlink/issues/530) Migrated project from deprecated `zendframework` components to the new `laminas` and `mezzio` ones.
### Deprecated
* *Nothing*
### Removed
* [#429](https://github.com/shlinkio/shlink/issues/429) Dropped support for PHP 7.2 and 7.3
* [#229](https://github.com/shlinkio/shlink/issues/229) Remove everything which was deprecated, including:
* Preview generation feature completely removed.
* Authentication against REST API using JWT is no longer supported.
See [UPGRADE](UPGRADE.md#from-v1x-to-v2x) doc in order to get details on how to migrate to this version.
### Fixed
* [#600](https://github.com/shlinkio/shlink/issues/600) Fixed health action so that it works with and without version in the path.

View File

@ -1,14 +1,10 @@
{
"type": "object",
"required": ["visitedUrl", "type"],
"required": ["type"],
"allOf": [{
"$ref": "./Visit.json"
}],
"properties": {
"visitedUrl": {
"type": ["string", "null"],
"description": "The originally visited URL that triggered the tracking of this visit"
},
"type": {
"type": "string",
"enum": [

View File

@ -15,14 +15,23 @@
"properties": {
"type": {
"type": "string",
"enum": ["device", "language", "query-param"],
"description": "The type of the condition, which will condition the logic used to match it"
"enum": [
"device",
"language",
"query-param",
"any-value-query-param",
"valueless-query-param",
"ip-address",
"geolocation-country-code",
"geolocation-city-name"
],
"description": "The type of the condition, which will determine the logic used to match it"
},
"matchKey": {
"type": ["string", "null"]
},
"matchValue": {
"type": "string"
"type": ["string", "null"]
}
}
}

View File

@ -11,7 +11,8 @@
"domain",
"title",
"crawlable",
"forwardQuery"
"forwardQuery",
"hasRedirectRules"
],
"properties": {
"shortCode": {
@ -59,6 +60,10 @@
"forwardQuery": {
"type": "boolean",
"description": "Tells if this URL will forward the query params to the long URL when visited, as explained in [the docs](https://shlink.io/documentation/some-features/#query-params-forwarding)."
},
"hasRedirectRules": {
"type": "boolean",
"description": "Whether this short URL has redirect rules attached to it or not. Use [this endpoint](https://api-spec.shlink.io/#/Redirect%20rules/listShortUrlRedirectRules) to get the actual list of rules."
}
}
}

View File

@ -1,6 +1,6 @@
{
"type": "object",
"required": ["referer", "date", "userAgent", "visitLocation"],
"required": ["referer", "date", "userAgent", "visitLocation", "potentialBot", "visitedUrl"],
"properties": {
"referer": {
"type": "string",
@ -21,6 +21,14 @@
"potentialBot": {
"type": "boolean",
"description": "Tells if Shlink thinks this visit comes potentially from a bot or crawler"
},
"visitedUrl": {
"type": ["string", "null"],
"description": "The originally visited URL that triggered the tracking of this visit"
},
"redirectUrl": {
"type": ["string", "null"],
"description": "The URL to which the visitor was redirected, or null if a redirect did not occur, like for 404 requests or pixel tracking"
}
}
}

View File

@ -31,7 +31,7 @@
{
"name": "searchTerm",
"in": "query",
"description": "A query used to filter results by searching for it on the longUrl and shortCode fields. (Since v1.3.0)",
"description": "A query used to filter results by searching for it on the longUrl and shortCode fields.",
"required": false,
"schema": {
"type": "string"
@ -40,7 +40,7 @@
{
"name": "tags[]",
"in": "query",
"description": "A list of tags used to filter the result set. Only short URLs tagged with at least one of the provided tags will be returned. (Since v1.3.0)",
"description": "A list of tags used to filter the result set. Only short URLs **with** these tags will be returned.",
"required": false,
"schema": {
"type": "array",
@ -52,7 +52,29 @@
{
"name": "tagsMode",
"in": "query",
"description": "Tells how the filtering by tags should work, returning short URLs containing \"any\" of the tags, or \"all\" the tags. It's ignored if no tags are provided, and defaults to \"any\" if not provided.",
"description": "Tells how the filtering by `tags` should work, returning short URLs containing \"any\" of the tags, or \"all\" the tags. Defaults to \"any\".<br />It's ignored if `tags` is not provided.",
"required": false,
"schema": {
"type": "string",
"enum": ["any", "all"]
}
},
{
"name": "excludeTags[]",
"in": "query",
"description": "A list of tags used to filter the result set. Only short URLs **without** these tags will be returned.",
"required": false,
"schema": {
"type": "array",
"items": {
"type": "string"
}
}
},
{
"name": "excludeTagsMode",
"in": "query",
"description": "Tells how the filtering by `excludeTags` should work, returning short URLs not containing \"any\" of the tags, or not containing \"all\" the tags. Defaults to \"any\".<br />It's ignored if `excludeTags` is not provided.",
"required": false,
"schema": {
"type": "string",
@ -125,6 +147,24 @@
"false"
]
}
},
{
"name": "domain",
"in": "query",
"description": "Get short URLs for this particular domain only. Use **DEFAULT** keyword for default domain.",
"required": false,
"schema": {
"type": "string"
}
},
{
"name": "apiKeyName",
"in": "query",
"description": "Only get short URLs created with this API key.<br />This value is **ignored** if the request is performed with a non-admin API key that does not match this name.",
"required": false,
"schema": {
"type": "string"
}
}
],
"security": [
@ -180,7 +220,9 @@
},
"domain": null,
"title": "Welcome to Steam",
"crawlable": false
"crawlable": false,
"forwardQuery": true,
"hasRedirectRules": true
},
{
"shortCode": "12Kb3",
@ -202,7 +244,9 @@
},
"domain": null,
"title": null,
"crawlable": false
"crawlable": false,
"forwardQuery": true,
"hasRedirectRules": false
},
{
"shortCode": "123bA",
@ -222,7 +266,9 @@
},
"domain": "example.com",
"title": null,
"crawlable": false
"crawlable": false,
"forwardQuery": false,
"hasRedirectRules": true
}
],
"pagination": {
@ -337,7 +383,9 @@
},
"domain": null,
"title": null,
"crawlable": false
"crawlable": false,
"forwardQuery": true,
"hasRedirectRules": false
}
}
}

View File

@ -72,7 +72,9 @@
},
"domain": null,
"title": null,
"crawlable": false
"crawlable": false,
"forwardQuery": true,
"hasRedirectRules": false
}
},
"text/plain": {

View File

@ -50,7 +50,9 @@
},
"domain": null,
"title": null,
"crawlable": false
"crawlable": false,
"forwardQuery": true,
"hasRedirectRules": true
}
}
}
@ -163,7 +165,9 @@
},
"domain": null,
"title": "Shlink - The URL shortener",
"crawlable": false
"crawlable": false,
"forwardQuery": false,
"hasRedirectRules": true
}
}
}

View File

@ -100,7 +100,8 @@
"date": "2015-08-20T05:05:03+04:00",
"userAgent": "Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:47.0) Gecko/20100101 Firefox/47.0 Mozilla/5.0 (Macintosh; Intel Mac OS X x.y; rv:42.0) Gecko/20100101 Firefox/42.0",
"visitLocation": null,
"potentialBot": false
"potentialBot": false,
"visitedUrl": "https://s.test"
},
{
"referer": "https://t.co",
@ -115,14 +116,16 @@
"regionName": "California",
"timezone": "America/Los_Angeles"
},
"potentialBot": false
"potentialBot": false,
"visitedUrl": "https://s.test"
},
{
"referer": null,
"date": "2015-08-20T05:05:03+04:00",
"userAgent": "some_web_crawler/1.4",
"visitLocation": null,
"potentialBot": true
"potentialBot": true,
"visitedUrl": "https://s.test"
}
],
"pagination": {

View File

@ -103,7 +103,8 @@
"date": "2015-08-20T05:05:03+04:00",
"userAgent": "Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:47.0) Gecko/20100101 Firefox/47.0 Mozilla/5.0 (Macintosh; Intel Mac OS X x.y; rv:42.0) Gecko/20100101 Firefox/42.0",
"visitLocation": null,
"potentialBot": false
"potentialBot": false,
"visitedUrl": "https://s.test"
},
{
"referer": "https://t.co",
@ -118,14 +119,16 @@
"regionName": "California",
"timezone": "America/Los_Angeles"
},
"potentialBot": false
"potentialBot": false,
"visitedUrl": "https://s.test"
},
{
"referer": null,
"date": "2015-08-20T05:05:03+04:00",
"userAgent": "some_web_crawler/1.4",
"visitLocation": null,
"potentialBot": true
"potentialBot": true,
"visitedUrl": "https://s.test"
}
],
"pagination": {

View File

@ -64,6 +64,10 @@
"type": "string",
"enum": ["true"]
}
},
{
"$ref": "../parameters/domain.json",
"description": "Return visits for short URLs that belong to this domain. Use **DEFAULT** keyword to return visits from default domain."
}
],
"security": [
@ -103,7 +107,8 @@
"date": "2015-08-20T05:05:03+04:00",
"userAgent": "Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:47.0) Gecko/20100101 Firefox/47.0 Mozilla/5.0 (Macintosh; Intel Mac OS X x.y; rv:42.0) Gecko/20100101 Firefox/42.0",
"visitLocation": null,
"potentialBot": false
"potentialBot": false,
"visitedUrl": "https://s.test"
},
{
"referer": "https://t.co",
@ -118,14 +123,16 @@
"regionName": "California",
"timezone": "America/Los_Angeles"
},
"potentialBot": false
"potentialBot": false,
"visitedUrl": "https://s.test"
},
{
"referer": null,
"date": "2015-08-20T05:05:03+04:00",
"userAgent": "some_web_crawler/1.4",
"visitLocation": null,
"potentialBot": true
"potentialBot": true,
"visitedUrl": "https://s.test"
}
],
"pagination": {

Some files were not shown because too many files have changed in this diff Show More