Compare commits

...

683 Commits

Author SHA1 Message Date
Alejandro Celaya
29bb201581 Merge pull request #889 from shlinkio/develop
Release v2.4.0
2020-11-08 12:27:50 +01:00
Alejandro Celaya
006ec7c1d0 Added v2.4 to changelog 2020-11-08 12:14:41 +01:00
Alejandro Celaya
1bc9e0643d Merge pull request #888 from acelaya-forks/feature/simplify-auth-checks
Deleted everything related with authentication plugins, as shlink onl…
2020-11-07 13:02:51 +01:00
Alejandro Celaya
d6395a3de8 Deleted everything related with authentication plugins, as shlink only supports API key auth since v2.0.0 2020-11-07 12:53:14 +01:00
Alejandro Celaya
098751d256 Fixed link in changelog 2020-11-07 10:54:21 +01:00
Alejandro Celaya
8577d6bd99 Merge pull request #887 from acelaya-forks/feature/track-url-creator
Feature/track url creator
2020-11-07 10:40:43 +01:00
Alejandro Celaya
fe4e171ecb Removed unused mock 2020-11-07 10:30:25 +01:00
Alejandro Celaya
d99ea82761 Added migrations folder to the static analysis 2020-11-07 10:27:35 +01:00
Alejandro Celaya
27bc8d4823 Ensured API key is tracked when creating short URLs from the REST API 2020-11-07 10:23:08 +01:00
Alejandro Celaya
7c9f572eb1 Deleted old domain resolvers and added tests for new short url relation resolvers 2020-11-07 09:49:09 +01:00
Alejandro Celaya
2732b05834 Added mechanisms to be able to provide the API key when creating a short URL 2020-11-07 09:34:10 +01:00
Alejandro Celaya
97f89bcede Simplified transactional URL shortening 2020-11-06 20:05:57 +01:00
Alejandro Celaya
00255b04eb Added migration to create new author_api_key_id in short_urls 2020-11-06 19:43:05 +01:00
Alejandro Celaya
f90ea4bd98 Updated dependencies 2020-11-06 18:58:07 +01:00
Alejandro Celaya
0d7fb1163a Merge pull request #886 from acelaya-forks/feature/update-dependencies
Updated dependencies
2020-11-02 12:17:55 +01:00
Alejandro Celaya
cb340b5867 Updated phpunit configs to use new schema introduced in v9.3 2020-11-02 12:07:45 +01:00
Alejandro Celaya
1621f3a943 Updated dependencies 2020-11-02 11:53:14 +01:00
Alejandro Celaya
ae636aef5a Merge pull request #885 from acelaya-forks/feature/deprecate-create-tag
Feature/deprecate create tag
2020-11-02 11:17:30 +01:00
Alejandro Celaya
1346d7902e Updated changelog 2020-11-02 11:06:41 +01:00
Alejandro Celaya
544836b986 Deprecated tags creation 2020-11-02 11:05:14 +01:00
Alejandro Celaya
397f7d09e3 Merge pull request #884 from acelaya-forks/feature/missing-docker-extension
Feature/missing docker extension
2020-11-02 09:50:52 +01:00
Alejandro Celaya
efa707c676 Updated changelog 2020-11-02 09:25:17 +01:00
Alejandro Celaya
51c8b80489 Changed to for consistency in the Dockerfile 2020-11-02 09:24:14 +01:00
Alejandro Celaya
e71fb0ac7f Added gmp extension to docker images, as it seems to be required by geolite in some cases 2020-11-02 09:02:00 +01:00
Alejandro Celaya
681b7c836d Added swoole extension to publish-release github action 2020-11-01 11:47:04 +01:00
Alejandro Celaya
7c2c90fc49 Merge pull request #879 from acelaya-forks/feature/github-release-action
Feature/GitHub release action
2020-11-01 11:42:08 +01:00
Alejandro Celaya
ebe6a5f4aa Moved github release creation from travis to github action 2020-11-01 11:23:11 +01:00
Alejandro Celaya
65651e4bbd Updated changelog to more strictly endorse to keepachangelog spec 2020-11-01 11:22:29 +01:00
Alejandro Celaya
33190c07c7 Updated references from travis-ci.org to travis-ci.com 2020-10-31 08:25:03 +01:00
Alejandro Celaya
f651b0e5a1 Merge pull request #873 from acelaya-forks/feature/disable-platform-checks
Disabled platform checks in composer
2020-10-29 17:28:18 +01:00
Alejandro Celaya
c85eb84b4c Disabled platform checks in composer 2020-10-29 17:24:12 +01:00
Alejandro Celaya
86d428184e Merge pull request #866 from acelaya-forks/feature/composer-2
Updated to composer 2
2020-10-26 19:47:35 +01:00
Alejandro Celaya
c1529b7d6c Updated to composer 2 2020-10-25 17:59:37 +01:00
Alejandro Celaya
7ecc3aacc4 Merge pull request #865 from acelaya-forks/feature/importer
Feature/importer
2020-10-25 14:17:50 +01:00
Alejandro Celaya
b091bd4e2a Ensured composer 1 for now 2020-10-25 13:46:39 +01:00
Alejandro Celaya
90b4bc9b1a Updated changelog 2020-10-25 13:36:21 +01:00
Alejandro Celaya
de7096010e Created DoctrineBatchHelperTest 2020-10-25 13:30:18 +01:00
Alejandro Celaya
03a9697298 Created ImportedLinksProcessorTest 2020-10-25 13:20:34 +01:00
Alejandro Celaya
fdcf88de67 Added database tests for ShortUrlRepository::importedUrlExists 2020-10-25 12:06:48 +01:00
Alejandro Celaya
7c343f42c1 Improved how existing imported short URLs are checked by tracking its original short code 2020-10-25 11:57:26 +01:00
Alejandro Celaya
786e4f642b Moved short code uniqueness checks to external helper class that is used in UrlShortener and ImportedLinksProcessor 2020-10-25 11:16:42 +01:00
Alejandro Celaya
b1a073b1ab Ensured uniqueness on imported short URLs short code 2020-10-25 10:26:11 +01:00
Alejandro Celaya
2256f6a9e7 Added feedback to ImportedLinksProcessor 2020-10-24 15:09:46 +02:00
Alejandro Celaya
ec3e7212b2 Basic short-úrl import implementation 2020-10-24 13:55:54 +02:00
Alejandro Celaya
554d9b092f Added import_source column in ShortUrls 2020-10-23 12:59:39 +02:00
Alejandro Celaya
33d3837795 Added dependency on shlinkio/shlink-importer 2020-10-22 18:12:22 +02:00
Alejandro Celaya
0686ac2fb1 Merge pull request #857 from acelaya-forks/feature/php8
Feature/php8
2020-10-16 20:14:57 +02:00
Alejandro Celaya
ce3d267572 Updated changelog 2020-10-16 19:54:09 +02:00
Alejandro Celaya
4ec90e02c9 Updated to latest infection 2020-10-16 19:53:05 +02:00
Alejandro Celaya
e7bccb088d Updated to latest swoole and pdo_sqlsrv versions which are compatible with PHP8 2020-10-16 19:28:57 +02:00
Alejandro Celaya
cbc9f1257d Enabled Diactoros as module 2020-10-16 19:21:40 +02:00
Alejandro Celaya
c7f15b77fd Merge pull request #853 from dlondero/phpunit-static-assertions
PHPUnit static assertions
2020-10-04 09:44:18 +02:00
Daniel Londero
a8b0c46142 Fix typo 2020-10-04 00:35:29 +02:00
Daniel Londero
065d314608 Invoke PHPUnit's assertions statically 2020-10-04 00:35:14 +02:00
Alejandro Celaya
d426dbc684 Merge pull request #850 from acelaya-forks/feature/env-docker-port
Feature/env docker port
2020-10-03 12:12:25 +02:00
Alejandro Celaya
c6c78f383f Updated changelog 2020-10-03 11:56:09 +02:00
Alejandro Celaya
450eea64aa Added support for port option in SimplifiedConfigParser 2020-10-03 11:54:31 +02:00
Alejandro Celaya
c8d7413dd4 Documented support for PORT env var in Docker image 2020-10-03 11:52:27 +02:00
Alejandro Celaya
00a96e6215 Allowed to change swoole port in docker image by using the PORT env var 2020-10-03 11:49:25 +02:00
Alejandro Celaya
b15e90408f Merge pull request #849 from acelaya-forks/feature/domains-endpoint
Feature/domains endpoint
2020-09-27 12:59:54 +02:00
Alejandro Celaya
34c10c0bc9 Updated changelog 2020-09-27 12:50:03 +02:00
Alejandro Celaya
63a24342e3 Created unit test for ListDomainsCommand 2020-09-27 12:48:24 +02:00
Alejandro Celaya
073e4eeac8 Created command to list domains 2020-09-27 12:39:02 +02:00
Alejandro Celaya
06eda073bf Added API test for /domains endpoint 2020-09-27 10:23:17 +02:00
Alejandro Celaya
614e1c37f8 Added database test for Domainrepository 2020-09-27 10:18:49 +02:00
Alejandro Celaya
24aab5cc0e Created unit tests for new Domain-related elements 2020-09-27 10:11:41 +02:00
Alejandro Celaya
76d6d9a7a9 Created rest endpoint to list existing domains 2020-09-27 09:53:12 +02:00
Alejandro Celaya
8109ceb7eb Merge pull request #845 from acelaya-forks/feature/api-test-coverage
Feature/api test coverage
2020-09-26 11:33:36 +02:00
Alejandro Celaya
6163e34327 Directly run API tests on travis, because they get stuck when run through composer 2020-09-26 11:16:35 +02:00
Alejandro Celaya
84b291e310 Added message with exit code in API tests script 2020-09-26 11:07:02 +02:00
Alejandro Celaya
20cd5cd752 Updated changelog 2020-09-26 10:54:52 +02:00
Alejandro Celaya
d9d57743e6 Fixed code copverage on API tests being exported as Clover instead of PHP 2020-09-26 10:49:56 +02:00
Alejandro Celaya
cc57dcd01a Added code coverage to API tests 2020-09-26 10:43:50 +02:00
Alejandro Celaya
10fbf8f8ff Merge pull request #843 from acelaya-forks/feature/runtime-validation-flag
Feature/runtime validation flag
2020-09-24 22:29:32 +02:00
Alejandro Celaya
cfc9a1b772 Ensure string casting safety 2020-09-24 22:15:26 +02:00
Alejandro Celaya
2555424124 Updated changelog 2020-09-24 22:04:38 +02:00
Alejandro Celaya
405369824b Added hability to override URL validation from the CLI 2020-09-24 21:54:03 +02:00
Alejandro Celaya
cdd87f5962 Documented validateUrl params on create/edit short URL endpoints 2020-09-23 19:24:15 +02:00
Alejandro Celaya
d5eac3b1c3 Added validateUrl optional flag for create/edit short URLs 2020-09-23 19:19:17 +02:00
Alejandro Celaya
1f78f5266a Merge pull request #842 from acelaya-forks/feature/find-if-exists-performance
Feature/find if exists performance
2020-09-23 08:01:02 +02:00
Alejandro Celaya
aa0124f4e9 Moved API tests back to composer ci command 2020-09-23 07:49:59 +02:00
Alejandro Celaya
641f35ae05 Updated changelog 2020-09-23 07:46:25 +02:00
Alejandro Celaya
4e94f07050 Added tests for new ShortUrlRepository::findOneMatching method 2020-09-23 07:34:36 +02:00
Alejandro Celaya
460ca032d2 Drastically improved performance when creating new short URLs with findIfExists by moving logic to DB query 2020-09-23 00:22:29 +02:00
Alejandro Celaya
8d438aa6aa Merge pull request #841 from acelaya-forks/feature/svg-qr-codes
Feature/svg qr codes
2020-09-21 23:05:11 +02:00
Alejandro Celaya
504d08101a Updated changelog 2020-09-21 22:55:18 +02:00
Alejandro Celaya
4b7184ac85 Added tests for new QR code format 2020-09-21 22:54:05 +02:00
Alejandro Celaya
55d9f2a4a1 Added support to return the QR code in SVG format 2020-09-21 22:48:52 +02:00
Alejandro Celaya
319b790628 Merge pull request #840 from acelaya-forks/feature/extended-ordering-support
Feature/extended ordering support
2020-09-21 22:19:55 +02:00
Alejandro Celaya
ee563978ac Updated changelog 2020-09-21 22:06:41 +02:00
Alejandro Celaya
be71a6eeb4 Replaced colon by hyphen as the ordering field-dir separator as it's a valid URL character 2020-09-21 22:03:43 +02:00
Alejandro Celaya
25fbbee883 Added support to order short urls liusts using the <field>:<dir> notaiton as string 2020-09-20 13:21:21 +02:00
Alejandro Celaya
8dbd9ca33d Merge pull request #824 from shlinkio/develop
Release v2.3.0
2020-08-09 11:47:57 +02:00
Alejandro Celaya
cad8c7ed48 Added v2.3.0 to changelog 2020-08-09 11:42:26 +02:00
Alejandro Celaya
c11c731bef Merge pull request #823 from acelaya-forks/feature/docker-updates
Feature/docker updates
2020-08-09 11:41:16 +02:00
Alejandro Celaya
a79362d520 Updated changelog 2020-08-09 11:14:50 +02:00
Alejandro Celaya
c708df2029 Updated to latest docker 2020-08-09 11:13:14 +02:00
Alejandro Celaya
e0760c371a Merge pull request #821 from acelaya-forks/feature/slug-regex
Feature/slug regex
2020-08-09 10:55:32 +02:00
Alejandro Celaya
714a58945e Fixed access to magic method that no longer exists 2020-08-09 10:46:44 +02:00
Alejandro Celaya
87e8ae7af6 Moved custom salugs regex to constant 2020-08-09 10:24:59 +02:00
Alejandro Celaya
a66dca4f07 Merge branch 'develop' of github.com:shlinkio/shlink into develop 2020-07-31 21:44:18 +02:00
Alejandro Celaya
9853b0916f Merge pull request #817 from acelaya-forks/feature/gh-action-docker-build
Feature/gh action docker build
2020-07-31 21:43:21 +02:00
Alejandro Celaya
18afd92fc3 Fixed how docker image version is extracted from github ref 2020-07-31 21:32:06 +02:00
Alejandro Celaya
0474b32c34 Recovered real docker image on docker build script 2020-07-31 21:25:42 +02:00
Alejandro Celaya
ca6fb1c656 Merge pull request #15 from acelaya-forks/feature/gh-action-docker-build
Feature/gh action docker build
2020-07-31 20:42:30 +02:00
Alejandro Celaya
a7a69506a0 Fixed how docker credentials are read from secrets 2020-07-31 20:41:39 +02:00
Alejandro Celaya
a32651aab3 Replace -u by --username on docker login command 2020-07-31 20:30:30 +02:00
Alejandro Celaya
977af0ee43 Fixed pattern for tags on github action 2020-07-31 20:24:44 +02:00
Alejandro Celaya
53bbcd34a6 Replaced built image by lab one while testing functionality 2020-07-31 20:19:46 +02:00
Alejandro Celaya
1eb9ef0361 Moved docker image build to github actions 2020-07-31 20:17:14 +02:00
Alejandro Celaya
1ac05fd3a4 Update CONTRIBUTING.md 2020-07-26 22:10:26 +02:00
Alejandro Celaya
4aef0fa728 Merge pull request #813 from acelaya-forks/feature/php8-ci
Added Builds on PHP nightly
2020-07-24 11:00:43 +02:00
Alejandro Celaya
f4da1b0a2e Fixed wrong regexes in phpstan.neon 2020-07-23 16:53:28 +02:00
Alejandro Celaya
163839494b Added Builds on PHP nightly 2020-07-23 16:34:25 +02:00
Alejandro Celaya
8a811c5b33 Merge pull request #809 from acelaya-forks/feature/list-all-command
Feature/list all command
2020-07-14 15:50:29 +02:00
Alejandro Celaya
007139e4ff Updated changelog 2020-07-14 15:37:21 +02:00
Alejandro Celaya
6be0310933 Improved command flag description 2020-07-14 15:31:18 +02:00
Alejandro Celaya
5f9b629676 Added test for short URLs with all items 2020-07-14 13:28:38 +02:00
Alejandro Celaya
8e84b0e8ac Ensured page footer on list short URLs is not displayed when printing all URLs 2020-07-14 13:14:53 +02:00
Alejandro Celaya
3ff9e101a8 Added support to print all short URLs at once from CLI 2020-07-14 13:00:56 +02:00
Alejandro Celaya
71570af7db Merge pull request #808 from acelaya-forks/feature/trailing-question-mark
Fixed issue introduced with league/uri library
2020-07-10 23:36:16 +02:00
Alejandro Celaya
1401dd9156 Fixed issue introduced with league/uri library 2020-07-10 23:25:31 +02:00
Alejandro Celaya
36c12a69b1 Added project structure explanation to CONTRIBUTING doc 2020-07-08 15:38:12 +02:00
Alejandro Celaya
742e2d724e Updated comment on issue templates 2020-07-06 09:28:31 +02:00
Alejandro Celaya
f74851b0d8 Merge pull request #804 from acelaya-forks/feature/document-tests
Added project tests section to the CONTRIBUTING file
2020-07-01 16:38:46 +02:00
Alejandro Celaya
dd5dcf6ec1 Fixed typo 2020-07-01 16:38:19 +02:00
Alejandro Celaya
a448972e3c Added project tests section to the CONTRIBUTING file 2020-07-01 16:35:25 +02:00
Alejandro Celaya
f784a4f794 Merge pull request #799 from acelaya-forks/feature/guzzle7
Feature/guzzle7
2020-06-28 10:23:20 +02:00
Alejandro Celaya
554a66503f Updated changelog 2020-06-28 10:07:43 +02:00
Alejandro Celaya
73c6c52b2a Updated to guzzle 7 2020-06-28 10:06:49 +02:00
Alejandro Celaya
509672f4c7 Added intl to required PHP extensions 2020-06-27 16:42:17 +02:00
Alejandro Celaya
e4f01e4cf8 Merge pull request #797 from acelaya-forks/feature/deeplinks-support
Feature/deeplinks support
2020-06-27 11:26:35 +02:00
Alejandro Celaya
156eae56d0 Fixed typo in contributing doc 2020-06-27 11:16:59 +02:00
Alejandro Celaya
2df6e694ea Updated changelog 2020-06-27 11:15:17 +02:00
Alejandro Celaya
78b838f6b6 Used league/uri to validate URLs including deeplinks, and fixed tests 2020-06-27 11:14:10 +02:00
Alejandro Celaya
08950f6433 Replaced UriInterface by string when creating a short URL 2020-06-27 10:48:35 +02:00
Alejandro Celaya
a74e1df55c Merge pull request #796 from acelaya-forks/feature/contributing
Feature/contributing
2020-06-27 10:45:09 +02:00
Alejandro Celaya
bf1c6e3d43 Referenced CONTRIBUTING doc from README 2020-06-27 10:43:43 +02:00
Alejandro Celaya
d234e114db Added description on how to create pull requests to CONTRIBUTING file 2020-06-27 10:41:29 +02:00
Alejandro Celaya
035743ef6a Added minor imporovements to CONTRIBUTING file 2020-06-27 10:34:26 +02:00
Alejandro Celaya
c7c9ab71ff Created first draft of the contributing file 2020-06-26 21:22:54 +02:00
Alejandro Celaya
e107aa9ed8 Removed commented migrations option 2020-06-23 19:23:33 +02:00
Alejandro Celaya
e9191732bd Merge pull request #794 from acelaya-forks/feature/migrations3
Feature/migrations3
2020-06-21 13:21:14 +02:00
Alejandro Celaya
f44540f95e Updated changelog 2020-06-21 13:01:10 +02:00
Alejandro Celaya
6b3fd2ac83 Commented out name config option for migrations, since it makes it fail 2020-06-21 13:00:32 +02:00
Alejandro Celaya
eed353fedf Updated migration template 2020-06-21 12:29:56 +02:00
Alejandro Celaya
b4e58cc1bb Updated doctrine config for v3 2020-06-21 12:24:47 +02:00
Alejandro Celaya
56d690d9a6 Removed references to master branch 2020-06-21 12:21:39 +02:00
Alejandro Celaya
bffc044bc7 Fixed typo 2020-06-20 11:34:09 +02:00
Alejandro Celaya
58dd1c54f9 Merge pull request #792 from acelaya-forks/feature/configurable-redirect
Feature/configurable redirect
2020-06-20 11:33:48 +02:00
Alejandro Celaya
5c163490c7 Allowed new redirect config options to be pased as env vars to the docker image 2020-06-20 11:21:37 +02:00
Alejandro Celaya
f2f07be11f Updated to latest installer, supporting redirects customizations 2020-06-20 11:07:15 +02:00
Alejandro Celaya
0bea843e7f Added test covering how redirects config works 2020-06-20 09:50:56 +02:00
Alejandro Celaya
83cc11030d Updated changelog 2020-06-20 09:30:23 +02:00
Alejandro Celaya
cb70dc5389 Removed stuff from local config file which already comes on third party config 2020-06-20 09:20:01 +02:00
Alejandro Celaya
68db52679b Added support to serve redirects with status 301 and Cache-Control 2020-06-17 19:01:56 +02:00
Alejandro Celaya
186168b26c Merge pull request #789 from acelaya-forks/feature/simplified-travis-config
Simplified travis configuration, by removing all env vars checks
2020-06-10 18:05:31 +02:00
Alejandro Celaya
e9c64b46b7 Removed condition from travis that is now implicit 2020-06-10 17:54:41 +02:00
Alejandro Celaya
f476cfc30f Simplified travis configuration, by removing all env vars checks 2020-06-10 17:51:20 +02:00
Alejandro Celaya
3706d6c82d Merge pull request #783 from acelaya-forks/feature/extended-mutation-checks
Feature/extended mutation checks
2020-06-09 12:11:56 +02:00
Alejandro Celaya
248209ab41 Updated changelog 2020-06-08 23:30:19 +02:00
Alejandro Celaya
2867a9b7b0 Added commands to run infection checks on database tests 2020-06-08 23:26:27 +02:00
Alejandro Celaya
68919c19b8 Added deprecation in BodyParserMiddleware 2020-06-08 23:25:54 +02:00
Alejandro Celaya
ee1aa42900 Improved titles on error templates 2020-06-08 23:25:54 +02:00
Alejandro Celaya
c3de39d313 Merge pull request #787 from shlinkio/develop
Release v2.2.2
2020-06-08 23:09:28 +02:00
Alejandro Celaya
8ecc9c69a2 Added v2.2.2 to changelog 2020-06-08 22:49:40 +02:00
Alejandro Celaya
e814f3afcf Merge pull request #784 from acelaya-forks/feature/tag-visits-many-short-urls
Feature/tag visits many short urls
2020-06-08 22:48:52 +02:00
Alejandro Celaya
a4eda9d761 Moved execution of API tests outside composer script 2020-06-08 22:38:51 +02:00
Alejandro Celaya
f3f3ef5c18 Removed unused import 2020-06-08 18:37:45 +02:00
Alejandro Celaya
296134078c Updated changelog 2020-06-08 18:37:45 +02:00
Alejandro Celaya
527faf27a8 Changed how visits for a tag are fetched, avoiding thousands of values to be loaded in memory 2020-06-08 18:37:22 +02:00
Alejandro Celaya
9c339b9c4f Merge pull request #785 from acelaya-forks/feature/improve-custom-slugs
Improved custom slug sluggification, allowing valid URL characters
2020-06-08 18:36:36 +02:00
Alejandro Celaya
f274cafa7c Updated changelog 2020-06-08 18:10:34 +02:00
Alejandro Celaya
371f246c41 Improved custom slug sluggification, allowing valid URL characters 2020-06-08 18:08:53 +02:00
Alejandro Celaya
95ae540799 Defined docker image to build in a var 2020-05-17 10:19:54 +02:00
Alejandro Celaya
f340e0e76e Temporary disabled ARM docker images to reduce build times 2020-05-17 09:37:05 +02:00
Alejandro Celaya
14e0766f72 Merge pull request #773 from acelaya-forks/feature/temporal-build-fix
Going back to single travis job for docker image building
2020-05-16 22:18:03 +02:00
Alejandro Celaya
17f3897746 Going back to single travis job for docker image building 2020-05-16 22:01:20 +02:00
Alejandro Celaya
3c3a30cc0e Merge pull request #772 from acelaya-forks/feature/separate-docker-builds
Separated docker builds in different platforms
2020-05-16 15:15:47 +02:00
Alejandro Celaya
726811f91f Separated docker builds in different platforms 2020-05-16 15:06:37 +02:00
Alejandro Celaya
75f5da5846 Fixed docker install in travis 2020-05-16 14:05:39 +02:00
Alejandro Celaya
489c739be2 Updated condition to run docker publish 2020-05-16 14:00:03 +02:00
Alejandro Celaya
9d6f14c81a Merge pull request #771 from acelaya-forks/feature/build-time-improvements
Changed travis build so that docker image publishing runs on its own …
2020-05-16 13:50:54 +02:00
Alejandro Celaya
788f9635dd Fixed travis config syntax error 2020-05-16 13:40:59 +02:00
Alejandro Celaya
09aa4cc977 Changed travis build so that docker image publishing runs on its own separated job 2020-05-16 13:28:29 +02:00
Alejandro Celaya
9252cc269b Merge pull request #770 from acelaya-forks/feature/multi-arch-improvements
Feature/multi arch improvements
2020-05-16 11:35:56 +02:00
Alejandro Celaya
65e6676c00 Removed docker image building on non-PR builds 2020-05-16 11:25:50 +02:00
Alejandro Celaya
135b62a9cc Documented multi-architecture on docker image 2020-05-16 10:39:47 +02:00
Alejandro Celaya
2ea58acde2 Updated changelog 2020-05-16 10:28:09 +02:00
Alejandro Celaya
e1085f3ef5 Merge pull request #756 from Starbix/multi-arch
Add multi arch support
2020-05-16 10:22:59 +02:00
Cédric Laubacher
f1db195a06 Merge branch 'develop' into multi-arch 2020-05-15 20:37:41 +02:00
Cédric Laubacher
fa646b0176 Add multi arch support 2020-05-15 18:32:35 +02:00
Alejandro Celaya
21ef1dfee8 Merge pull request #765 from acelaya-forks/feature/fix-dates-match
Feature/fix dates match
2020-05-11 13:27:38 +02:00
Alejandro Celaya
5ef548bc2a Updated changelog with v2.2.1 2020-05-11 13:19:01 +02:00
Alejandro Celaya
1fa9896524 Fixed error when trying to match creteria on a Short URL with dates 2020-05-11 13:12:55 +02:00
Alejandro Celaya
cb6756d801 Merge pull request #763 from shlinkio/develop
Release 2.2.0
2020-05-09 11:10:31 +02:00
Alejandro Celaya
cf605407ad Used definitive dependency versions for shlink-common and shlñink-installer 2020-05-09 10:56:07 +02:00
Alejandro Celaya
1a4eee1c81 Merge pull request #762 from acelaya-forks/feature/visits-by-tag
Feature/visits by tag
2020-05-09 10:52:33 +02:00
Alejandro Celaya
4c5cd88041 Updated changelog 2020-05-09 10:38:18 +02:00
Alejandro Celaya
4d346d1fea Created API test for tags visits endpoint 2020-05-09 10:31:39 +02:00
Alejandro Celaya
7f39e6d768 Created TagVisitsActionTest 2020-05-09 10:22:07 +02:00
Alejandro Celaya
9b9de8e290 Updated VisitsTrackerTest 2020-05-09 10:14:26 +02:00
Alejandro Celaya
e1e3c7f061 Created paginator adapter tests 2020-05-09 10:10:48 +02:00
Alejandro Celaya
3218f8c283 Added Created endpoint to serve visits by tag 2020-05-09 09:53:45 +02:00
Alejandro Celaya
f0acce1be0 Updated to latest common 2020-05-09 09:34:59 +02:00
Alejandro Celaya
dd4b4277c9 Added test for VisitRepository tag methods 2020-05-08 20:11:37 +02:00
Alejandro Celaya
baf77b6ffb Implemented methods to get paginated list of visits by tag, reusing methods used for short code filtering 2020-05-08 19:55:05 +02:00
Alejandro Celaya
5be882a31b Improved parameter definition in some private queries in VisitRepository 2020-05-08 19:41:21 +02:00
Alejandro Celaya
ae060f3b13 Merge pull request #761 from acelaya-forks/feature/optional-obfuscation
Feature/optional obfuscation
2020-05-08 16:03:11 +02:00
Alejandro Celaya
e8ab664561 Updated changelog 2020-05-08 15:54:50 +02:00
Alejandro Celaya
f4bf3551f6 Updated shlink-installer to a version supporting IP anonymization param 2020-05-08 15:50:16 +02:00
Alejandro Celaya
8f06e4b20f Replaced references to obfuscate by anonymize 2020-05-08 15:43:09 +02:00
Alejandro Celaya
bfdd6e0c50 Ensured SimplifiedConfigParser properly handles obfuscate_remote_addr option 2020-05-08 13:21:49 +02:00
Alejandro Celaya
ba13d99a71 Allowed remote addr obfuscation to be configured on docker image by using the OBFUSCATE_REMOTE_ADDR env var 2020-05-08 13:19:40 +02:00
Alejandro Celaya
eac468514b Allow to determine if remote addresses should be obfuscated at configuration level 2020-05-08 13:10:58 +02:00
Alejandro Celaya
7da00fbc8c Updated Visit entity so that the address can be optionally obfuscated 2020-05-08 12:58:49 +02:00
Alejandro Celaya
4b7c54d7a9 Merge pull request #760 from acelaya-forks/feature/list-tags-command
Updated ListTagsCommand so that it displays extended information
2020-05-08 12:57:35 +02:00
Alejandro Celaya
c336bb1901 Updated ListTagsCommand so that it displays extended information 2020-05-08 12:39:02 +02:00
Alejandro Celaya
fbb1c449da Merge pull request #759 from acelaya-forks/feature/improved-tags-endpoint
Feature/improved tags endpoint
2020-05-08 12:17:32 +02:00
Alejandro Celaya
252cc7f49d Updated changelog 2020-05-08 11:53:26 +02:00
Alejandro Celaya
00cac4ba72 Created rest test for list tags action 2020-05-08 11:51:28 +02:00
Alejandro Celaya
91aaffc6db Updated ListTagsActionTest 2020-05-08 11:32:06 +02:00
Alejandro Celaya
2e269bcacd Updated TagServiceTest 2020-05-08 11:14:39 +02:00
Alejandro Celaya
bdd14427d9 Added tests for TagRepository::findTagsWithInfo 2020-05-08 11:09:28 +02:00
Alejandro Celaya
06c59fe2dd Fixed invalid imports after class refactoring 2020-05-08 10:29:24 +02:00
Alejandro Celaya
9a78fd1a26 Fixed definition of inversed many to many entity relationship 2020-05-08 10:25:33 +02:00
Alejandro Celaya
626c92460b Enhanced list tags endpoint so that it can also return stats foir every tag 2020-05-08 10:15:33 +02:00
Alejandro Celaya
7e0a14493e Documented updates on the tags endpoint to return more detailed information 2020-05-08 10:14:38 +02:00
Alejandro Celaya
8d23e60d3a Merge pull request #758 from acelaya-forks/feature/non-stable-alpha
Ensured stable tag is not pushed when building docker image for alpha or beta versions
2020-05-07 10:57:52 +02:00
Alejandro Celaya
5f0293bc21 Ensured stable tag is not pushed when building docker image for alpha or beta versions 2020-05-07 10:45:53 +02:00
Alejandro Celaya
afe7381263 Merge pull request #757 from acelaya-forks/feature/docker-img-impr
Feature/docker img impr
2020-05-07 10:31:32 +02:00
Alejandro Celaya
b75922f1d3 Updated changelog 2020-05-07 10:17:34 +02:00
Alejandro Celaya
d9ae83a92b Updated everything related with dependencies in docker images 2020-05-07 10:16:20 +02:00
Alejandro Celaya
22cc9ace4d Merge pull request #755 from acelaya-forks/feature/fix-logged-remote-ip
Feature/fix logged remote ip
2020-05-05 13:04:02 +02:00
Alejandro Celaya
53a37feafe Updated changelogs 2020-05-05 12:54:08 +02:00
Alejandro Celaya
0cab51b01b Enforced mezzio-swoole 2.6.4 or greater 2020-05-05 12:51:47 +02:00
Alejandro Celaya
5f258b6a28 Merge pull request #752 from acelaya-forks/feature/travis-db-tests
Feature/travis db tests
2020-05-04 22:06:04 +02:00
Alejandro Celaya
cc41c51f77 Removed duplicated pdo_sqlsrv enabling on travis config 2020-05-04 21:55:18 +02:00
Alejandro Celaya
5f42266cf2 Moved ms odbc commands to a script 2020-05-04 21:48:54 +02:00
Alejandro Celaya
522d8ed236 Ensured some commands are run as sudo during travis CI 2020-05-04 21:33:19 +02:00
Alejandro Celaya
78359c28c7 Added MS ODBC package installation to travis 2020-05-04 21:22:41 +02:00
Alejandro Celaya
13bb48d068 Installed pdo_sqlsrv extension in travis 2020-05-04 21:12:49 +02:00
Alejandro Celaya
f6d9a83202 Moved initial ci databases to specific docker-compose file 2020-05-04 21:00:09 +02:00
Alejandro Celaya
dfdae96da5 Added commands to initially create all testing database for all database engines in travis 2020-05-04 20:34:28 +02:00
Alejandro Celaya
9f13063b1f Fixed docker-compose command run in travis 2020-05-04 20:02:48 +02:00
Alejandro Celaya
1e8c36b5f1 Updated changelog 2020-05-04 19:55:52 +02:00
Alejandro Celaya
e747a0b250 Updated how database tests are run in travis, so that all DB engines are covered 2020-05-04 19:55:03 +02:00
Alejandro Celaya
79b8834c61 Merge pull request #748 from acelaya-forks/feature/visits-perf-improvements
Feature/visits perf improvements
2020-05-03 20:11:40 +02:00
Alejandro Celaya
313b6a59b9 Updated changelog 2020-05-03 20:02:50 +02:00
Alejandro Celaya
d5288f756e Fixed entity mapping for visits without a visit location 2020-05-03 19:52:40 +02:00
Alejandro Celaya
867659ea25 Created index on visits.date column 2020-05-03 19:15:26 +02:00
Alejandro Celaya
74ad3553cb Hardcoded types on date fields when filtering visits lists 2020-05-03 19:02:13 +02:00
Alejandro Celaya
8b0ce8e6f3 Improved performance when loading visits chuncks at high offsets 2020-05-03 18:20:01 +02:00
Alejandro Celaya
0e4bccc4bb Cached result of the count query on VisitsPaginatorAdapter 2020-05-03 10:44:01 +02:00
Alejandro Celaya
c4ae89a279 Removed DISTINCT when counting visits for a short URL 2020-05-03 10:22:00 +02:00
Alejandro Celaya
80d41db901 Improved performance on query that returns the list of visits for a short URL 2020-05-02 22:47:59 +02:00
Alejandro Celaya
6c30fc73ee Added swoole reverse proxy container 2020-05-02 12:04:42 +02:00
Alejandro Celaya
56932e4ea6 Disabled swoole coroutines 2020-05-01 18:24:48 +02:00
Alejandro Celaya
84b38c4940 Merge pull request #745 from acelaya-forks/feature/general-visits
Feature/general visits
2020-05-01 12:16:22 +02:00
Alejandro Celaya
aece9e68ba Removed logger dependency from rest actions 2020-05-01 12:08:44 +02:00
Alejandro Celaya
d067f52ac2 Updated changelog 2020-05-01 11:58:59 +02:00
Alejandro Celaya
b5947d1642 Created more unit tests 2020-05-01 11:57:46 +02:00
Alejandro Celaya
3232ab401f Documented new visits endpoint 2020-05-01 11:44:55 +02:00
Alejandro Celaya
1ef10f11cb Created new action to get default visit stats 2020-05-01 11:40:02 +02:00
Alejandro Celaya
5beaab85ac Renamed GetVisitsAction to ShortUrlVisitsAction 2020-05-01 11:17:07 +02:00
Alejandro Celaya
4498386f56 Fixed merge conflicts 2020-04-30 20:26:00 +02:00
Alejandro Celaya
a30f796100 Merge pull request #743 from acelaya-forks/feature/geolite-license
Feature/geolite license
2020-04-30 19:34:44 +02:00
Alejandro Celaya
93a2c83652 Enabled GeoLite installer config option 2020-04-29 20:31:06 +02:00
Alejandro Celaya
4d4423413d Added GEOLITE_LICENSE_KEY env var to basic docker example, to encourage using it 2020-04-29 19:44:08 +02:00
Alejandro Celaya
a1c74c4038 Updated changelog 2020-04-29 19:31:10 +02:00
Alejandro Celaya
f71bb5e307 Added support for GEOLITE_LICENSE_KEY env var for docker image 2020-04-29 19:27:35 +02:00
Alejandro Celaya
9190996e54 Added support for geolite_license_key config option 2020-04-29 19:26:34 +02:00
Alejandro Celaya
af8b6b7f96 Documented how to pass a GEOLITE license key 2020-04-29 19:24:18 +02:00
Alejandro Celaya
704958994d Merge pull request #738 from acelaya-forks/feature/health-fix
Feature/health fix
2020-04-25 20:07:09 +02:00
Alejandro Celaya
a6864bca7c Updated changelog 2020-04-25 20:00:01 +02:00
Alejandro Celaya
15a8305209 Fixed random 503 responses from the HealthAction when the database connection injected on it has expired 2020-04-25 19:58:49 +02:00
Alejandro Celaya
469b70d708 Merge pull request #737 from acelaya-forks/feature/installation-error
Fixed error when cleaning metadata cache during installation with APC…
2020-04-25 19:30:06 +02:00
Alejandro Celaya
4f988d223b Fixed error when cleaning metadata cache during installation with APCu enabled 2020-04-25 19:13:47 +02:00
Alejandro Celaya
e95abc4efb Merge pull request #736 from acelaya-forks/feature/mercure-proxy
Configured an nginx container acting as a reverse proxy for the mercu…
2020-04-25 13:56:07 +02:00
Alejandro Celaya
4917e53acd Configured an nginx container acting as a reverse proxy for the mercure container 2020-04-25 13:44:09 +02:00
Alejandro Celaya
45db4c321a Merge pull request #731 from acelaya-forks/feature/fix-local-sqlite-tests
Ensured mysql config is not loaded for sqlite test envs
2020-04-18 14:06:44 +02:00
Alejandro Celaya
e6d914cfe1 Ensured mysql config is not loaded for sqlite test envs 2020-04-18 14:01:24 +02:00
Alejandro Celaya
85714c931d Merge pull request #730 from acelaya-forks/feature/fix-mysql-buffered-error
Feature/fix mysql buffered error
2020-04-18 13:29:24 +02:00
Alejandro Celaya
66a7f279c2 Updated changelog 2020-04-18 13:22:51 +02:00
Alejandro Celaya
7c6827ea9f Added MYSQL_ATTR_USE_BUFFERED_QUERY driver option with value true for mysql/maria connections 2020-04-18 13:21:46 +02:00
Alejandro Celaya
078c8ea011 Changed default mercure token duration to 1 day 2020-04-18 11:29:49 +02:00
Alejandro Celaya
655fd58a9d Added async API spec file 2020-04-16 22:44:08 +02:00
Alejandro Celaya
6ba6b951bf Changed mercure topics to be dash-cased 2020-04-16 22:25:12 +02:00
Alejandro Celaya
8e0e11f3b3 Merge pull request #727 from acelaya-forks/feature/mercure-improvement
Feature/mercure improvement
2020-04-14 21:16:16 +02:00
Alejandro Celaya
18b12ab1e6 Updated NotifyVisitToMercure to send both an update for all short URLs and one specific short URL 2020-04-14 20:57:25 +02:00
Alejandro Celaya
3908f63b0d Updated to latest installer version 2020-04-14 20:30:05 +02:00
Alejandro Celaya
ca2c32fa8c Removed no-longer used dependencies 2020-04-14 20:24:36 +02:00
Alejandro Celaya
a3a3ac1859 Added missing escaped characters 2020-04-13 13:23:26 +02:00
Alejandro Celaya
f5e0d0c2b1 Merge pull request #726 from acelaya-forks/feature/mercure-integration
Feature/mercure integration
2020-04-13 10:03:12 +02:00
Alejandro Celaya
ba0678946f Updated installer to use a version supporting mercure options 2020-04-13 09:38:18 +02:00
Alejandro Celaya
934fa937b5 Updated config parsers for docker image to accept new mercure env vars and configs 2020-04-12 20:41:23 +02:00
Alejandro Celaya
8d888cb43d Documented how to use a mercure hub when using the docker image 2020-04-12 18:39:28 +02:00
Alejandro Celaya
7f888c49b4 Created MercureUpdatesGeneratorTest 2020-04-12 18:01:13 +02:00
Alejandro Celaya
e97dfbfdda Created NotifyVisitToMercureTest 2020-04-12 17:50:40 +02:00
Alejandro Celaya
b858d79b9e Fixed mercure hub URL returned by MercureInfoAction 2020-04-12 17:50:09 +02:00
Alejandro Celaya
72d8edf4ff Created event listener that notifies mercure hub for new visits 2020-04-12 17:05:59 +02:00
Alejandro Celaya
31db97228d Created MercureInfoActionTest 2020-04-12 14:22:23 +02:00
Alejandro Celaya
2ffbf03cf8 Created action to get mercure integration info 2020-04-12 13:59:10 +02:00
Alejandro Celaya
85440c1c5f Improved mercure-related configs 2020-04-12 12:21:05 +02:00
Alejandro Celaya
69962f1fe8 Added package to handle JWTs 2020-04-11 18:10:56 +02:00
Alejandro Celaya
10cad33248 Added configuration for mercure integration 2020-04-11 18:10:56 +02:00
Alejandro Celaya
0c9deca3f8 Added symfony/mercure package and a container for development 2020-04-11 18:10:56 +02:00
Alejandro Celaya
e1cd4a6ee3 Merge pull request #724 from acelaya-forks/feature/clean-tasks
Created decorator for database connection closing and reopening for s…
2020-04-11 18:09:26 +02:00
Alejandro Celaya
f915b97606 Created decorator for database connection closing and reopening for swoole tasks 2020-04-11 18:00:40 +02:00
Alejandro Celaya
e775b0f12f Merge pull request #722 from shlinkio/develop
Release 2.1.3
2020-04-09 12:50:46 +02:00
Alejandro Celaya
3ee5853b32 Merge pull request #721 from acelaya-forks/feature/qr-code-links
Feature/qr code links
2020-04-09 12:40:05 +02:00
Alejandro Celaya
832a24e4c7 Updated changelog 2020-04-09 12:33:00 +02:00
Alejandro Celaya
551368c30d Ensured QR code action respects configured domain 2020-04-09 12:31:03 +02:00
Alejandro Celaya
9f24b8eb76 Merge pull request #720 from acelaya-forks/feature/db-conn-recovery-task-workers
Feature/db conn recovery task workers
2020-04-09 12:01:47 +02:00
Alejandro Celaya
4c83ae2b22 Updated changelog 2020-04-09 11:55:47 +02:00
Alejandro Celaya
28e0fb049b Added check to ensure DB connection is gracefully recovered on swoole task workers 2020-04-09 11:54:54 +02:00
Alejandro Celaya
f79a369884 Merge pull request #719 from acelaya-forks/feature/handle-HEAD-requests
Feature/handle head requests
2020-04-09 00:06:28 +02:00
Alejandro Celaya
34c7b870a7 Removed unnecessary service registration, as it comes preregistered from third party config provider 2020-04-08 23:56:39 +02:00
Alejandro Celaya
ec9f874bb9 Updated changelog 2020-04-08 23:53:23 +02:00
Alejandro Celaya
1980d35691 Ensured redirect requests are not tracked when request is performed using method HEAD 2020-04-08 23:51:57 +02:00
Alejandro Celaya
ec8cbf82e5 Added HEAD request implicit handling 2020-04-08 17:27:26 +02:00
Alejandro Celaya
2b1011de52 Merge pull request #714 from acelaya-forks/feature/metadata-cache-clear
Feature/metadata cache clear
2020-04-06 21:08:46 +02:00
Alejandro Celaya
fa9ace83ad Fixed incorrect use of tilde 2020-04-06 20:59:10 +02:00
Alejandro Celaya
a9a53a9652 Ensured entities metadata cache is cleared during installation and docker start-up 2020-04-06 20:52:33 +02:00
Alejandro Celaya
afca8b2a62 Merge pull request #704 from shlinkio/develop
Release v2.1.2
2020-03-29 13:23:41 +02:00
Alejandro Celaya
daeb293fb9 Merge pull request #703 from acelaya-forks/feature/infection-0.16
Feature/infection 0.16
2020-03-29 13:14:07 +02:00
Alejandro Celaya
1ca50a4a8a Updated changelog 2020-03-29 13:08:21 +02:00
Alejandro Celaya
c6602a81ab Updated to infection 0.16 2020-03-29 13:07:27 +02:00
Alejandro Celaya
46da0e7824 Merge pull request #702 from acelaya-forks/feature/fix-tags-filtering
Feature/fix tags filtering
2020-03-29 13:00:06 +02:00
Alejandro Celaya
e790a38cea Updated changelog 2020-03-29 12:54:09 +02:00
Alejandro Celaya
11879ea377 Ensured tags are not sluggified when using them to filter short URL lists 2020-03-29 12:51:39 +02:00
Alejandro Celaya
7105add009 Merge pull request #701 from acelaya-forks/feature/fix-migration
Fixed query in migration for postgres
2020-03-29 12:25:19 +02:00
Alejandro Celaya
af61fdb52d Updated changelog 2020-03-29 12:15:52 +02:00
Alejandro Celaya
2b4fc354db Fixed query in migration for postgres 2020-03-29 12:13:52 +02:00
Alejandro Celaya
5b72001a8c Merge pull request #699 from shlinkio/develop
V2.1.1
2020-03-28 20:14:35 +01:00
Alejandro Celaya
7c79906ac4 Merge pull request #698 from acelaya-forks/feature/2.1.1
Feature/2.1.1
2020-03-28 20:03:09 +01:00
Alejandro Celaya
e30a724529 Make sure dist files include the htaccess file 2020-03-28 19:54:02 +01:00
Alejandro Celaya
73f97ea874 Ensured releases are published to github before the docker image is built 2020-03-28 19:48:06 +01:00
Alejandro Celaya
09c155b7d3 Merge pull request #695 from shlinkio/develop
v2.1.0
2020-03-28 12:23:41 +01:00
Alejandro Celaya
1e2d115768 Merge pull request #694 from acelaya-forks/feature/process-retry
Feature/process retry
2020-03-28 12:06:09 +01:00
Alejandro Celaya
53ba58d7e9 Moved initialization of the io object in LocateVisitsCommand to the initialize method 2020-03-28 10:37:41 +01:00
Alejandro Celaya
2a30afbe7d Updated changelog 2020-03-28 10:29:12 +01:00
Alejandro Celaya
4d39c7041b Improved LocateVisitsCommandtest so that it covers all possible workflows 2020-03-28 10:23:34 +01:00
Alejandro Celaya
c012b4740d Updated VisitLocator test so that it covers all public methods 2020-03-28 10:01:09 +01:00
Alejandro Celaya
55778eb810 Ensured old visit locations are deleted when relocating a visit that has already been located 2020-03-28 09:27:45 +01:00
Alejandro Celaya
fb8ab0b5fe Implemented how to reprocess the locations of all existing visits 2020-03-28 09:12:15 +01:00
Alejandro Celaya
fcce18b059 Changed VisitLocator signature so that it expects an object implementing an interface instead of two arbitrary callbacks 2020-03-28 08:05:15 +01:00
Alejandro Celaya
43a3d469e7 Improved how visits with some conditions are fetched from the database, so all internal logic is 100% transparent regardless the purpose 2020-03-27 22:01:26 +01:00
Alejandro Celaya
f730c24ecb Created method to return visits with empty location 2020-03-26 22:56:53 +01:00
Alejandro Celaya
b8522b8c17 Created new method to locate empty visits 2020-03-26 22:37:46 +01:00
Alejandro Celaya
b0d96040be Merge pull request #691 from Lynnesbian/develop
Update sample Nginx config to point to PHP 7.4
2020-03-25 08:49:36 +01:00
Lynne
5554675d03 Update sample Nginx config to point to PHP 7.4 2020-03-25 16:48:44 +10:00
Alejandro Celaya
c88401ef29 Added isEmpty column to VisitLocation 2020-03-23 20:42:03 +01:00
Alejandro Celaya
75f77ed929 Merge pull request #689 from acelaya-forks/feature/fake-class-constant
Moved hardcoded class alias to a namespaced constant
2020-03-22 17:50:57 +01:00
Alejandro Celaya
4539ab2dcf Moved hardcoded class alias to a namespaced constant 2020-03-22 17:42:56 +01:00
Alejandro Celaya
9ad0561cac Merge pull request #688 from acelaya-forks/feature/edit-long-url
Feature/edit long url
2020-03-22 17:38:00 +01:00
Alejandro Celaya
774052a983 Updated changelog 2020-03-22 17:31:16 +01:00
Alejandro Celaya
3beb27acc2 Added API tests for the edition of the longURL 2020-03-22 17:30:01 +01:00
Alejandro Celaya
5432eb7b77 Added URL validation to ShortUrl edition, as long URL can now be edited 2020-03-22 17:22:52 +01:00
Alejandro Celaya
181ff16409 Registered PersistenceDomainResolver as a service to avoid instantiating a new one on every ShortUrl creation 2020-03-22 17:05:59 +01:00
Alejandro Celaya
682a0768b7 Moved check for URL validation config option to the UrlValidator itself 2020-03-22 16:58:28 +01:00
Alejandro Celaya
d29ebb706e Documented longUrl param on PATCH short URL endpoint 2020-03-22 14:06:13 +01:00
Alejandro Celaya
4e6836c605 Long URLs can now be edited on existing short URLs 2020-03-22 14:04:01 +01:00
Alejandro Celaya
59c0d36c0b Merge pull request #687 from acelaya-forks/feature/real-ip-geolocation
Feature/real ip geolocation
2020-03-22 11:55:02 +01:00
Alejandro Celaya
e10b2884c0 Added more exclussions to dockerignore 2020-03-22 11:33:00 +01:00
Alejandro Celaya
8fb54e815e Ensured scrutinizer build ignores platform requirements when installing dependencies 2020-03-22 11:27:03 +01:00
Alejandro Celaya
3a14483568 Updated changelog 2020-03-22 11:13:33 +01:00
Alejandro Celaya
fdd8efc12d Added test covering case in which the original address is provided when locating visits 2020-03-22 11:12:30 +01:00
Alejandro Celaya
3fef4b4a28 Ensured non-obfuscated IP address is passed to event listener which geolocates it 2020-03-22 10:48:27 +01:00
Alejandro Celaya
cea50a860e Improved docker image generation 2020-03-22 10:01:34 +01:00
Alejandro Celaya
f9318bb1b3 Merge pull request #686 from acelaya-forks/feature/reduce-docker-image
Feature/reduce docker image
2020-03-21 16:21:25 +01:00
Alejandro Celaya
d22f020eb5 Ensured more non-prod files are ignored/deleted from the final docker image 2020-03-21 16:11:56 +01:00
Alejandro Celaya
c556d8123b Fixed name of the build arg passed when building docker for a specific tag 2020-03-21 15:43:52 +01:00
Alejandro Celaya
a592833bd0 Merge pull request #685 from acelaya-forks/feature/fix-docker-build
Ensured docker build happens in all branches
2020-03-21 15:36:18 +01:00
Alejandro Celaya
881da3db3b Ensured docker build happens in all branches 2020-03-21 15:31:14 +01:00
Alejandro Celaya
32eb9924e5 Merge pull request #684 from acelaya-forks/feature/travis-docker-build
Feature/travis docker build
2020-03-21 15:14:14 +01:00
Alejandro Celaya
d2c06dd0ab Initialized typed nullable props as null in all entities 2020-03-21 14:38:24 +01:00
Alejandro Celaya
75b8ed813f Enforced Swoole 4.4.15 to be installed during travis build, to match production one 2020-03-21 14:25:19 +01:00
Alejandro Celaya
f811002c2b Small adjustments on docker build 2020-03-21 09:37:31 +01:00
Alejandro Celaya
ca1b17863c Split Dockerfile into multiple stages, making the build be independent and then the released image just copy files from it 2020-03-21 09:10:15 +01:00
Alejandro Celaya
644f5be6fe Added scripts and configs to build docker image on travis 2020-03-21 08:42:13 +01:00
Alejandro Celaya
65fbb1dfb3 Merge pull request #680 from acelaya-forks/feature/non-interactive-db-commands
Feature/non interactive db commands
2020-03-15 17:32:57 +01:00
Alejandro Celaya
8597966187 Updated changelog 2020-03-15 17:26:34 +01:00
Alejandro Celaya
6ddd70d21d Added --no-interaction to commands run internally from shlink DB commands 2020-03-15 17:25:39 +01:00
Alejandro Celaya
d32112fe7e Updated shlink packages and installed shlink-config 2020-03-15 17:14:37 +01:00
Alejandro Celaya
da858f0353 Merge branch 'develop' of github.com:shlinkio/shlink into develop 2020-03-10 21:45:57 +01:00
Alejandro Celaya
ba8b041698 Updated API docs links 2020-03-10 21:45:20 +01:00
Alejandro Celaya
d9fee5582a Added docker pulls badge to main readme file 2020-03-07 08:44:12 +01:00
Alejandro Celaya
c9f17d54ee Merge pull request #676 from acelaya-forks/feature/bar-slug
Feature/bar slug
2020-03-06 20:09:59 +01:00
Alejandro Celaya
f5c1e12db4 Added more tests covering invalid custom slugs 2020-03-06 20:01:41 +01:00
Alejandro Celaya
18ceafeb60 Ensured only empty strings are checked while verifying empty value on custom slug 2020-03-06 19:25:05 +01:00
Alejandro Celaya
67e93a6874 Ensured empty values cannot be provided as the custom slug 2020-03-06 19:20:33 +01:00
Alejandro Celaya
f6a83a3062 Merge pull request #670 from acelaya-forks/feature/base-url-redirect
Feature/base url redirect
2020-02-25 18:06:47 +01:00
Alejandro Celaya
8a0e902bdd Updated changelog 2020-02-25 18:02:38 +01:00
Alejandro Celaya
590fc3fc92 Added tests covering redirect simplified config parsing 2020-02-25 18:01:06 +01:00
Alejandro Celaya
0d54b7696f Merge pull request #669 from jpatters/fix/base_url_redirect_to
fixed incorrect configuration option for base_url_redirect_to
2020-02-25 17:58:58 +01:00
Jordan Patterson
6b1dadc35c fixed incorrect configuration option for base_url_redirect_to 2020-02-25 06:47:02 -08:00
Alejandro Celaya
86009543ed Merge pull request #662 from acelaya-forks/feature/rest-request-id
Feature/rest request
2020-02-21 19:54:30 +01:00
Alejandro Celaya
b728a78673 Updated changelog 2020-02-21 19:47:30 +01:00
Alejandro Celaya
fb89cb80ac Added formatting to swoole logs, to avoid duplicating the date 2020-02-19 19:48:48 +01:00
Alejandro Celaya
d0a986dd5a Added request ID to logs with monolog 2020-02-19 19:37:47 +01:00
Alejandro Celaya
bb231e668b Registered middleware generating request ID 2020-02-19 18:58:25 +01:00
Alejandro Celaya
f53fa5c90f Merge pull request #660 from acelaya-forks/feature/short-codes-length
Feature/short codes length
2020-02-18 20:42:37 +01:00
Alejandro Celaya
1f3e0d1f73 Updated changelog 2020-02-18 20:35:41 +01:00
Alejandro Celaya
33a404f051 Updated CLI command to create short URLs so that it respects configs for short code length 2020-02-18 20:34:48 +01:00
Alejandro Celaya
51e130c7a0 Added env var that can be used to define default short codes length on docker image 2020-02-18 19:34:01 +01:00
Alejandro Celaya
343ee04acb Created middleware which injects default short code length from config when a value was not explicitly provided 2020-02-18 19:21:34 +01:00
Alejandro Celaya
9372d1739a Enforced short URLs length to be 4 at least 2020-02-18 18:57:24 +01:00
Alejandro Celaya
13555366e3 Short code lengths can now be customized 2020-02-18 18:54:40 +01:00
Alejandro Celaya
8162dafe16 Added separator in readme before MaxMind reference 2020-02-16 12:18:23 +01:00
Alejandro Celaya
0b6602b275 Merge pull request #659 from acelaya-forks/feature/phpunit9
Feature/phpunit9
2020-02-15 21:21:00 +01:00
Alejandro Celaya
2cf9f64e8e Updated changelog 2020-02-15 21:14:55 +01:00
Alejandro Celaya
37c0a813db Updated to PHPUnit 9 2020-02-15 21:14:14 +01:00
Alejandro Celaya
a9269811dc Added command to run api tests with code coverage 2020-02-15 20:55:04 +01:00
Alejandro Celaya
0b353737ea Merge pull request #658 from acelaya-forks/feature/mssql
Feature/mssql
2020-02-15 20:39:20 +01:00
Alejandro Celaya
a3fc1513e1 Updated Installer to include the one supporting MsSQL 2020-02-15 20:28:32 +01:00
Alejandro Celaya
5886d73093 Documented support for Microsoft SQL Server 2020-02-14 19:55:24 +01:00
Alejandro Celaya
12adce9ac2 Updated changelog 2020-02-14 19:51:58 +01:00
Alejandro Celaya
d8cbf0512b Added missing env var which fixes issues with mssql driver on alpine 2020-02-14 19:44:11 +01:00
Alejandro Celaya
2bb2c2cde3 Documented how to use mssql with the docker image 2020-02-14 19:27:21 +01:00
Alejandro Celaya
27fd9c5988 Added MSSQL driver to prod docker image 2020-02-14 19:20:54 +01:00
Alejandro Celaya
542673fcb0 Updated development docker images 2020-02-14 19:11:29 +01:00
Alejandro Celaya
e60d80bb16 Merge pull request #655 from shlinkio/develop
Release v2.0.5
2020-02-09 18:07:55 +01:00
Alejandro Celaya
bb9e57fa8b Added support for mssql on dev env 2020-02-09 18:01:11 +01:00
Alejandro Celaya
1d4bea68af Updated changelog 2020-02-09 17:59:01 +01:00
Alejandro Celaya
d2f9f5fd5e Merge pull request #654 from acelaya-forks/feature/domain-docs
Feature/domain docs
2020-02-08 19:02:39 +01:00
Alejandro Celaya
f13c3364eb Updated changelog 2020-02-08 18:52:48 +01:00
Alejandro Celaya
ac04bedead Documented how Shlink behaves when using multiple domains 2020-02-08 18:52:02 +01:00
Alejandro Celaya
67a66cefa6 Merge pull request #653 from acelaya-forks/feature/remove-default-domain-from-body
Feature/remove default domain from body
2020-02-08 12:38:20 +01:00
Alejandro Celaya
43db066cb4 Updated changelog 2020-02-08 12:31:25 +01:00
Alejandro Celaya
faec758fba Added test to ensure default domain is ignored if provided when creatin a short URL 2020-02-08 12:30:47 +01:00
Alejandro Celaya
ccec6e03aa Updated middleware which drops default domain so that it is capable of doing it from parsed body too 2020-02-08 12:22:07 +01:00
Alejandro Celaya
3f08b38558 Merge pull request #652 from acelaya-forks/feature/fix-logs-permissions
Feature/fix logs permissions
2020-02-08 11:58:09 +01:00
Alejandro Celaya
1ee5f64738 Updated changelog 2020-02-08 11:51:39 +01:00
Alejandro Celaya
d22169803f Ensured any user can write in generated log files 2020-02-08 11:50:25 +01:00
Alejandro Celaya
57807c4360 Merge pull request #647 from shlinkio/develop
Release v2.0.4
2020-02-02 20:23:09 +01:00
Alejandro Celaya
6e1d07b0cc Merge pull request #646 from acelaya-forks/feature/search-on-domains
Feature/search on domains
2020-02-02 20:14:27 +01:00
Alejandro Celaya
0c0349fa39 Fixed version on changelog 2020-02-02 20:09:30 +01:00
Alejandro Celaya
8d8a0f2484 Updated changelog 2020-02-02 20:08:22 +01:00
Alejandro Celaya
8ff913aaf2 Ensured search terms are applied to the domain too 2020-02-02 20:07:19 +01:00
Alejandro Celaya
f7d54abb2b Merge pull request #645 from acelaya-forks/feature/multi-domain-fixes
Feature/multi domain fixes
2020-02-02 19:28:21 +01:00
Alejandro Celaya
ce990c67e3 Fixed coding styles 2020-02-02 19:19:35 +01:00
Alejandro Celaya
907b8453c6 Updated changelog 2020-02-02 19:16:53 +01:00
Alejandro Celaya
8a0ba11f79 Added one more test case for not found URLs on API tests 2020-02-02 19:15:14 +01:00
Alejandro Celaya
0c1ecd3caa Created DropDefaultDomainFromQueryMiddlewareTest 2020-02-02 19:13:32 +01:00
Alejandro Celaya
c07c37f7bd Created middleware to drop domain from query when it is the default one 2020-02-02 19:03:43 +01:00
Alejandro Celaya
fe652c67f4 Covered with API tests getting invalid short URLs by short code and domain 2020-02-02 13:15:08 +01:00
Alejandro Celaya
297985cf01 Ensured trying to fetch a short URL for any operation through the API results in 404 if it does not match with porovided domain 2020-02-02 12:58:26 +01:00
Alejandro Celaya
10f79ec01d Created new repository method which will look for short URLs without doing domain fallback 2020-02-02 12:44:35 +01:00
Alejandro Celaya
e87d4d61bc Added API test for editing tags with and without domain 2020-02-02 10:53:49 +01:00
Alejandro Celaya
e58f2a384e Added API test for visits with and without domain 2020-02-02 10:46:38 +01:00
Alejandro Celaya
881002634a Added API tests for short URL deletion with domain 2020-02-02 10:28:10 +01:00
Alejandro Celaya
aa80c2bb82 Updated API tests so that fixture short URLs are created with matching short codes and different domains 2020-02-02 09:51:17 +01:00
Alejandro Celaya
75cd9774b7 Added optional domain query param to documentation for all rest endpoints that need it 2020-02-02 09:15:43 +01:00
Alejandro Celaya
1a8e4cdfd7 Exposed domain on short URLs 2020-02-02 08:57:04 +01:00
Alejandro Celaya
6858dc4785 Updated setting short URL tags so that it accepts providing the domain 2020-02-01 22:59:21 +01:00
Alejandro Celaya
5d1d9dcac3 Allowed domain to be provided when editing short URL meta 2020-02-01 22:54:21 +01:00
Alejandro Celaya
732bb06c62 Updated short URL deletion so that it accepts the domain 2020-02-01 18:06:50 +01:00
Alejandro Celaya
5f00d8b732 Added domain flag to GetVisitsCommand 2020-02-01 17:56:43 +01:00
Alejandro Celaya
a3ff545d43 Improved VisitsRepositoryTest to cover fetching visits for URL with domain 2020-02-01 17:44:37 +01:00
Alejandro Celaya
279bd12a2d Ensured domain can be passed when fetching visits for a short URL 2020-02-01 17:34:16 +01:00
Alejandro Celaya
1b2a0d674f Fixed correct short URL being tracked when domain exists 2020-02-01 13:03:48 +01:00
Alejandro Celaya
fd82de31c0 Fixed the way ShortUrlIdentifier is created from requests, on different request scopes 2020-02-01 12:54:10 +01:00
Alejandro Celaya
327d35fe57 Created DTO used to transfer props needed to uniquely identify a short URL 2020-02-01 11:47:01 +01:00
Alejandro Celaya
e18187f04e Merge pull request #636 from acelaya-forks/feature/postgres-schema-support
Feature/postgres schema support
2020-01-29 11:02:23 +01:00
Alejandro Celaya
bd2f488e2c Updated entity mappings so that schema an table prefixes can be eventually provided 2020-01-29 10:53:06 +01:00
Alejandro Celaya
96350c8b8f Updated entities mapping config so that they return a function 2020-01-29 10:06:42 +01:00
Alejandro Celaya
a737eed5c5 Merge pull request #634 from acelaya-forks/feature/simplify-error-logs
Updated to shlink-common 2.6
2020-01-28 18:23:37 +01:00
Alejandro Celaya
9b2ccaeb7b Updated to shlink-common 2.6 2020-01-28 18:11:39 +01:00
Alejandro Celaya
304979273f Merge pull request #633 from acelaya-forks/feature/list-filtering-dto
Feature/list filtering dto
2020-01-28 13:05:24 +01:00
Alejandro Celaya
7add41d560 Ensured BC on dates for short urls params 2020-01-28 12:57:21 +01:00
Alejandro Celaya
51ebe57ac8 Updated changelog 2020-01-28 12:12:50 +01:00
Alejandro Celaya
6ff5a532ea Added extra API test covering complex order by for short URL lists 2020-01-28 11:20:48 +01:00
Alejandro Celaya
fccd92497a Added last check on ShortUrlsOrdering which makes sure everything keeps behaving as it used to 2020-01-28 11:17:54 +01:00
Alejandro Celaya
452bfea088 Created DTOs with implicit validation to wrap short URLs lists params 2020-01-28 10:49:55 +01:00
Alejandro Celaya
240d2588f9 Extracted some private functions ase helper global functions 2020-01-28 09:41:48 +01:00
Alejandro Celaya
eca7800487 Merge pull request #632 from shlinkio/develop
Release v2.0.3
2020-01-27 11:44:43 +01:00
Alejandro Celaya
b9e58b9300 Merge pull request #631 from acelaya-forks/feature/permission-denied
Feature/permission denied
2020-01-27 11:37:37 +01:00
Alejandro Celaya
54918db9ef Updated changelog 2020-01-27 11:31:44 +01:00
Alejandro Celaya
b07a603456 Updated dependencies 2020-01-27 11:30:29 +01:00
Alejandro Celaya
4fb2c64fa8 Merge pull request #630 from acelaya-forks/feature/fetch-not-visitable-url
Feature/fetch not visitable url
2020-01-26 20:00:47 +01:00
Alejandro Celaya
258c4102be Updated changelog 2020-01-26 19:55:03 +01:00
Alejandro Celaya
b9c7f8e8d4 Added unit tests for ShortUrlresolver 2020-01-26 19:53:18 +01:00
Alejandro Celaya
f32e7cc7c4 Removed tests checking domain logic from ShortUrlRepositoryTest 2020-01-26 19:25:41 +01:00
Alejandro Celaya
4ebd48b2b0 Created new service to resolve short URLs 2020-01-26 19:21:51 +01:00
Alejandro Celaya
f71bd84a20 Merge pull request #629 from acelaya-forks/feature/reset-meta
Feature/reset meta
2020-01-26 09:49:36 +01:00
Alejandro Celaya
33b45eb620 Updated changelog 2020-01-26 09:37:43 +01:00
Alejandro Celaya
1f9a912c04 Added API tests covering the edition of short URL meta with resetted values 2020-01-26 09:29:04 +01:00
Alejandro Celaya
45151cdde6 Standardized how the ShortUrlMeta object is created by exposing a single named constructor 2020-01-26 08:42:51 +01:00
Alejandro Celaya
8ca45eb388 Merge pull request #627 from acelaya-forks/feature/remote-ip-order
Feature/remote ip order
2020-01-24 21:28:39 +01:00
Alejandro Celaya
b7a34a6640 Updated changelog 2020-01-24 21:21:13 +01:00
Alejandro Celaya
8ec686f4e2 Updated order in which headers for remote IP detection are inspected 2020-01-24 21:19:40 +01:00
Alejandro Celaya
43fc655218 Merge pull request #618 from shlinkio/develop
Release v2.0.2
2020-01-12 11:02:10 +01:00
Alejandro Celaya
f5a30c4c2d Merge pull request #617 from acelaya-forks/feature/fix-urls-with-hash
Feature/fix urls with hash
2020-01-12 10:38:25 +01:00
Alejandro Celaya
af1dd78b2c Fixed typo 2020-01-12 10:32:03 +01:00
Alejandro Celaya
fc95986f0e Updated changelog for v2.0.2 2020-01-12 10:30:14 +01:00
Alejandro Celaya
c52794aed6 Replaced standard http_build_query by guzzle's build_query, which keeps params with no value 2020-01-12 10:28:44 +01:00
Alejandro Celaya
15a72e2a88 Updated local config files which were not fulfilling the project's coding standards 2020-01-12 10:06:45 +01:00
Alejandro Celaya
94af588a3c Merge pull request #616 from acelaya-forks/feature/successful-options-req
Feature/successful options req
2020-01-12 09:36:18 +01:00
Alejandro Celaya
0a4f3bc0f5 Updated changelog 2020-01-11 20:38:10 +01:00
Alejandro Celaya
09e3464426 Ensured CrossDomainMiddleware always returns empty responses with success status on OPTIONS requests 2020-01-11 20:36:17 +01:00
Alejandro Celaya
7fcc4ebd57 Merge pull request #613 from shlinkio/develop
Releasing v2.0.1
2020-01-10 19:56:56 +01:00
Alejandro Celaya
b246815529 Merge pull request #612 from acelaya-forks/feature/avoid-nulls
Feature/avoid nulls
2020-01-10 19:51:02 +01:00
Alejandro Celaya
ad1334f289 Created database migration which ensures no nulls are present 2020-01-10 19:44:35 +01:00
Alejandro Celaya
49bccf9a06 Updated changelog 2020-01-10 19:20:44 +01:00
Alejandro Celaya
1a8bf54e8b Merge pull request #611 from acelaya-forks/feature/db-migrate-syntax-error
Feature/db migrate syntax error
2020-01-10 19:17:45 +01:00
Alejandro Celaya
96bb0321eb Updated casting type so that it is dynamic 2020-01-10 19:08:23 +01:00
Alejandro Celaya
37f0abf86f Fixed use of hardcoded quotes on database migration, making it crash on postgres 2020-01-10 18:40:49 +01:00
Alejandro Celaya
f9119a38b3 Updated changelog 2020-01-10 16:04:35 +01:00
Alejandro Celaya
8465a9da31 Merge pull request #608 from acelaya-forks/feature/missing-upgrade+info
Added missing information in upgrading document
2020-01-09 07:48:27 +01:00
Alejandro Celaya
b6b0d09647 Added missing information in upgrading document 2020-01-09 07:37:59 +01:00
Alejandro Celaya
3d2932782d Merge pull request #604 from shlinkio/develop
Release v2
2020-01-08 19:46:53 +01:00
Alejandro Celaya
fa2fede604 Updated docker docs 2020-01-08 19:37:12 +01:00
Alejandro Celaya
3aded3bc5f Updated changelog tagging v2.0.0 2020-01-08 19:17:02 +01:00
Alejandro Celaya
2d4cc912b7 Required proxy-manager 2.6 2020-01-07 18:27:32 +01:00
Alejandro Celaya
c0881f9292 Merge pull request #603 from acelaya-forks/feature/versioned-health
Feature/versioned health
2020-01-07 18:15:17 +01:00
Alejandro Celaya
302a77b9dd Updated changelog 2020-01-07 18:10:09 +01:00
Alejandro Celaya
2b544ad141 Refactored Rest ConfigProvider so that it appends the health action with and without version 2020-01-07 18:07:51 +01:00
Alejandro Celaya
36d5e057d0 Ensured the health action is registered bit with and without version 2020-01-06 23:32:43 +01:00
Alejandro Celaya
96eb6a80e1 Merge pull request #599 from acelaya-forks/feature/update-shlink-packages
Feature/update shlink packages
2020-01-06 23:19:52 +01:00
Alejandro Celaya
9c5f5a46b5 Replaced use of deprecated class by a non-deprecated one 2020-01-06 23:08:14 +01:00
Alejandro Celaya
886f63d3e4 Workarounded doctrine-dbal issue by creating new columns instead of changing column types 2020-01-06 22:57:10 +01:00
Alejandro Celaya
7748dd7cef Ensured latitude and longitude are set as float in DB 2020-01-06 22:31:00 +01:00
Alejandro Celaya
2e0f8067aa Enabled redis config option 2020-01-05 17:04:22 +01:00
Alejandro Celaya
4fadd523f1 Updated config to read redis from config.redis 2020-01-05 16:45:14 +01:00
Alejandro Celaya
abe54c67d8 Updated changelog 2020-01-04 13:21:37 +01:00
Alejandro Celaya
f3f35218c3 Updated to installer v4 2020-01-04 13:18:28 +01:00
Alejandro Celaya
b9eb9cb6d9 Updated all shlink packages but installer to their latest version 2020-01-03 11:38:21 +01:00
Alejandro Celaya
40c014b663 Fixed merge conflicts 2020-01-02 22:58:40 +01:00
Alejandro Celaya
70f1db4e94 Merge pull request #597 from acelaya-forks/hotfix/1.21.1
Updated URL from which GeoLite DB is downloaded
2020-01-02 22:55:45 +01:00
Alejandro Celaya
2fff38d51a Added v1.21.1 to changelog 2020-01-02 22:49:17 +01:00
Alejandro Celaya
32de74b23e Updated URL from which GeoLite DB is downloaded 2020-01-02 22:46:56 +01:00
Alejandro Celaya
c96d24cc0b Merge pull request #595 from acelaya-forks/feature/laminas
Feature/laminas
2020-01-01 21:26:33 +01:00
Alejandro Celaya
824ee55d38 Updated changelog 2020-01-01 21:16:26 +01:00
Alejandro Celaya
86e701dccc Updated local config file 2020-01-01 21:15:23 +01:00
Alejandro Celaya
2151b97bec Project migrated from zend to laminas 2020-01-01 21:13:09 +01:00
Alejandro Celaya
18312b0624 Merge pull request #593 from acelaya-forks/feature/coding-standard-2.1
Feature/coding standard 2.1
2020-01-01 20:56:47 +01:00
Alejandro Celaya
a67cf4fb85 Updated changelog 2020-01-01 20:49:29 +01:00
Alejandro Celaya
50100c251e Updated to coding standard v2.1 2020-01-01 20:48:31 +01:00
Alejandro Celaya
1070482629 Merge pull request #591 from acelaya-forks/feature/upgrading-doc
Feature/upgrading doc
2020-01-01 11:31:58 +01:00
Alejandro Celaya
d205405dcc Updated DeprecatedConfigParser to remove the secret key 2020-01-01 11:27:09 +01:00
Alejandro Celaya
16d9c3b93f Updated changelog 2020-01-01 11:19:07 +01:00
Alejandro Celaya
52ecef0311 Created upgrade doc explaining how to upgrade from Shlinnk 1.x to 2.x 2020-01-01 11:17:44 +01:00
Alejandro Celaya
f172146f27 Merge pull request #590 from acelaya-forks/feature/remove-deprecations
Feature/remove deprecations
2019-12-31 23:51:27 +01:00
Alejandro Celaya
ee986912d4 Updated changelog 2019-12-31 23:44:42 +01:00
Alejandro Celaya
0e04968bc2 Changed latitude and longitude to float 2019-12-31 23:37:49 +01:00
Alejandro Celaya
0cf1d8d375 Changed validate_url to have default value of false 2019-12-31 16:30:59 +01:00
Alejandro Celaya
339121fbb1 Removed PathVersionMiddleware as routes without version are not longer supported 2019-12-31 16:26:00 +01:00
Alejandro Celaya
f99ca464de Removed old command aliases 2019-12-31 16:08:08 +01:00
Alejandro Celaya
5c90a7c7a7 Removed remaining deprecated elements 2019-12-31 16:05:02 +01:00
Alejandro Celaya
434b56fa8c Removed several deprecated components 2019-12-31 15:38:37 +01:00
Alejandro Celaya
78b484e657 Deleted everything related with previews generation 2019-12-31 13:40:38 +01:00
Alejandro Celaya
9d36534230 Merge pull request #589 from acelaya-forks/feature/problem-details-upgrade
Updated to problem-details 1.1, removing custom code
2019-12-30 22:48:24 +01:00
Alejandro Celaya
bd6243b2ac Updated to problem-details 1.1, removing custom code 2019-12-30 22:42:29 +01:00
Alejandro Celaya
416857e129 Updated changelog 2019-12-29 23:32:33 +01:00
Alejandro Celaya
8cd81d0441 Merge pull request #578 from acelaya-forks/feature/php-74-requirement
Feature/php 74 requirement
2019-12-29 23:29:42 +01:00
Alejandro Celaya
bf0d9ab7d9 Removed not needed null check 2019-12-29 23:19:00 +01:00
Alejandro Celaya
bfeb915cd2 Replaced regular callbacks by arrow functions when possible 2019-12-29 23:16:55 +01:00
Alejandro Celaya
a830420d75 Added property types to all non-deprecated classes 2019-12-29 22:48:40 +01:00
Alejandro Celaya
b904c6d00d Added property types to some classes 2019-12-29 22:27:00 +01:00
Alejandro Celaya
050050a9eb Updated base image for all docerfiles to use PHP 7.4.1 2019-12-29 16:34:43 +01:00
Alejandro Celaya
9e48dc4137 Updated more references to now unsupported PHP versions 2019-12-29 16:09:05 +01:00
Alejandro Celaya
2bd9bb233c Updated development assets and required PHP 7.4 2019-12-29 16:09:05 +01:00
Alejandro Celaya
e92d437456 Merge pull request #588 from shlinkio/develop
Develop to master
2019-12-29 15:55:01 +01:00
Alejandro Celaya
fd6151040e Merge pull request #587 from acelaya-forks/feature/visit-webhook
Feature/visit webhook
2019-12-29 14:36:40 +01:00
Alejandro Celaya
b4e3dd7b4e Updated changelog with v1.21.0 2019-12-29 14:15:50 +01:00
Alejandro Celaya
8667544b3a Updated to installer v3.3 2019-12-29 14:09:51 +01:00
Alejandro Celaya
664569a52b Added visits_webhooks option to SimplifiedConfigParser 2019-12-28 16:42:21 +01:00
Alejandro Celaya
3c9da80962 Documented how to provide visits webhooks to docker image via env vars 2019-12-28 16:35:16 +01:00
Alejandro Celaya
583985e7ce Moved VisitLocated as a regular event, since async tasks cannot trigger other async tasks 2019-12-28 13:50:41 +01:00
Alejandro Celaya
b17bcb6c93 Updated LocateShortUrlVisit so that it dispatches a VisitLocated event 2019-12-28 13:07:11 +01:00
Alejandro Celaya
4886825564 Improved NotifyVisitToWebHooksTest to kill more mutants 2019-12-28 10:43:13 +01:00
Alejandro Celaya
79cd3ba912 Created NotifyVisitToWebhooksTest 2019-12-27 20:32:21 +01:00
Alejandro Celaya
21a3d4b66b Updated NotifyVisitToWebHooks so that it waits for all requests to finish 2019-12-27 17:07:20 +01:00
Alejandro Celaya
562b0a0868 Used PSR3 logger preprocessor format instead of sprintf when possible 2019-12-27 16:15:14 +01:00
Alejandro Celaya
25243a10ec Moved common bootstrapping code to run.php script 2019-12-27 14:02:43 +01:00
Alejandro Celaya
3fdba53995 Added basic implementation for new webhook events 2019-12-27 13:37:59 +01:00
Alejandro Celaya
34d8b396a4 Fixed merge conflicts 2019-12-23 11:17:20 +01:00
Alejandro Celaya
c560e1fda2 Merge pull request #586 from acelaya-forks/hotfix/1.20.3
Hotfix/1.20.3
2019-12-23 11:06:29 +01:00
Alejandro Celaya
3634236214 Fixed some comments 2019-12-23 11:00:38 +01:00
Alejandro Celaya
6de0cba0b0 Updated changelog 2019-12-23 10:50:03 +01:00
Alejandro Celaya
35f5f4851e Moved class alias to container.php to avoid it from being lost after configuration is cached 2019-12-23 10:38:06 +01:00
Alejandro Celaya
3a8f4de3bb Merge pull request #584 from acelaya-forks/feature/guzzle-fix
Feature/guzzle fix
2019-12-21 16:38:34 +01:00
Alejandro Celaya
d67321f187 Removed workarounds from UrlValidator that were required for guzzle 6.5.0 2019-12-21 16:09:29 +01:00
Alejandro Celaya
7c52d0ec19 Required at least guzzle 6.5.1 2019-12-21 16:02:12 +01:00
Alejandro Celaya
ea08ab9758 Merge pull request #583 from acelaya-forks/feature/improve-docs
Updated documentation, adding more sub-sections and trying to explain…
2019-12-19 11:41:36 +01:00
Alejandro Celaya
9c06803a31 Updated documentation, adding more sub-sections and trying to explain everything even better 2019-12-19 11:39:41 +01:00
Alejandro Celaya
9222dcbc2e Merge pull request #581 from acelaya-forks/feature/improve-dockers
Feature/improve dockers
2019-12-18 15:38:03 +01:00
Alejandro Celaya
75b91dc26b Hardcoded different creation dates for fixture short URLs to avoid random API test failures 2019-12-18 15:26:23 +01:00
Alejandro Celaya
4334ea295d Added missing local config 2019-12-18 15:00:39 +01:00
Alejandro Celaya
03eeef7f52 Updated changelog 2019-12-18 14:55:37 +01:00
Alejandro Celaya
0747137679 Added php ini config for docker image 2019-12-18 14:54:48 +01:00
Alejandro Celaya
0152f6fa1a Added dev php config and removed .env dependencies 2019-12-17 21:20:24 +01:00
Alejandro Celaya
9f2ede0b84 Merge pull request #579 from acelaya-forks/feature/readme-logo
Feature/readme logo
2019-12-17 13:46:46 +01:00
Alejandro Celaya
748786d599 Updated project header image 2019-12-17 13:44:22 +01:00
Alejandro Celaya
18f0fb556a Added project logo to readme file 2019-12-17 13:40:08 +01:00
Alejandro Celaya
685b3f86b3 Merge pull request #572 from alesub/date-filters
Date filters for short urls list endpoint
2019-12-17 10:33:46 +01:00
Alejandro Celaya
524914fd35 Updated changelog 2019-12-17 10:14:18 +01:00
Alejandro Celaya
f7d09bf173 Slight refactoring on ListSHortUrlsCommand 2019-12-17 10:11:54 +01:00
Alejandro Celaya
4b113e5781 Added tests covering how orderBy is parsed on ListShortUrlsCommand 2019-12-17 10:06:54 +01:00
Alejandro Celaya
5616579131 Added startDate and endDate params to ListShortUrlsCommand 2019-12-17 09:59:54 +01:00
Alejandro Celaya
8142801f1f Updated ListShortUrlsAction api test so that it covers filtering use cases 2019-12-16 23:03:32 +01:00
Alejandro Celaya
35eeaf4282 Improved repository tests covering fetching and counting filtered short URL lists 2019-12-16 22:17:33 +01:00
Alejandro Celaya
8ad8b08aa4 Improved ListShortUrlsActionTest covering different scenarios in which date ranges are provided 2019-12-16 21:55:48 +01:00
Alejandro Celaya
839ca31821 Ensured date range filtering is also passed to the count function on ShortUrlRepository 2019-12-16 21:46:27 +01:00
Alejandro Celaya
03a92e5556 Fixed trailing method comma which is not compatible with PHP 7.2 2019-12-16 15:22:03 +01:00
Alejandro Celaya
99fd5f937e Fixed existing tests and coding styles 2019-12-16 15:16:18 +01:00
Alejandro Medina
d7ffcd903d Add date filter fields to short urls documentation 2019-12-14 18:42:02 -03:00
Alejandro Medina
a28e7987e6 fixup! Add date range filter to list urls endpoint parameters 2019-12-14 18:32:58 -03:00
Alejandro Medina
f17c46bbed Add date range filter to short url service 2019-12-14 15:10:09 -03:00
Alejandro Medina
27008505e5 Add date range filter to short url service interface 2019-12-14 15:03:39 -03:00
Alejandro Medina
f9ba322547 Add date range filter to list urls endpoint parameters 2019-12-14 13:55:03 -03:00
Alejandro Medina
661efcb51f Add date range filter to short url repository adapter 2019-12-14 12:01:56 -03:00
Alejandro Medina
5928f28699 Add date range filter to short url repository 2019-12-14 11:58:52 -03:00
Alejandro Medina
1183d65184 Add date range filter to short url repository interface 2019-12-14 11:58:08 -03:00
Alejandro Celaya
fd53e49508 Merge pull request #574 from shlinkio/feature/php-7.4-build
Replaced PHP 7.4 snapshot by regular 7.4
2019-12-14 11:47:10 +01:00
Alejandro Celaya
c484e32641 Replaced PHP 7.4 snapshot by regular 7.4 2019-12-14 10:57:29 +01:00
Alejandro Celaya
c1741c99be Merge pull request #571 from acelaya-forks/bugfix/docker-version
Bugfix/docker version
2019-12-09 18:31:08 +01:00
Alejandro Celaya
a03179743d Updated changelog 2019-12-09 18:18:03 +01:00
Alejandro Celaya
83b9160ab1 Updated docker image build script so that it sets shlink version to 'latest' when source branch is develop 2019-12-09 18:16:57 +01:00
Alejandro Celaya
83757ed390 Merge pull request #568 from acelaya-forks/feature/guzzle-update
Updated to guzzle 6.5 and removed custom code
2019-12-07 21:13:17 +01:00
Alejandro Celaya
843e943251 Updated to guzzle 6.5 and removed custom code 2019-12-07 21:01:14 +01:00
Alejandro Celaya
a0a1d3de72 Used stable docker tag on every docker run example 2019-12-07 09:31:46 +01:00
Alejandro Celaya
e5f262869c Improved tag conflict docs and tests 2019-12-06 23:40:54 +01:00
Alejandro Celaya
a070a68a57 Fixed merge conflicts 2019-12-06 23:34:50 +01:00
Alejandro Celaya
03825469ca Merge pull request #559 from acelaya-forks/feature/msi-80
Feature/msi 80
2019-12-01 12:42:57 +01:00
Alejandro Celaya
058cdf7a82 Enforced a min msi of 80% 2019-12-01 12:34:26 +01:00
Alejandro Celaya
7f43890713 Improved CreateShortUrlAction test so that it cover more mutants 2019-12-01 12:26:31 +01:00
Alejandro Celaya
57070ef155 Improved Rest's ConfigProvider test to kill more mutants 2019-12-01 12:04:31 +01:00
Alejandro Celaya
fc5904e743 Improved BodyParserMiddlewareTest to kill more mutants 2019-12-01 10:58:48 +01:00
Alejandro Celaya
46c0620236 More test improvements trying to increase mutation score 2019-12-01 10:47:56 +01:00
Alejandro Celaya
1bf56b658b Improved domain exception tests to cover more possible mutants 2019-12-01 10:24:42 +01:00
Alejandro Celaya
8cc4d3e6d5 Merge pull request #558 from acelaya-forks/feature/monolog2
Feature/monolog2
2019-11-30 18:22:44 +01:00
Alejandro Celaya
3080c49caf Bringing back allowing failures on PHP 7.4 2019-11-30 18:15:09 +01:00
Alejandro Celaya
ed94ec39c4 Updated changelog 2019-11-30 18:09:38 +01:00
Alejandro Celaya
6bcdd5e6c8 Cleaned last beats of to make everything BC 2019-11-30 18:08:30 +01:00
Alejandro Celaya
cf3d763731 Replaced monolog-cascade by MonologFactory 2019-11-30 17:59:04 +01:00
Alejandro Celaya
e558bb17cb Updated dependencies 2019-11-30 17:21:36 +01:00
Alejandro Celaya
5d76a55c46 Updated how monolog handlers are registered so that it is possible to overwrite them via local config 2019-11-30 09:28:30 +01:00
Alejandro Celaya
4401824716 Updated changelog 2019-11-29 19:43:34 +01:00
Alejandro Celaya
df23f20d31 Merge pull request #554 from acelaya-forks/feature/problem-details
Feature/problem details
2019-11-29 19:38:08 +01:00
Alejandro Celaya
6c37905c15 Fixed cross-domain headers being lost when ProblemDetailsMiddleware throws an error 2019-11-29 19:24:04 +01:00
Alejandro Celaya
4685572def Added version param to endpoints 2019-11-29 19:09:03 +01:00
Alejandro Celaya
3cf1657d54 Simplified invalidElements to be a plain list of keys when a ValidationException is cast into a problem details error 2019-11-29 18:55:27 +01:00
Alejandro Celaya
60d3c09da5 Updated API docs to reference the use of application/problem+json 2019-11-28 19:37:31 +01:00
Alejandro Celaya
5055ddf995 Updated CLI commands to just print exception messages when possible 2019-11-28 18:47:34 +01:00
Alejandro Celaya
d83d2f82bd Added more strict checks on API errors tests 2019-11-27 20:48:35 +01:00
Alejandro Celaya
5266743a0c Added as much additional data as possible to exceptions 2019-11-27 20:18:36 +01:00
Alejandro Celaya
fffb2872ef Replaced hardcoded error response by the use of a problem details action 2019-11-26 22:18:55 +01:00
Alejandro Celaya
3b56fc3760 Refactored and fixed unit tests 2019-11-26 22:12:52 +01:00
Alejandro Celaya
5213faa0a1 Converted VerifyAuthenticationException into a problem details exception 2019-11-26 22:03:40 +01:00
Alejandro Celaya
6f4e5175da Converted MissingAuthenticationException into a problem details exception 2019-11-26 21:43:29 +01:00
Alejandro Celaya
f502eb0195 Added new test for the case in which an invalid URL is provided 2019-11-26 21:33:22 +01:00
Alejandro Celaya
509c9fe2e8 Improved AuthenticationMiddleware API tests 2019-11-26 21:29:25 +01:00
Alejandro Celaya
13e795d25d Updated ValidationException's base exception 2019-11-26 20:58:38 +01:00
Alejandro Celaya
a28ef1f176 Converted EntityDoesNotExistException into a problem details exception renamed as TagNotFoundException 2019-11-25 19:15:46 +01:00
Alejandro Celaya
0c5eec7e95 Replaced the use of EntityDoesNotExistException by ShorturlNotFoundException where applicable 2019-11-25 18:54:25 +01:00
Alejandro Celaya
310032e303 Converted DeleteShortUrlException into a problem details exception 2019-11-24 23:56:02 +01:00
Alejandro Celaya
32b3c72bdf Converted ValidationException into a problem details exception 2019-11-24 23:45:40 +01:00
Alejandro Celaya
c1eee2246b Converted NonUniqueSlugException into a problem details exception 2019-11-24 23:32:37 +01:00
Alejandro Celaya
0d7d53ab5b Converted InvalidUrlException into a problem details exception 2019-11-24 23:24:53 +01:00
Alejandro Celaya
2f1de4a162 Renamed InvalidShortCodeException to ShortCodeNotFoundException 2019-11-24 23:15:55 +01:00
Alejandro Celaya
cdd36b6712 Created BackwardsCompatibleProblemDetailsMiddlewareTest 2019-11-24 13:24:52 +01:00
Alejandro Celaya
6f0afe269d Moved InvalidShortCode exception handling to problem details 2019-11-24 12:41:12 +01:00
Alejandro Celaya
09321eaa93 Updated InvalidShortCodeException to implement ProblemDetails 2019-11-23 13:41:07 +01:00
Alejandro Celaya
850259290a Covered new use case on NotFoundRedirectHandlerTest 2019-11-23 10:28:58 +01:00
Alejandro Celaya
1bafe54a75 Split NotFoundHandler into two different middlewares 2019-11-23 10:25:12 +01:00
Alejandro Celaya
89e373f775 Moved NotFoundHandler to ErrorHandler namespace 2019-11-23 10:11:34 +01:00
Alejandro Celaya
74854b3dac Added zend problem details to the project 2019-11-22 19:49:14 +01:00
Alejandro Celaya
4e5ab21a47 Removed whoops dev dependency 2019-11-22 18:03:11 +01:00
Alejandro Celaya
a0510d6a69 Removed content-based-error-handler in preparation for the problem details module 2019-11-22 18:01:38 +01:00
Alejandro Celaya
6ddb60d047 Improved ValidationException to avoid polluting the message with invalid data but keeping it on the string representation 2019-11-21 20:07:57 +01:00
Alejandro Celaya
ad592a563c Updated testing utils library 2019-11-21 19:22:04 +01:00
Alejandro Celaya
b3b67b051d Created API tests for errors when updating tags 2019-11-21 19:03:34 +01:00
Alejandro Celaya
8607d58e18 Created API tests for errors when editting short URL tags 2019-11-21 18:49:55 +01:00
Alejandro Celaya
34e60ec5b8 Created API tests for errors when getting short URL visits 2019-11-20 20:58:16 +01:00
Alejandro Celaya
d044e1a5b7 Created API tests for errors when resolving short URLs 2019-11-20 20:44:03 +01:00
Alejandro Celaya
9096318968 Created API tests for errors when deleting short URLs 2019-11-20 20:38:19 +01:00
Alejandro Celaya
ba6e8c4092 Created API tests for errors when editing a short URL 2019-11-20 20:31:18 +01:00
Alejandro Celaya
98b6dba05d Removed generic error handling from action that will usually be handled by ErrorHandler middleware 2019-11-20 20:21:02 +01:00
Alejandro Celaya
84c4631124 Deleted specific factory by replacing it by ConfigAbstractFactory 2019-11-20 20:18:21 +01:00
Alejandro Celaya
a7d308c585 Merge pull request #553 from acelaya-forks/feature/doctrine-2.7
Updated to latest doctrine versions, solving deprecations
2019-11-20 20:12:02 +01:00
Alejandro Celaya
af0ed6135e Updated to latest doctrine versions, solving deprecations 2019-11-20 20:03:06 +01:00
485 changed files with 13829 additions and 9510 deletions

View File

@@ -8,17 +8,16 @@ data/migrations_template.txt
data/GeoLite2-City.*
data/database.sqlite
data/shlink-tests.db
**/.gitignore
CHANGELOG.md
UPGRADE.md
composer.lock
vendor
docs
indocker
docker-*
php*
infection.json
phpstan.neon
php*xml*
infection.json
**/test*
build*
.github
hooks
**/.*

View File

@@ -1,10 +0,0 @@
# Application
APP_ENV=
SECRET_KEY=
SHORTENED_URL_SCHEMA=
SHORTENED_URL_HOSTNAME=
# Database
DB_USER=
DB_PASSWORD=
DB_NAME=

3
.gitattributes vendored
View File

@@ -5,11 +5,8 @@
/module/CLI/test-resources export-ignore
/module/Core/test export-ignore
/module/Core/test-db export-ignore
/module/PreviewGenerator/test export-ignore
/module/PreviewGenerator/test-db export-ignore
/module/Rest/test export-ignore
/module/Rest/test-api export-ignore
.env.dist export-ignore
.gitattributes export-ignore
.gitignore export-ignore
.phpstorm.meta.php export-ignore

View File

@@ -1,6 +1,7 @@
<!--
Before opening an issue, just take into account that this is a completely free of charge open source project.
I'm always happy to help and provide support, but some understanding will be required.
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 personal 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

@@ -5,9 +5,10 @@ labels: bug
---
<!--
Before opening an issue, just take into account that this is a completely free of charge open source project.
I'm always happy to help and provide support, but some understanding will be required.
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 personal 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.
With that said, please fill in the information requested next. More information might be requested next (like logs or system configs).
@@ -18,7 +19,7 @@ With that said, please fill in the information requested next. More information
* Shlink Version: x.y.z
* PHP Version: x.y.z
* How do you serve Shlink: Self-hosted Apache|Self-hosted nginx|Self-hosted swoole|Docker image
* Database engine used: MySQL|MariaDB|PostgreSQL|SQLite (x.y.z)
* Database engine used: MySQL|MariaDB|PostgreSQL|MicrosoftSQL|SQLite (x.y.z)
#### Summary

View File

@@ -5,9 +5,10 @@ labels: feature
---
<!--
Before opening an issue, just take into account that this is a completely free of charge open source project.
I'm always happy to help and provide support, but some understanding will be required.
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 personal 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.
With that said, please fill in the information requested next. More information might be requested next (like logs or system configs).

View File

@@ -5,9 +5,10 @@ labels: question
---
<!--
Before opening an issue, just take into account that this is a completely free of charge open source project.
I'm always happy to help and provide support, but some understanding will be required.
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 personal 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.
With that said, please fill in the information requested next. More information might be requested next (like logs or system configs).
@@ -18,7 +19,7 @@ With that said, please fill in the information requested next. More information
* Shlink Version: x.y.z
* PHP Version: x.y.z
* How do you serve Shlink: Self-hosted Apache|Self-hosted nginx|Self-hosted swoole|Docker image
* Database engine used: MySQL|MariaDB|PostgreSQL|SQLite (x.y.z)
* Database engine used: MySQL|MariaDB|PostgreSQL|MicrosoftSQL|SQLite (x.y.z)
#### Summary

View File

@@ -0,0 +1,24 @@
name: Build docker image
on:
push:
branches:
- develop
tags:
- 'v*'
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Install buildx
id: buildx
uses: crazy-max/ghaction-docker-buildx@v1
with:
buildx-version: latest
- name: Login to docker hub
run: echo "${{ secrets.DOCKER_PASSWORD }}" | docker login -u "${{ secrets.DOCKER_USERNAME }}" --password-stdin
- name: Build the image
run: bash ./docker/build

30
.github/workflows/publish-release.yml vendored Normal file
View File

@@ -0,0 +1,30 @@
name: Publish release
on:
push:
tags:
- 'v*'
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Use PHP 7.4
uses: shivammathur/setup-php@v2
with:
php-version: '7.4' # Publish release with lowest supported PHP version
tools: composer
extensions: swoole-4.5.5
- name: Generate release assets
run: ./build.sh ${GITHUB_REF#refs/tags/v}
- name: Publish release with assets
uses: docker://antonyurchenko/git-release:latest
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
ALLOW_TAG_PREFIX: "true"
ALLOW_EMPTY_CHANGELOG: "true"
with:
args: |
build/shlink_*_dist.zip

3
.gitignore vendored
View File

@@ -1,10 +1,9 @@
.idea
build
!hooks/build
!docker/build
composer.lock
composer.phar
vendor/
.env
data/database.sqlite
data/shlink-tests.db
data/GeoLite2-City.mmdb

View File

@@ -2,7 +2,7 @@
namespace PHPSTORM_META;
use Psr\Container\ContainerInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
use Laminas\ServiceManager\ServiceLocatorInterface;
/**
* PhpStorm Container Interop code completion

View File

@@ -6,6 +6,9 @@ checks:
code_rating: true
duplication: true
build:
dependencies:
override:
- composer install --no-interaction --no-scripts --ignore-platform-reqs
nodes:
analysis:
tests:

View File

@@ -1,63 +1,56 @@
dist: bionic
language: php
branches:
only:
- /.*/
php:
- '7.2'
- '7.3'
- '7.4snapshot'
matrix:
allow_failures:
- php: '7.4snapshot'
services:
- mysql
- postgresql
- docker
cache:
directories:
- $HOME/.composer/cache/files
jobs:
fast_finish: true
allow_failures:
- php: 'nightly'
include:
- name: "CI - 8.0"
php: 'nightly'
env:
- COMPOSER_FLAGS='--ignore-platform-reqs'
- name: "CI - 7.4"
php: '7.4'
env:
- COMPOSER_FLAGS=''
before_install:
- echo 'extension = apcu.so' >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini
- yes | pecl install swoole
- phpenv config-rm xdebug.ini || return 0
- sudo ./data/infra/ci/install-ms-odbc.sh
- docker-compose -f docker-compose.yml -f docker-compose.ci.yml up -d shlink_db_ms shlink_db shlink_db_postgres shlink_db_maria
- yes | pecl install pdo_sqlsrv-5.9.0preview1 swoole-4.5.5 pcov
install:
- composer self-update
- composer install --no-interaction --prefer-dist
- composer install --no-interaction --prefer-dist $COMPOSER_FLAGS
before_script:
- mysql -e 'CREATE DATABASE shlink_test;'
- psql -c 'create database shlink_test;' -U postgres
- docker-compose exec shlink_db_ms /opt/mssql-tools/bin/sqlcmd -S localhost -U sa -P 'Passw0rd!' -Q "CREATE DATABASE shlink_test;"
- mkdir build
- export DOCKERFILE_CHANGED=$(git diff ${TRAVIS_COMMIT_RANGE:-origin/master} --name-only | grep Dockerfile)
- export DOCKERFILE_CHANGED=$(git diff ${TRAVIS_COMMIT_RANGE:-origin/main} --name-only | grep Dockerfile)
script:
- composer ci
- if [[ ! -z "$DOCKERFILE_CHANGED" && "${TRAVIS_PHP_VERSION}" == "7.2" ]]; then docker build -t shlink-docker-image:temp . ; fi
- bin/test/run-api-tests.sh
- if [[ ! -z "${DOCKERFILE_CHANGED}" && "${TRAVIS_PHP_VERSION}" == "7.4" ]]; then docker build -t shlink-docker-image:temp . ; fi
after_success:
- rm -f build/clover.xml
- phpdbg -qrr vendor/bin/phpcov merge build --clover build/clover.xml
- wget https://phar.phpunit.de/phpcov-7.0.2.phar
- php phpcov-7.0.2.phar merge build --clover build/clover.xml
- wget https://scrutinizer-ci.com/ocular.phar
- php ocular.phar code-coverage:upload --format=php-clover build/clover.xml
# Before deploying, build dist file for current travis tag
before_deploy:
- rm -f ocular.phar
- ./build.sh ${TRAVIS_TAG#?}
deploy:
- provider: releases
api_key:
secure: a9dbZchocqeuOViwUeNH54bQR5Sz7rEYXx5b9WPFtnFn9LGKKUaLbA2U91UQ9QKPrcTpsALubUYbw2CnNmvCwzaY+R8lCD3gkU4ohsEnbpnw3deOeixI74sqBHJAuCH9FSaRDGILoBMtUKx2xlzIymFxkIsgIukkGbdkWHDlRWY3oTUUuw1SQ2Xk9KDsbJQtjIc1+G/O6gHaV4qv/R9W8NPmJExKTNDrAZbC1vIUnxqp4UpVo1hst8qPd1at94CndDYM5rG+7imGbdtxTxzamt819qdTO1OfvtctKawNAm7YXZrrWft6c7gI6j6SI4hxd+ZrrPBqbaRFHkZHjnNssO/yn4SaOHFFzccmu0MzvpPCf0qWZwd3sGHVYer1MnR2mHYqU84QPlW3nrHwJjkrpq3+q0JcBY6GsJs+RskHNtkMTKV05Iz6QUI5YZGwTpuXaRm036SmavjGc4IDlMaYCk/NmbB9BKpthJxLdUpczOHpnjXXHziotWD6cfEnbjU3byfD8HY5WrxSjsNT7SKmXN3hRof7bk985ewQVjGT42O3NbnfnqjQQWr/B7/zFTpLR4f526Bkq12CdCyf5lvrbq+POkLVdJ+uFfR7ds248Ue/jBQy6kM1tWmKF9QiwisFlA84eQ4CW3I93Rp97URv+AQa9zmbD0Ve3Udp+g6nF5I=
file: "./build/shlink_${TRAVIS_TAG#?}_dist.zip"
skip_cleanup: true
on:
tags: true
php: '7.2'

File diff suppressed because it is too large Load Diff

136
CONTRIBUTING.md Normal file
View File

@@ -0,0 +1,136 @@
# Contributing
This file will guide you through the process of getting to project up and running, in case you want to provide coding contributions.
You will also see how to ensure the code fulfills the expected code checks, and how to create a pull request.
## System dependencies
The project provides all its dependencies as docker containers through a docker-compose configuration.
Because of this, the only actual dependencies are [docker](https://docs.docker.com/get-docker/) and [docker-compose](https://docs.docker.com/compose/install/).
## Setting up the project
The first thing you need to do is fork the repository, and clone it in your local machine.
Then you will have to follow these steps:
* Copy all files with `.local.php.dist` extension from `config/autoload` by removing the dist extension.
For example the `common.local.php.dist` file should be copied as `common.local.php`.
* 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.
It will also create some empty databases and install the project dependencies with composer.
* Run `./indocker bin/cli db:create` to create the initial database.
* Run `./indocker bin/cli db:migrate` to get database migrations up to date.
* Run `./indocker bin/cli api-key:generate` to get your first API key generated.
Once you finish this, you will have the project exposed in ports `8000` through nginx+php-fpm and `8080` through swoole.
> Note: The `indocker` shell script is a helper used to run commands inside the main docker container.
## Project structure
This project is structured as a modular application, using [laminas/laminas-config-aggregator](https://github.com/laminas/laminas-config-aggregator) to merge the configuration provided by every module.
All modules are inside the `module` folder, and each one has its own `src`, `test` and `config` folders, with the source code, tests and configuration. They also have their own `ConfigProvider` class, which is consumed by the config aggregator.
This is a simplified version of the project structure:
```
shlink
├── bin
│   ├── cli
│   ├── install
│   └── update
├── config
│   ├── autoload
│   ├── params
│   ├── config.php
│   └── container.php
├── data
│   ├── cache
│   ├── locks
│   ├── log
│   ├── migrations
│   └── proxies
├── docs
│   ├── async-api
│   └── swagger
├── module
│   ├── CLI
│   ├── Core
│   └── Rest
├── public
├── composer.json
└── README.md
```
The purposes of every folder are:
* `bin`: It contains the CLI tools. The `cli` one is the main entry point to run shlink from the command line, while `install` and `update` are helper tools used to install and update shlink when not using the docker image.
* `config`: Contains application-wide configurations, which are later merged with the ones provided by every module.
* `data`: Common runtime-generated git-ignored assets, like logs, caches, etc.
* `docs`: Any project documentation is stored here, like API spec definitions.
* `module`: Contains a subfolder for every module in the project. Modules contain the source code, tests and configurations for every context in the project.
* `public`: Few assets (like `favicon.ico` or `robots.txt`) and the web entry point are stored here. This web entry point is not used when serving the app with swoole.
## Project tests
In order to ensure stability and no regressions are introduced while developing new features, this project has different types of tests.
* **Unit tests**: These are the simplest to run, and usually test individual pieces of code, replacing any external dependency by mocks.
The code coverage of unit tests is pretty high, and only entity repositories are excluded because of their nature.
* **Database tests**: These are integration tests that run against a real database, and only cover entity repositories.
Its purpose is to verify all the database queries behave as expected and return what's expected.
The project provides some tooling to run them against any of the supported database engines.
* **API tests**: These are E2E tests that spin up an instance of the app and test it from the outside, by interacting with the REST API.
These are the best tests to catch regressions, and to verify everything interacts as expected.
They use MySQL as the database engine, and include some fixtures that ensure the same data exists at the beginning of the execution.
* **CLI tests**: *TBD. Once included, its purpose will be the same as API tests, but running through the command line*
Depending on the kind of contribution, maybe not all kinds of tests are needed, but the more you provide, the better.
## Running code checks
* Run `./indocker composer cs` to check coding styles are fulfilled.
* Run `./indocker composer cs:fix` to fix coding styles (some may not be fixable from the CLI)
* Run `./indocker composer stan` to statically analyze the code with [phpstan](https://phpstan.org/). This tool is the closest to "compile" PHP and verify everything would work as expected.
* Run `./indocker composer test:unit` to run the unit tests.
* Run `./indocker composer test:db` to run the database integration tests.
This command runs the same test suite against all supported database engines. If you just want to run one of them, you can add one of `:sqlite`, `:mysql`, `:maria`, `:postgres`, `:mssql` at the end of the command.
For example, `test:db:postgres`.
* Run `./indocker composer test:api` to run API E2E tests. For these, the MySQL database engine is used.
* Run `./indocker composer infect:test` ti run both unit and database tests (over sqlite) and then apply mutations to them with [infection](https://infection.github.io/).
* Run `./indocker composer ci` to run all previous commands together. This command is run during the project's continuous integration.
> Note: Due to some limitations in the tooling used by shlink, the testing databases need to exist beforehand, both for db and api tests (except sqlite).
>
> However, they just need to be created empty, with no tables. Also, once created, they are automatically reset before every new execution.
>
> The testing database is always called `shlink_test`. You can create it using the database client of your choice. [DBeaver](https://dbeaver.io/) is a good multi-platform desktop database client which supports all the engines supported by shlink.
## Pull request process
In order to provide pull requests to this project, you should always start by creating a new branch, where you will make all desired changes.
The base branch should always be `develop`, and the target branch for the pull request should also be `develop`.
Before your branch can be merged, all the checks described in [Running code checks](#running-code-checks) have to be passing. You can verify that manually by running `./indocker composer ci`, or wait for the build to be run automatically after the pull request is created.

View File

@@ -1,15 +1,15 @@
FROM php:7.3.11-alpine3.10
LABEL maintainer="Alejandro Celaya <alejandro@alejandrocelaya.com>"
FROM php:7.4.11-alpine3.12 as base
ARG SHLINK_VERSION=1.20.0
ARG SHLINK_VERSION=2.3.0
ENV SHLINK_VERSION ${SHLINK_VERSION}
ENV SWOOLE_VERSION 4.4.12
ENV COMPOSER_VERSION 1.9.1
ENV SWOOLE_VERSION 4.5.5
ENV LC_ALL "C"
WORKDIR /etc/shlink
# Install required PHP extensions
RUN \
# Install mysl and calendar
# Install mysql and calendar
docker-php-ext-install -j"$(nproc)" pdo_mysql calendar && \
# Install sqlite
apk add --no-cache sqlite-libs sqlite-dev && \
@@ -22,28 +22,48 @@ RUN \
docker-php-ext-install -j"$(nproc)" intl && \
# Install zip and gd
apk add --no-cache libzip-dev zlib-dev libpng-dev && \
docker-php-ext-install -j"$(nproc)" zip gd
docker-php-ext-install -j"$(nproc)" zip gd && \
# Install gmp
apk add --no-cache gmp-dev && \
docker-php-ext-install -j"$(nproc)" gmp
# Install sqlsrv driver
RUN if [ $(uname -m) == "x86_64" ]; then \
wget https://download.microsoft.com/download/e/4/e/e4e67866-dffd-428c-aac7-8d28ddafb39b/msodbcsql17_17.5.1.1-1_amd64.apk && \
apk add --allow-untrusted msodbcsql17_17.5.1.1-1_amd64.apk && \
apk add --no-cache --virtual .phpize-deps ${PHPIZE_DEPS} unixodbc-dev && \
pecl install pdo_sqlsrv && \
docker-php-ext-enable pdo_sqlsrv && \
apk del .phpize-deps && \
rm msodbcsql17_17.5.1.1-1_amd64.apk ; \
fi
# Install swoole
# First line fixes an error when installing pecl extensions. Found in https://github.com/docker-library/php/issues/233
RUN apk add --no-cache --virtual .phpize-deps ${PHPIZE_DEPS} && \
pecl install swoole-${SWOOLE_VERSION} && \
docker-php-ext-enable swoole && \
apk del .phpize-deps
# Install shlink
FROM base as builder
COPY . .
RUN rm -rf ./docker && \
wget https://getcomposer.org/download/${COMPOSER_VERSION}/composer.phar && \
COPY --from=composer:2 /usr/bin/composer ./composer.phar
RUN apk add --no-cache git && \
php composer.phar install --no-dev --optimize-autoloader --prefer-dist --no-progress --no-interaction && \
php composer.phar clear-cache && \
rm composer.*
rm -r docker composer.* && \
sed -i "s/%SHLINK_VERSION%/${SHLINK_VERSION}/g" config/autoload/app_options.global.php
# Add shlink to the path to ease running it after container is created
# Prepare final image
FROM base
LABEL maintainer="Alejandro Celaya <alejandro@alejandrocelaya.com>"
COPY --from=builder /etc/shlink .
RUN ln -s /etc/shlink/bin/cli /usr/local/bin/shlink
RUN sed -i "s/%SHLINK_VERSION%/${SHLINK_VERSION}/g" config/autoload/app_options.global.php
# Expose swoole port
# Expose default swoole port
EXPOSE 8080
# Expose params config dir, since the user is expected to provide custom config from there
@@ -52,5 +72,6 @@ VOLUME /etc/shlink/config/params
# 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/
ENTRYPOINT ["/bin/sh", "./docker-entrypoint.sh"]

View File

@@ -1,6 +1,6 @@
The MIT License (MIT)
Copyright (c) 2016-2019 Alejandro Celaya
Copyright (c) 2016-2020 Alejandro Celaya
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

431
README.md
View File

@@ -1,37 +1,56 @@
# Shlink
![Shlink](https://raw.githubusercontent.com/shlinkio/shlink.io/main/public/images/shlink-hero.png)
[![Build Status](https://img.shields.io/travis/shlinkio/shlink.svg?style=flat-square)](https://travis-ci.org/shlinkio/shlink)
[![Code Coverage](https://img.shields.io/scrutinizer/coverage/g/shlinkio/shlink.svg?style=flat-square)](https://scrutinizer-ci.com/g/shlinkio/shlink/?branch=master)
[![Scrutinizer Code Quality](https://img.shields.io/scrutinizer/g/shlinkio/shlink.svg?style=flat-square)](https://scrutinizer-ci.com/g/shlinkio/shlink/?branch=master)
[![Build Status](https://img.shields.io/travis/com/shlinkio/shlink.svg?style=flat-square)](https://travis-ci.com/shlinkio/shlink)
[![Code Coverage](https://img.shields.io/scrutinizer/coverage/g/shlinkio/shlink.svg?style=flat-square)](https://scrutinizer-ci.com/g/shlinkio/shlink/)
[![Scrutinizer Code Quality](https://img.shields.io/scrutinizer/g/shlinkio/shlink.svg?style=flat-square)](https://scrutinizer-ci.com/g/shlinkio/shlink/)
[![Latest Stable Version](https://img.shields.io/github/release/shlinkio/shlink.svg?style=flat-square)](https://packagist.org/packages/shlinkio/shlink)
[![License](https://img.shields.io/github/license/shlinkio/shlink.svg?style=flat-square)](https://github.com/shlinkio/shlink/blob/master/LICENSE)
[![Paypal donate](https://img.shields.io/badge/Donate-paypal-blue.svg?style=flat-square&logo=paypal&colorA=aaaaaa)](https://acel.me/donate)
[![Docker pulls](https://img.shields.io/docker/pulls/shlinkio/shlink.svg?style=flat-square)](https://hub.docker.com/r/shlinkio/shlink/)
[![License](https://img.shields.io/github/license/shlinkio/shlink.svg?style=flat-square)](https://github.com/shlinkio/shlink/blob/main/LICENSE)
[![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 custom domain.
> This document references Shlink 2.x. If you are using an older version and want to upgrade, follow the [UPGRADE](UPGRADE.md) doc.
> 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.
## Table of Contents
- [Installation](#installation)
- [Download](#download)
- [Configure](#configure)
- [Serve](#serve)
- [Bonus](#bonus)
- [Update to new version](#update-to-new-version)
- [Using a docker image](#using-a-docker-image)
- [Using shlink](#using-shlink)
- [Shlink CLI Help](#shlink-cli-help)
- [Shlink CLI Help](#shlink-cli-help)
- [Multiple domains](#multiple-domains)
- [Management](#management)
- [Visits](#visits)
- [Special redirects](#special-redirects)
## Installation
First make sure the host where you are going to run shlink fulfills these requirements:
> These are the steps needed to install Shlink if you plan to manually host it.
>
> Alternatively, you can use the official docker image. If that's your intention, jump directly to [Using a docker image](#using-a-docker-image)
* PHP 7.2 or greater with JSON, APCu, intl, curl, PDO and gd extensions enabled.
* MySQL, MariaDB, PostgreSQL or SQLite.
First, make sure the host where you are going to run shlink fulfills these requirements:
* PHP 7.4 or greater with JSON, curl, PDO, intl and gd extensions enabled.
* MySQL, MariaDB, PostgreSQL, Microsoft SQL Server or SQLite.
* The web server of your choice with PHP integration (Apache or Nginx recommended).
Then, you will need a built version of the project. There are a few ways to get it.
### Download
In order to run Shlink, you will need a built version of the project. There are two ways to get it.
* **Using a dist file**
The easiest way to install shlink is by using one of the pre-bundled distributable packages.
Just go to the [latest version](https://github.com/shlinkio/shlink/releases/latest) and download the `shlink_X.X.X_dist.zip` file you will find there.
Go to the [latest version](https://github.com/shlinkio/shlink/releases/latest) and download the `shlink_x.x.x_dist.zip` file you will find there.
Finally, decompress the file in the location of your choice.
@@ -43,183 +62,168 @@ Then, you will need a built version of the project. There are a few ways to get
* Download the [Composer](https://getcomposer.org/download/) PHP package manager inside the project folder.
* Run `./build.sh 1.0.0`, replacing the version with the version number you are going to build (the version number is only used for the generated dist file).
After that, you will have a `shlink_x.x.x_dist.zip` dist file inside the `build` directory.
After that, you will have a `shlink_x.x.x_dist.zip` dist file inside the `build` directory, that you need to decompress in the location fo your choice.
This is the process used when releasing new shlink versions. After tagging the new version with git, the Github release is automatically created by [travis](https://travis-ci.org/shlinkio/shlink), attaching generated dist file to it.
> This is the process used when releasing new shlink versions. After tagging the new version with git, the Github release is automatically created by [travis](https://travis-ci.com/shlinkio/shlink), attaching the generated dist file to it.
Despite how you built the project, you are going to need to install it now, by following these steps:
### Configure
* If you are going to use MySQL, MariaDB or PostgreSQL, create an empty database with the name of your choice.
Despite how you built the project, you now need to configure it, by following these steps:
* If you are going to use MySQL, MariaDB, PostgreSQL or Microsoft SQL Server, create an empty database with the name of your choice.
* Recursively grant write permissions to the `data` directory. Shlink uses it to cache some information.
* Setup the application by running the `bin/install` script. It is a command line tool that will guide you through the installation process. **Take into account that this tool has to be run directly on the server where you plan to host Shlink. Do not run it before uploading/moving it there.**
* Expose shlink to the web, either by using a traditional web server + fast CGI approach, or by using a [swoole](https://www.swoole.co.uk/) non-blocking server.
* **Using a web server:**
For example, assuming your domain is doma.in and shlink is in the `/path/to/shlink` folder, these would be the basic configurations for Nginx and Apache.
*Nginx:*
```nginx
server {
server_name doma.in;
listen 80;
root /path/to/shlink/public;
index index.php;
charset utf-8;
location / {
try_files $uri $uri/ /index.php$is_args$args;
}
location ~ \.php$ {
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass unix:/var/run/php/php7.2-fpm.sock;
fastcgi_index index.php;
include fastcgi.conf;
}
location ~ /\.ht {
deny all;
}
}
```
*Apache:*
```apache
<VirtualHost *:80>
ServerName doma.in
DocumentRoot "/path/to/shlink/public"
<Directory "/path/to/shlink/public">
Options FollowSymLinks Includes ExecCGI
AllowOverride all
Order allow,deny
Allow from all
</Directory>
</VirtualHost>
```
* **Using swoole:**
First you need to install the swoole PHP extension with [pecl](https://pecl.php.net/package/swoole), `pecl install swoole`.
Once installed, it's actually pretty easy to get shlink up and running with swoole. Just run `./vendor/bin/zend-expressive-swoole start -d` and you will get shlink running on port 8080.
However, by doing it this way, you are loosing all the access logs, and the service won't be automatically run if the server has to be restarted.
For that reason, you should create a daemon script, in `/etc/init.d/shlink_swoole`, like this one, replacing `/path/to/shlink` by the path to your shlink installation:
```bash
#!/bin/bash
### BEGIN INIT INFO
# Provides: shlink_swoole
# Required-Start: $local_fs $network $named $time $syslog
# Required-Stop: $local_fs $network $named $time $syslog
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Description: Shlink non-blocking server with swoole
### END INIT INFO
SCRIPT=/path/to/shlink/vendor/bin/zend-expressive-swoole\ start
RUNAS=root
PIDFILE=/var/run/shlink_swoole.pid
LOGDIR=/var/log/shlink
LOGFILE=${LOGDIR}/shlink_swoole.log
start() {
if [[ -f "$PIDFILE" ]] && kill -0 $(cat "$PIDFILE"); then
echo 'Shlink with swoole already running' >&2
return 1
fi
echo 'Starting shlink with swoole' >&2
mkdir -p "$LOGDIR"
touch "$LOGFILE"
local CMD="$SCRIPT &> \"$LOGFILE\" & echo \$!"
su -c "$CMD" $RUNAS > "$PIDFILE"
echo 'Shlink started' >&2
}
stop() {
if [[ ! -f "$PIDFILE" ]] || ! kill -0 $(cat "$PIDFILE"); then
echo 'Shlink with swoole not running' >&2
return 1
fi
echo 'Stopping shlink with swoole' >&2
kill -15 $(cat "$PIDFILE") && rm -f "$PIDFILE"
echo 'Shlink stopped' >&2
}
case "$1" in
start)
start
;;
stop)
stop
;;
restart)
stop
start
;;
*)
echo "Usage: $0 {start|stop|restart}"
esac
```
Then run these commands to enable the service and start it:
* `sudo chmod +x /etc/init.d/shlink_swoole`
* `sudo update-rc.d shlink_swoole defaults`
* `sudo update-rc.d shlink_swoole enable`
* `/etc/init.d/shlink_swoole start`
Now again, you can access shlink on port 8080, but this time the service will be automatically run at system start-up, and all access logs will be written in `/var/log/shlink/shlink_swoole.log` (you will probably want to [rotate those logs](https://www.digitalocean.com/community/tutorials/how-to-manage-logfiles-with-logrotate-on-ubuntu-16-04). You can find an example logrotate config file [here](data/infra/examples/shlink-daemon-logrotate.conf)).
* Generate your first API key by running `bin/cli api-key:generate`. You will need the key in order to interact with shlink's API.
* Finally access to [https://app.shlink.io](https://app.shlink.io) and configure your server to start creating short URLs.
**Bonus**
### Serve
There are a couple of time-consuming tasks that shlink expects you to do manually, or at least it is recommended, since it will improve runtime performance.
Once Shlink is configured, you need to expose it to the web, either by using a traditional web server + fast CGI approach, or by using a [swoole](https://www.swoole.co.uk/) non-blocking server.
Those tasks can be performed using shlink's CLI, so it should be easy to schedule them to be run in the background (for example, using cron jobs):
* **Using a web server:**
* **For shlink older than 1.18.0 or not using swoole as the web server**: Resolve IP address locations: `/path/to/shlink/bin/cli visit:locate`
For example, assuming your domain is doma.in and shlink is in the `/path/to/shlink` folder, these would be the basic configurations for Nginx and Apache.
If you don't run this command regularly, the stats will say all visits come from *unknown* locations.
*Nginx:*
* Generate website previews: `/path/to/shlink/bin/cli short-url:process-previews`
```nginx
server {
server_name doma.in;
listen 80;
root /path/to/shlink/public;
index index.php;
charset utf-8;
Running this will improve the performance of the `doma.in/abc123/preview` URLs, which return a preview of the site.
location / {
try_files $uri $uri/ /index.php$is_args$args;
}
> **Important!** Generating previews is considered deprecated and the feature will be removed in Shlink v2.
location ~ \.php$ {
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass unix:/var/run/php/php7.4-fpm.sock;
fastcgi_index index.php;
include fastcgi.conf;
}
* **For shlink older than v1.17.0**: Update IP geolocation database: `/path/to/shlink/bin/cli visit:update-db`
location ~ /\.ht {
deny all;
}
}
```
When shlink is installed it downloads a fresh [GeoLite2](https://dev.maxmind.com/geoip/geoip2/geolite2/) db file. Running this command will update this file.
*Apache:*
The file is updated the first Tuesday of every month, so it should be enough running this command the first Wednesday.
```apache
<VirtualHost *:80>
ServerName doma.in
DocumentRoot "/path/to/shlink/public"
*Any of these commands accept the `-q` flag, which makes it not display any output. This is recommended when configuring the commands as cron jobs.*
<Directory "/path/to/shlink/public">
Options FollowSymLinks Includes ExecCGI
AllowOverride all
Order allow,deny
Allow from all
</Directory>
</VirtualHost>
```
> In future versions, it is planed that, when using **swoole** to serve shlink, some of these tasks are automatically run without blocking the request and also, without having to configure cron jobs. Probably resolving IP locations and generating previews.
* **Using swoole:**
First you need to install the swoole PHP extension with [pecl](https://pecl.php.net/package/swoole), `pecl install swoole`.
Once installed, it's actually pretty easy to get shlink up and running with swoole. Run `./vendor/bin/mezzio-swoole start -d` and you will get shlink running on port 8080.
However, by doing it this way, you are loosing all the access logs, and the service won't be automatically run if the server has to be restarted.
For that reason, you should create a daemon script, in `/etc/init.d/shlink_swoole`, like this one, replacing `/path/to/shlink` by the path to your shlink installation:
```bash
#!/bin/bash
### BEGIN INIT INFO
# Provides: shlink_swoole
# Required-Start: $local_fs $network $named $time $syslog
# Required-Stop: $local_fs $network $named $time $syslog
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Description: Shlink non-blocking server with swoole
### END INIT INFO
SCRIPT=/path/to/shlink/vendor/bin/mezzio-swoole\ start
RUNAS=root
PIDFILE=/var/run/shlink_swoole.pid
LOGDIR=/var/log/shlink
LOGFILE=${LOGDIR}/shlink_swoole.log
start() {
if [[ -f "$PIDFILE" ]] && kill -0 $(cat "$PIDFILE"); then
echo 'Shlink with swoole already running' >&2
return 1
fi
echo 'Starting shlink with swoole' >&2
mkdir -p "$LOGDIR"
touch "$LOGFILE"
local CMD="$SCRIPT &> \"$LOGFILE\" & echo \$!"
su -c "$CMD" $RUNAS > "$PIDFILE"
echo 'Shlink started' >&2
}
stop() {
if [[ ! -f "$PIDFILE" ]] || ! kill -0 $(cat "$PIDFILE"); then
echo 'Shlink with swoole not running' >&2
return 1
fi
echo 'Stopping shlink with swoole' >&2
kill -15 $(cat "$PIDFILE") && rm -f "$PIDFILE"
echo 'Shlink stopped' >&2
}
case "$1" in
start)
start
;;
stop)
stop
;;
restart)
stop
start
;;
*)
echo "Usage: $0 {start|stop|restart}"
esac
```
Then run these commands to enable the service and start it:
* `sudo chmod +x /etc/init.d/shlink_swoole`
* `sudo update-rc.d shlink_swoole defaults`
* `sudo update-rc.d shlink_swoole enable`
* `/etc/init.d/shlink_swoole start`
Now again, you can access shlink on port 8080, but this time the service will be automatically run at system start-up, and all access logs will be written in `/var/log/shlink/shlink_swoole.log` (you will probably want to [rotate those logs](https://www.digitalocean.com/community/tutorials/how-to-manage-logfiles-with-logrotate-on-ubuntu-16-04). You can find an example logrotate config file [here](data/infra/examples/shlink-daemon-logrotate.conf)).
Finally access to [https://app.shlink.io](https://app.shlink.io) and configure your server to start creating short URLs.
### Bonus
Geo-locating visits to your short links is a time-consuming task. When serving Shlink with swoole, the geo-location task is automatically run asynchronously just after a visit to a short URL happens.
However, if you are not serving Shlink with swoole, you will have to schedule the geo-location task to be run regularly in the background (for example, using cron jobs):
The command you need to run is `/path/to/shlink/bin/cli visit:locate`, and you can optionally provide the `-q` flag to remove any output and avoid your cron logs to be polluted.
## Update to new version
When a new Shlink version is available, you don't need to repeat the entire process yourself. Instead, follow these steps:
When a new Shlink version is available, you don't need to repeat the entire process. Instead, follow these steps:
1. Rename your existing Shlink directory to something else (ie. `shlink` ---> `shlink-old`).
2. Download and extract the new version of Shlink, and set the directories name to that of the old version. (ie. `shlink`).
3. Run the `bin/update` script in the new version's directory to migrate your configuration over.
2. Download and extract the new version of Shlink, and set the directory name to that of the old version (ie. `shlink`).
3. Run the `bin/update` script in the new version's directory to migrate your configuration over. You will be asked to provide the path to the old instance (ie. `shlink-old`).
4. If you are using shlink with swoole, restart the service by running `/etc/init.d/shlink_swoole restart`.
The `bin/update` script will ask you for the location from previous shlink version, and use it in order to import the configuration. It will then update the database and generate some assets shlink needs to work.
The `bin/update` will use the location from previous shlink version to import the configuration. It will then update the database and generate some assets shlink needs to work.
Right now, it does not import cached info (like website previews), but it will. For now you will need to regenerate them again.
**Important!** It is recommended that you don't skip any version when using this process. The update gets better on every version, but older versions might make assumptions.
**Important!** It is recommended that you don't skip any version when using this process. The update tool gets better on every version, but older versions might make assumptions.
## Using a docker image
@@ -237,7 +241,7 @@ Once shlink is installed, there are two main ways to interact with it:
It is probably a good idea to symlink the CLI entry point (`bin/cli`) to somewhere in your path, so that you can run shlink from any directory.
* **The REST API**. The complete docs on how to use the API can be found [here](https://shlink.io/api-docs), and a sandbox which also documents every endpoint can be found [here](https://shlink.io/swagger-ui/index.html).
* **The REST API**. The complete docs on how to use the API can be found [here](https://shlink.io/documentation/api-docs), and a sandbox which also documents every endpoint can be found in the [API Spec](https://api-spec.shlink.io/) portal.
However, you probably don't want to consume the raw API yourself. That's why a nice [web client](https://github.com/shlinkio/shlink-web-client) is provided that can be directly used from [https://app.shlink.io](https://app.shlink.io), or you can host it yourself too.
@@ -259,33 +263,90 @@ Options:
-v|vv|vvv, --verbose Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
Available commands:
help Displays help for a command
list Lists commands
help Displays help for a command
list Lists commands
api-key
api-key:disable Disables an API key.
api-key:generate Generates a new valid API key.
api-key:list Lists all the available API keys.
config
config:generate-charset [DEPRECATED] Generates a character set sample just by shuffling the default one, "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ". Then it can be set in the SHORTCODE_CHARS environment variable
config:generate-secret [DEPRECATED] Generates a random secret string that can be used for JWT token encryption
api-key:disable Disables an API key.
api-key:generate Generates a new valid API key.
api-key:list Lists all the available API keys.
db
db:create Creates the database needed for shlink to work. It will do nothing if the database already exists
db:migrate Runs database migrations, which will ensure the shlink database is up to date.
db:create Creates the database needed for shlink to work. It will do nothing if the database already exists
db:migrate Runs database migrations, which will ensure the shlink database is up to date.
short-url
short-url:delete [short-code:delete] Deletes a short URL
short-url:generate [shortcode:generate|short-code:generate] Generates a short URL for provided long URL and returns it
short-url:list [shortcode:list|short-code:list] List all short URLs
short-url:parse [shortcode:parse|short-code:parse] Returns the long URL behind a short code
short-url:process-previews [shortcode:process-previews|short-code:process-previews] [DEPRECATED] Processes and generates the previews for every URL, improving performance for later web requests.
short-url:visits [shortcode:visits|short-code:visits] Returns the detailed visits information for provided short code
short-url:delete Deletes a short URL
short-url:generate Generates a short URL for provided long URL and returns it
short-url:list List all short URLs
short-url:parse Returns the long URL behind a short code
short-url:visits Returns the detailed visits information for provided short code
tag
tag:create Creates one or more tags.
tag:delete Deletes one or more tags.
tag:list Lists existing tags.
tag:rename Renames one existing tag.
tag:create Creates one or more tags.
tag:delete Deletes one or more tags.
tag:list Lists existing tags.
tag:rename Renames one existing tag.
visit
visit:locate [visit:process] Resolves visits origin locations.
visit:update-db [DEPRECATED] Updates the GeoLite2 database file used to geolocate IP addresses
visit:locate Resolves visits origin locations.
```
## Multiple domains
While in many cases you will just have one short domain and you'll want all your short URLs to be served from it, there are some cases in which you might want to have multiple short domains served from the same Shlink instance.
If that's the case, you need to understand how Shlink will behave when managing your short URLs or any of them is visited.
### Management
When you create a short URL it is possible to optionally pass a `domain` param. If you don't pass it, the short URL will be created for the default domain (the one provided during Shlink's installation or in the `SHORT_DOMAIN_HOST` env var when using the docker image).
However, if you pass it, the short URL will be "linked" to that domain.
> Note that, if the default domain is passed, Shlink will ignore it and will behave as if no `domain` param was provided.
The main benefit of being able to pass the domain is that Shlink will allow the same custom slug to be used in multiple short URLs, as long as the domain is different (like `example.com/my-compaign`, `another.com/my-compaign` and `foo.com/my-compaign`).
Then, each short URL will be tracked separately and you will be able to define specific tags and metadata for each one of them.
However, this has a side effect. When you try to interact with an existing short URL (editing tags, editing meta, resolving it or deleting it), either from the REST API or the CLI tool, you will have to provide the domain appropriately.
Let's imagine this situation. Shlink's default domain is `example.com`, and you have the next short URLs:
* `https://example.com/abc123` -> a regular short URL where no domain was provided.
* `https://example.com/my-campaign` -> a regular short URL where no domain was provided, but it has a custom slug.
* `https://another.com/my-campaign` -> a short URL where the `another.com` domain was provided, and it has a custom slug.
* `https://another.com/def456` -> a short URL where the `another.com` domain was provided.
These are some of the results you will get when trying to interact with them, depending on the params you provide:
* Providing just the `abc123` short code -> the first URL will be matched.
* Providing just the `my-campaign` short code -> the second URL will be matched, since you did not specify a domain, therefor, Shlink looks for the one with the short code/slug `my-campaign` which is also linked to default domain (or not linked to any domain, to be more accurate).
* Providing the `my-campaign` short code and the `another.com` domain -> The third one will be matched.
* Providing just the `def456` short code -> Shlink will fail/not find any short URL, since there's none with the short code `def456` linked to default domain.
* Providing the `def456` short code and the `another.com` domain -> The fourth short URL will be matched.
* Providing any short code and the `foo.com` domain -> Again, no short URL will be found, as there's none linked to `foo.com` domain.
### Visits
Before adding support for multiple domains, you could point as many domains as you wanted to Shlink, and they would have always worked for existing short codes/slugs.
In order to keep backwards compatibility, Shlink's behavior when a short URL is visited is slightly different, getting to fallback in some cases.
Let's continue with previous example, and also consider we have three domains that will resolve to our Shlink instance, which are `example.com`, `another.com` and `foo.com`.
With that in mind, this is how Shlink will behave when the next short URLs are visited:
* `https://another.com/abc123` -> There was no short URL specifically defined for domain `another.com` and short code `abc123`, but it exists for default domain (`example.com`), so it will fall back to it and redirect to where `example.com/abc123` is configured to redirect.
* `https://example.com/def456` -> The fall-back does not happen from default domain to specific ones, only the other way around (like in previous case). Because of that, this one will result in a not-found URL, even though the `def456` short code exists for `another.com` domain.
* `https://foo.com/abc123` -> This will also fall-back to `example.com/abc123`, like in the first case.
* `https://another.com/non-existing` -> The combination of `another.com` domain with the `non-existing` slug does not exist, so Shlink will try to fall-back to the same but for default domain (`example.com`). However, since that combination does not exist either, it will result in a not-found URL.
* Any other short URL visited exactly as it was configured will, of course, resolve as expected.
### Special redirects
It is currently possible to configure some special redirects when the base domain is visited, a URL does not match, or an invalid/disabled short URL is visited.
Those are configured during Shlink's installation or via env vars when using the docker image.
Currently those are all shared for all domains serving the same Shlink instance, but the plan is to update that and allow specific ones for every existing domain.
---
> This product includes GeoLite2 data created by MaxMind, available from [https://www.maxmind.com](https://www.maxmind.com)

88
UPGRADE.md Normal file
View File

@@ -0,0 +1,88 @@
# Upgrading
## From v1.x to v2.x
### PHP 7.4 required
This new version takes advantage of several new features introduced in PHP 7.4.
Thanks to that, the code is more reliable and robust, and easier to maintain and improve.
However, that means that any previous PHP version is no longer supported.
### Preview generation
The ability to generate website previews has been completely removed and has no replacement.
The feature never properly worked, and it wasn't really useful. Because of that, the feature is no longer available on Shlink 2.x
Removing this feature has these implications:
* The `short-url:process-previews` CLI command no longer exists, and an error will be thrown if executed.
* The `/{shortCode}/preview` path is no longer valid, and will return a 404 status.
### Removed paths
These routes have been removed, but have a direct replacement:
* `/qr/{shortCode}[/{size}]` -> `/{shortCode}/qr-code[/{size}]`
* `PUT /rest/v{version}/short-urls/{shortCode}` -> `PATCH /rest/v{version}/short-urls/{shortCode}`
When using the old ones, a 404 status will me returned now.
### Removed command and route aliases
All the aliases for the CLI commands in the `short-urls` namespace have been removed. If you were using any of those commands with the `shortcode` or `short-code` prefixes, make sure to update them to use the `short-urls` prefix instead.
The same happens for all REST endpoints starting with `/short-code`. They were previously aliased to `/short-urls` ones, but they will return a 404 now. Make sure to update them accordingly.
### JWT authentication removed
Shlink's REST API no longer accepts authentication using a JWT token. The API key has to be passed now in the `x-api-key` header.
Removing this feature has these implications:
* Shlink will no longer introspect the `Authorization` header for Bearer tokens.
* The `POST /rest/v{version}/authenticate` endpoint no longer exists and will return a 404.
### API version is now required
Endpoints need to provide a version in the path now. Previously, not providing a version used to fall back to v1. Now, it will return a 404 status, as no route will match.
The only exception is the `/rest/health` endpoint, which will continue working without the version.
### API errors
Shlink v1.21.0 introduced support for API errors using the Problem Details format, as well as the v2 of the API.
For backwards compatibility reasons, requests performed to v1 continued to return the old `error` and `message` properties.
Starting with Shlink v2.0.0, both versions of the API will no longer return those two properties.
As a replacement, use `type` instead of `error`, and `detail` instead of `message`.
### Changes in models
The next REST API models have changed:
* **ShortUrl**: The `originalUrl` property was deprecated and has been removed. Use `longUrl` instead.
* **Visit**: The `remoteAddr` property was deprecated and has been removed. It has no replacement.
* **VisitLocation**: The `latitude` and `longitude` properties are no longer strings, but float.
### URL validation
Shlink can verify provided long URLs are valid before trying to shorten them. Starting with v2, it no longer does it by default and needs to be explicitly enabled instead of explicitly disabled.
### Removed config options
The `not_found_redirect_to` config option and the `NOT_FOUND_REDIRECT_TO` env var are no longer taken into consideration for the docker image.
Instead, use `invalid_short_url_redirect_to` and `INVALID_SHORT_URL_REDIRECT_TO` respectively.
### Migrated to Laminas
The project has been using Zend Framework components since the beginning. Since it has been re-branded as [Laminas](https://getlaminas.org/), this version updates to the new set of components.
Updating to Laminas components has these implications:
* If you were manually serving Shlink with swoole, the entry script has to be changed from `/path/to/shlink/vendor/bin/zend-expressive-swoole` to `/path/to/shlink/vendor/bin/mezzio-swoole`

View File

@@ -1,10 +1,7 @@
#!/usr/bin/env php
<?php
declare(strict_types=1);
use Psr\Container\ContainerInterface;
use Symfony\Component\Console\Application as CliApp;
/** @var ContainerInterface $container */
$container = include __DIR__ . '/../config/container.php';
$container->get(CliApp::class)->run();
$run = require __DIR__ . '/../config/run.php';
$run(true);

View File

@@ -8,5 +8,5 @@ use function chdir;
use function dirname;
chdir(dirname(__DIR__));
$run = require __DIR__ . '/../vendor/shlinkio/shlink-installer/bin/run.php';
$run(false);
[$install] = require __DIR__ . '/../vendor/shlinkio/shlink-installer/bin/run.php';
$install();

View File

@@ -1,18 +1,19 @@
#!/usr/bin/env sh
export APP_ENV=test
export DB_DRIVER=mysql
export TEST_ENV=api
# Try to stop server just in case it hanged in last execution
vendor/bin/zend-expressive-swoole stop
vendor/bin/mezzio-swoole stop
echo 'Starting server...'
vendor/bin/zend-expressive-swoole start -d
vendor/bin/mezzio-swoole start -d
sleep 2
vendor/bin/phpunit --order-by=random -c phpunit-api.xml --testdox --colors=always
vendor/bin/phpunit --order-by=random -c phpunit-api.xml --testdox --colors=always --log-junit=build/coverage-api/junit.xml $*
testsExitCode=$?
vendor/bin/zend-expressive-swoole stop
vendor/bin/mezzio-swoole stop
# Exit this script with the same code as the tests. If tests failed, this script has to fail
exit $testsExitCode

View File

@@ -8,5 +8,5 @@ use function chdir;
use function dirname;
chdir(dirname(__DIR__));
$run = require __DIR__ . '/../vendor/shlinkio/shlink-installer/bin/run.php';
$run(true);
[, $update] = require __DIR__ . '/../vendor/shlinkio/shlink-installer/bin/run.php';
$update();

Binary file not shown.

View File

@@ -19,13 +19,14 @@ mkdir -p "${builtcontent}"
rsync -av * "${builtcontent}" \
--exclude=*docker* \
--exclude=Dockerfile \
--include=.htaccess \
--exclude-from=./.dockerignore
cd "${builtcontent}"
# Install dependencies
echo "Installing dependencies with $composerBin..."
${composerBin} self-update
${composerBin} install --no-dev --optimize-autoloader --no-progress --no-interaction
${composerBin} install --no-dev --optimize-autoloader --prefer-dist --no-progress --no-interaction
# Delete development files
echo 'Deleting dev files...'

View File

@@ -12,72 +12,75 @@
}
],
"require": {
"php": "^7.2",
"php": "^7.4",
"ext-json": "*",
"ext-pdo": "*",
"acelaya/ze-content-based-error-handler": "^3.0",
"akrabat/ip-address-middleware": "^1.0",
"cakephp/chronos": "^1.2",
"cocur/slugify": "^3.0",
"doctrine/cache": "^1.6",
"doctrine/dbal": "^2.9",
"doctrine/migrations": "^2.0",
"doctrine/orm": "^2.5",
"cakephp/chronos": "^2.0",
"cocur/slugify": "^4.0",
"doctrine/cache": "^1.9",
"doctrine/dbal": "^2.10",
"doctrine/migrations": "^3.0.1",
"doctrine/orm": "^2.7",
"endroid/qr-code": "^3.6",
"firebase/php-jwt": "^4.0",
"geoip2/geoip2": "^2.9",
"guzzlehttp/guzzle": "^6.3",
"guzzlehttp/guzzle": "^7.0",
"laminas/laminas-config": "^3.3",
"laminas/laminas-config-aggregator": "^1.1",
"laminas/laminas-diactoros": "^2.1.3",
"laminas/laminas-inputfilter": "^2.10",
"laminas/laminas-paginator": "^2.8",
"laminas/laminas-servicemanager": "^3.4",
"laminas/laminas-stdlib": "^3.2",
"lcobucci/jwt": "^4.0@alpha",
"league/uri": "^6.2",
"lstrojny/functional-php": "^1.9",
"mikehaertl/phpwkhtmltopdf": "^2.2",
"monolog/monolog": "^1.24",
"ocramius/proxy-manager": "~2.2.2",
"mezzio/mezzio": "^3.2",
"mezzio/mezzio-fastroute": "^3.0",
"mezzio/mezzio-helpers": "^5.3",
"mezzio/mezzio-platesrenderer": "^2.1",
"mezzio/mezzio-problem-details": "^1.1",
"mezzio/mezzio-swoole": "^2.6.4",
"monolog/monolog": "^2.0",
"nikolaposa/monolog-factory": "^3.0",
"ocramius/proxy-manager": "^2.7.0",
"phly/phly-event-dispatcher": "^1.0",
"php-middleware/request-id": "^4.0",
"predis/predis": "^1.1",
"pugx/shortid-php": "^0.5",
"shlinkio/shlink-common": "^2.2.1",
"shlinkio/shlink-event-dispatcher": "^1.0",
"shlinkio/shlink-installer": "^3.1",
"shlinkio/shlink-ip-geolocation": "^1.1",
"symfony/console": "^4.3",
"symfony/filesystem": "^4.3",
"symfony/lock": "^4.3",
"symfony/process": "^4.3",
"theorchard/monolog-cascade": "^0.5",
"zendframework/zend-config": "^3.3",
"zendframework/zend-config-aggregator": "^1.1",
"zendframework/zend-diactoros": "^2.1.3",
"zendframework/zend-expressive": "^3.2",
"zendframework/zend-expressive-fastroute": "^3.0",
"zendframework/zend-expressive-helpers": "^5.3",
"zendframework/zend-expressive-platesrenderer": "^2.1",
"zendframework/zend-expressive-swoole": "^2.4",
"zendframework/zend-inputfilter": "^2.10",
"zendframework/zend-paginator": "^2.8",
"zendframework/zend-servicemanager": "^3.4",
"zendframework/zend-stdlib": "^3.2"
"ramsey/uuid": "^3.9",
"shlinkio/shlink-common": "^3.3.0",
"shlinkio/shlink-config": "^1.0",
"shlinkio/shlink-event-dispatcher": "^1.4",
"shlinkio/shlink-importer": "^2.0.1",
"shlinkio/shlink-installer": "^5.1.0",
"shlinkio/shlink-ip-geolocation": "^1.5",
"symfony/console": "^5.1",
"symfony/filesystem": "^5.1",
"symfony/lock": "^5.1",
"symfony/mercure": "^0.3.0",
"symfony/process": "^5.1",
"symfony/string": "^5.1"
},
"require-dev": {
"devster/ubench": "^2.0",
"dms/phpunit-arraysubset-asserts": "^0.2.0",
"eaglewu/swoole-ide-helper": "dev-master",
"filp/whoops": "^2.4",
"infection/infection": "^0.14.2",
"phpstan/phpstan": "^0.11.16",
"phpunit/phpcov": "^6.0",
"phpunit/phpunit": "^8.3",
"infection/infection": "^0.20.0",
"phpspec/prophecy-phpunit": "^2.0",
"phpstan/phpstan": "^0.12.52",
"phpunit/php-code-coverage": "^9.2",
"phpunit/phpunit": "^9.4",
"roave/security-advisories": "dev-master",
"shlinkio/php-coding-standard": "~2.0.0",
"shlinkio/shlink-test-utils": "^1.0",
"symfony/dotenv": "^4.3",
"symfony/var-dumper": "^4.3",
"zendframework/zend-component-installer": "^2.1",
"zendframework/zend-expressive-tooling": "^1.2"
"shlinkio/php-coding-standard": "~2.1.1",
"shlinkio/shlink-test-utils": "^1.5",
"symfony/var-dumper": "^5.1"
},
"autoload": {
"psr-4": {
"Shlinkio\\Shlink\\CLI\\": "module/CLI/src",
"Shlinkio\\Shlink\\Rest\\": "module/Rest/src",
"Shlinkio\\Shlink\\Core\\": "module/Core/src",
"Shlinkio\\Shlink\\PreviewGenerator\\": "module/PreviewGenerator/src/"
"Shlinkio\\Shlink\\Core\\": "module/Core/src"
},
"files": [
"module/Core/functions/functions.php"
@@ -91,9 +94,11 @@
"ShlinkioTest\\Shlink\\Core\\": [
"module/Core/test",
"module/Core/test-db"
],
"ShlinkioTest\\Shlink\\PreviewGenerator\\": "module/PreviewGenerator/test"
}
]
},
"files": [
"config/test/constants.php"
]
},
"scripts": {
"ci": [
@@ -104,7 +109,7 @@
],
"cs": "phpcs",
"cs:fix": "phpcbf",
"stan": "phpstan analyse module/*/src/ module/*/config config docker/config --level=5 -c phpstan.neon",
"stan": "phpstan analyse module/*/src/ module/*/config config docker/config data/migrations --level=6",
"test": [
"@test:unit",
"@test:db",
@@ -112,37 +117,34 @@
],
"test:ci": [
"@test:unit:ci",
"@test:db:ci",
"@test:api"
"@test:db"
],
"test:unit": "phpdbg -qrr vendor/bin/phpunit --order-by=random --colors=always --coverage-php build/coverage-unit.cov --testdox",
"test:unit:ci": "phpdbg -qrr vendor/bin/phpunit --order-by=random --colors=always --coverage-php build/coverage-unit.cov --coverage-clover=build/clover.xml --coverage-xml=build/coverage-xml --log-junit=build/phpunit.junit.xml --testdox",
"test:unit": "@php vendor/bin/phpunit --order-by=random --colors=always --coverage-php build/coverage-unit.cov --testdox",
"test:unit:ci": "@test:unit --coverage-xml=build/coverage-unit/coverage-xml --log-junit=build/coverage-unit/junit.xml",
"test:db": [
"@test:db:sqlite",
"@test:db:sqlite:ci",
"@test:db:mysql",
"@test:db:maria",
"@test:db:postgres"
"@test:db:postgres",
"@test:db:ms"
],
"test:db:ci": [
"@test:db:sqlite",
"@test:db:mysql",
"@test:db:postgres"
],
"test:db:sqlite": "APP_ENV=test phpdbg -qrr vendor/bin/phpunit --order-by=random --colors=always -c phpunit-db.xml --coverage-php build/coverage-db.cov --testdox",
"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 --coverage-xml=build/coverage-db/coverage-xml --log-junit=build/coverage-db/junit.xml",
"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:api": "bin/test/run-api-tests.sh",
"test:pretty": [
"@test",
"phpdbg -qrr vendor/bin/phpcov merge build --html build/html"
"test:unit:pretty": "@php vendor/bin/phpunit --order-by=random --colors=always --coverage-html build/coverage-unit-html",
"infect": "infection --threads=4 --min-msi=80 --log-verbosity=default --only-covered",
"infect:ci:base": "@infect --skip-initial-tests",
"infect:ci": [
"@infect:ci:base --coverage=build/coverage-unit",
"@infect:ci:base --coverage=build/coverage-db --test-framework-options=--configuration=phpunit-db.xml"
],
"test:unit:pretty": "phpdbg -qrr vendor/bin/phpunit --order-by=random --colors=always --coverage-html build/coverage",
"infect": "infection --threads=4 --min-msi=75 --log-verbosity=default --only-covered",
"infect:ci": "infection --threads=4 --min-msi=75 --log-verbosity=default --only-covered --coverage=build",
"infect:show": "infection --threads=4 --min-msi=75 --log-verbosity=default --only-covered --show-mutations",
"infect:test": [
"@test:unit:ci",
"@test:db:sqlite:ci",
"@infect:ci"
],
"clean:dev": "rm -f data/database.sqlite && rm -f config/params/generated_config.php"
@@ -156,22 +158,20 @@
"test:ci": "<fg=blue;options=bold>Runs all test suites, generating all needed reports and logs for CI envs</>",
"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:db": "<fg=blue;options=bold>Runs database test suites on a SQLite, MySQL, MariaDB and PostgreSQL</>",
"test:db:ci": "<fg=blue;options=bold>Runs database test suites on a SQLite, MySQL and PostgreSQL</>",
"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: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:api": "<fg=blue;options=bold>Runs API test suites</>",
"test:pretty": "<fg=blue;options=bold>Runs all test suites and generates an HTML code coverage report</>",
"test:unit:pretty": "<fg=blue;options=bold>Runs unit test suites and generates an HTML code coverage report</>",
"infect": "<fg=blue;options=bold>Checks unit tests quality applying mutation testing</>",
"infect:ci": "<fg=blue;options=bold>Checks unit tests quality applying mutation testing with existing reports and logs</>",
"infect:show": "<fg=blue;options=bold>Checks unit tests quality applying mutation testing and shows applied mutators</>",
"infect:test": "<fg=blue;options=bold>Checks unit tests quality applying mutation testing</>",
"clean:dev": "<fg=blue;options=bold>Deletes artifacts which are gitignored and could affect dev env</>"
},
"config": {
"sort-packages": true
"sort-packages": true,
"platform-check": false
}
}

View File

@@ -2,14 +2,11 @@
declare(strict_types=1);
use function Shlinkio\Shlink\Common\env;
return [
'app_options' => [
'name' => 'Shlink',
'version' => '%SHLINK_VERSION%',
'secret_key' => env('SECRET_KEY', ''),
'disable_track_param' => null,
],

View File

@@ -7,11 +7,11 @@ return [
'ip_address_resolution' => [
'headers_to_inspect' => [
'CF-Connecting-IP',
'True-Client-IP',
'X-Real-IP',
'Forwarded',
'X-Forwarded-For',
'X-Forwarded',
'Forwarded',
'True-Client-IP',
'X-Real-IP',
'X-Cluster-Client-Ip',
'Client-Ip',
],

View File

@@ -2,7 +2,7 @@
declare(strict_types=1);
use Zend\ConfigAggregator\ConfigAggregator;
use Laminas\ConfigAggregator\ConfigAggregator;
return [

View File

@@ -1,7 +1,8 @@
<?php
declare(strict_types=1);
use Zend\ConfigAggregator\ConfigAggregator;
use Laminas\ConfigAggregator\ConfigAggregator;
return [

View File

@@ -2,18 +2,23 @@
declare(strict_types=1);
use Zend\Expressive;
use Zend\Expressive\Container;
use GuzzleHttp\Client;
use Mezzio\Container;
use Psr\Http\Client\ClientInterface;
return [
'dependencies' => [
'delegators' => [
Expressive\Application::class => [
Mezzio\Application::class => [
Container\ApplicationConfigInjectionDelegator::class,
],
],
'aliases' => [
ClientInterface::class => Client::class,
],
'lazy_services' => [
'proxies_target_dir' => 'data/proxies',
'proxies_namespace' => 'ShlinkProxy',

View File

@@ -1,4 +1,5 @@
<?php
declare(strict_types=1);
use Psr\Container\ContainerInterface;
@@ -12,7 +13,7 @@ return [
],
'initializers' => [
function (ContainerInterface $container, $instance) {
function (ContainerInterface $container, $instance): void {
if ($instance instanceof Log\LoggerAwareInterface) {
$instance->setLogger($container->get(Log\LoggerInterface::class));
}

View File

@@ -9,11 +9,12 @@ return [
'entity_manager' => [
'orm' => [
'proxies_dir' => 'data/proxies',
'load_mappings_using_functional_style' => true,
],
'connection' => [
'user' => env('DB_USER'),
'password' => env('DB_PASSWORD'),
'dbname' => env('DB_NAME', 'shlink'),
'user' => '',
'password' => '',
'dbname' => 'shlink',
'charset' => 'utf8',
],
],

View File

@@ -1,13 +1,24 @@
<?php
declare(strict_types=1);
use function Shlinkio\Shlink\Common\env;
// When running tests, any mysql-specific option can interfere with other drivers
$driverOptions = env('APP_ENV') === 'test' ? [] : [
PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8',
PDO::MYSQL_ATTR_USE_BUFFERED_QUERY => true,
];
return [
'entity_manager' => [
'connection' => [
'user' => 'root',
'password' => 'root',
'driver' => 'pdo_mysql',
'host' => 'shlink_db',
'driverOptions' => [
PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8',
],
'driverOptions' => $driverOptions,
],
],

View File

@@ -0,0 +1,33 @@
<?php
declare(strict_types=1);
use Laminas\Stratigility\Middleware\ErrorHandler;
use Mezzio\ProblemDetails\ProblemDetailsMiddleware;
use Shlinkio\Shlink\Common\Logger;
return [
'problem-details' => [
'default_types_map' => [
404 => 'NOT_FOUND',
500 => 'INTERNAL_SERVER_ERROR',
],
],
'error_handler' => [
'listeners' => [Logger\ErrorLogger::class],
],
'dependencies' => [
'delegators' => [
ErrorHandler::class => [
Logger\ErrorHandlerListenerAttachingDelegator::class,
],
ProblemDetailsMiddleware::class => [
Logger\ErrorHandlerListenerAttachingDelegator::class,
],
],
],
];

View File

@@ -1,29 +0,0 @@
<?php
declare(strict_types=1);
use Zend\Expressive\Container\WhoopsErrorResponseGeneratorFactory;
return [
'dependencies' => [
'invokables' => [
'Zend\Expressive\Whoops' => Whoops\Run::class,
'Zend\Expressive\WhoopsPageHandler' => Whoops\Handler\PrettyPageHandler::class,
],
],
'whoops' => [
'json_exceptions' => [
'display' => true,
'show_trace' => true,
'ajax_only' => true,
],
],
'error_handler' => [
'plugins' => [
'factories' => [
'text/html' => WhoopsErrorResponseGeneratorFactory::class,
],
],
],
];

View File

@@ -7,7 +7,7 @@ return [
'geolite2' => [
'db_location' => __DIR__ . '/../../data/GeoLite2-City.mmdb',
'temp_dir' => sys_get_temp_dir(),
'download_from' => 'http://geolite.maxmind.com/download/geoip/database/GeoLite2-City.tar.gz',
'license_key' => 'G4Lm0C60yJsnkdPi',
],
];

View File

@@ -2,49 +2,52 @@
declare(strict_types=1);
use Shlinkio\Shlink\Installer\Config\Plugin;
use Shlinkio\Shlink\Installer\Config\Option;
return [
'installer_plugins_expected_config' => [
Plugin\UrlShortenerConfigCustomizer::class => [
Plugin\UrlShortenerConfigCustomizer::SCHEMA,
Plugin\UrlShortenerConfigCustomizer::HOSTNAME,
Plugin\UrlShortenerConfigCustomizer::VALIDATE_URL,
'installer' => [
'enabled_options' => [
Option\Database\DatabaseDriverConfigOption::class,
Option\Database\DatabaseNameConfigOption::class,
Option\Database\DatabaseHostConfigOption::class,
Option\Database\DatabasePortConfigOption::class,
Option\Database\DatabaseUserConfigOption::class,
Option\Database\DatabasePasswordConfigOption::class,
Option\Database\DatabaseSqlitePathConfigOption::class,
Option\Database\DatabaseMySqlOptionsConfigOption::class,
Option\UrlShortener\ShortDomainHostConfigOption::class,
Option\UrlShortener\ShortDomainSchemaConfigOption::class,
Option\UrlShortener\ValidateUrlConfigOption::class,
Option\Visit\VisitsWebhooksConfigOption::class,
Option\Redirect\BaseUrlRedirectConfigOption::class,
Option\Redirect\InvalidShortUrlRedirectConfigOption::class,
Option\Redirect\Regular404RedirectConfigOption::class,
Option\DisableTrackParamConfigOption::class,
Option\Visit\CheckVisitsThresholdConfigOption::class,
Option\Visit\VisitsThresholdConfigOption::class,
Option\BasePathConfigOption::class,
Option\Worker\TaskWorkerNumConfigOption::class,
Option\Worker\WebWorkerNumConfigOption::class,
Option\RedisServersConfigOption::class,
Option\UrlShortener\ShortCodeLengthOption::class,
Option\Mercure\EnableMercureConfigOption::class,
Option\Mercure\MercurePublicUrlConfigOption::class,
Option\Mercure\MercureInternalUrlConfigOption::class,
Option\Mercure\MercureJwtSecretConfigOption::class,
Option\UrlShortener\GeoLiteLicenseKeyConfigOption::class,
Option\UrlShortener\IpAnonymizationConfigOption::class,
Option\UrlShortener\RedirectStatusCodeConfigOption::class,
Option\UrlShortener\RedirectCacheLifeTimeConfigOption::class,
],
Plugin\ApplicationConfigCustomizer::class => [
Plugin\ApplicationConfigCustomizer::SECRET,
Plugin\ApplicationConfigCustomizer::DISABLE_TRACK_PARAM,
Plugin\ApplicationConfigCustomizer::CHECK_VISITS_THRESHOLD,
Plugin\ApplicationConfigCustomizer::VISITS_THRESHOLD,
Plugin\ApplicationConfigCustomizer::BASE_PATH,
Plugin\ApplicationConfigCustomizer::WEB_WORKER_NUM,
Plugin\ApplicationConfigCustomizer::TASK_WORKER_NUM,
],
Plugin\DatabaseConfigCustomizer::class => [
Plugin\DatabaseConfigCustomizer::DRIVER,
Plugin\DatabaseConfigCustomizer::NAME,
Plugin\DatabaseConfigCustomizer::USER,
Plugin\DatabaseConfigCustomizer::PASSWORD,
Plugin\DatabaseConfigCustomizer::HOST,
Plugin\DatabaseConfigCustomizer::PORT,
],
Plugin\RedirectsConfigCustomizer::class => [
Plugin\RedirectsConfigCustomizer::INVALID_SHORT_URL_REDIRECT_TO,
Plugin\RedirectsConfigCustomizer::REGULAR_404_REDIRECT_TO,
Plugin\RedirectsConfigCustomizer::BASE_URL_REDIRECT_TO,
],
],
'installation_commands' => [
'db_create_schema' => [
'command' => 'bin/cli db:create',
],
'db_migrate' => [
'command' => 'bin/cli db:migrate',
'installation_commands' => [
'db_create_schema' => [
'command' => 'bin/cli db:create',
],
'db_migrate' => [
'command' => 'bin/cli db:migrate',
],
],
],

View File

@@ -2,15 +2,13 @@
declare(strict_types=1);
use Laminas\ServiceManager\AbstractFactory\ConfigAbstractFactory;
use Shlinkio\Shlink\Common\Cache\RedisFactory;
use Shlinkio\Shlink\Common\Lock\RetryLockStoreDelegatorFactory;
use Shlinkio\Shlink\Common\Logger\LoggerAwareDelegatorFactory;
use Symfony\Component\Lock;
use Zend\ServiceManager\AbstractFactory\ConfigAbstractFactory;
// This class alias tricks the ConfigAbstractFactory to return Lock\Factory instances even with a different service name
$localLockFactory = 'Shlinkio\Shlink\LocalLockFactory';
class_alias(Lock\Factory::class, $localLockFactory);
use const Shlinkio\Shlink\Core\LOCAL_LOCK_FACTORY;
return [
@@ -22,8 +20,8 @@ return [
'factories' => [
Lock\Store\FlockStore::class => ConfigAbstractFactory::class,
Lock\Store\RedisStore::class => ConfigAbstractFactory::class,
Lock\Factory::class => ConfigAbstractFactory::class,
$localLockFactory => ConfigAbstractFactory::class,
Lock\LockFactory::class => ConfigAbstractFactory::class,
LOCAL_LOCK_FACTORY => ConfigAbstractFactory::class,
],
'aliases' => [
// With this config, a user could alias 'lock_store' => 'redis_lock_store' to override the default
@@ -36,7 +34,7 @@ return [
Lock\Store\RedisStore::class => [
RetryLockStoreDelegatorFactory::class,
],
Lock\Factory::class => [
Lock\LockFactory::class => [
LoggerAwareDelegatorFactory::class,
],
],
@@ -45,8 +43,8 @@ return [
ConfigAbstractFactory::class => [
Lock\Store\FlockStore::class => ['config.locks.locks_dir'],
Lock\Store\RedisStore::class => [RedisFactory::SERVICE_NAME],
Lock\Factory::class => ['lock_store'],
$localLockFactory => ['local_lock_store'],
Lock\LockFactory::class => ['lock_store'],
LOCAL_LOCK_FACTORY => ['local_lock_store'],
],
];

View File

@@ -4,64 +4,72 @@ declare(strict_types=1);
namespace Shlinkio\Shlink;
use Monolog\Handler\RotatingFileHandler;
use Monolog\Handler\StreamHandler;
use Monolog\Formatter;
use Monolog\Handler;
use Monolog\Logger;
use Monolog\Processor;
use MonologFactory\DiContainerLoggerFactory;
use PhpMiddleware\RequestId;
use Psr\Log\LoggerInterface;
use const PHP_EOL;
$processors = [
'exception_with_new_line' => [
'name' => Common\Logger\Processor\ExceptionWithNewLineProcessor::class,
],
'psr3' => [
'name' => Processor\PsrLogMessageProcessor::class,
],
'request_id' => RequestId\MonologProcessor::class,
];
$formatter = [
'name' => Formatter\LineFormatter::class,
'params' => [
'format' => '[%datetime%] [%extra.request_id%] %channel%.%level_name% - %message%' . PHP_EOL,
'allow_inline_line_breaks' => true,
],
];
return [
'logger' => [
'formatters' => [
'dashed' => [
'format' => '[%datetime%] %channel%.%level_name% - %message%' . PHP_EOL,
'include_stacktraces' => true,
'Shlink' => [
'name' => 'Shlink',
'handlers' => [
'shlink_handler' => [
'name' => Handler\RotatingFileHandler::class,
'params' => [
'level' => Logger::INFO,
'filename' => 'data/log/shlink_log.log',
'max_files' => 30,
'file_permission' => 0666,
],
'formatter' => $formatter,
],
],
'processors' => $processors,
],
'handlers' => [
'shlink_rotating_handler' => [
'class' => RotatingFileHandler::class,
'level' => Logger::INFO,
'filename' => 'data/log/shlink_log.log',
'max_files' => 30,
'formatter' => 'dashed',
],
'access_handler' => [
'class' => StreamHandler::class,
'level' => Logger::INFO,
'stream' => 'php://stdout',
],
],
'processors' => [
'exception_with_new_line' => [
'class' => Common\Logger\Processor\ExceptionWithNewLineProcessor::class,
],
'psr3' => [
'class' => Processor\PsrLogMessageProcessor::class,
],
],
'loggers' => [
'Shlink' => [
'handlers' => ['shlink_rotating_handler'],
'processors' => ['exception_with_new_line', 'psr3'],
],
'Access' => [
'handlers' => ['access_handler'],
'processors' => ['exception_with_new_line', 'psr3'],
'Access' => [
'name' => 'Access',
'handlers' => [
'access_handler' => [
'name' => Handler\StreamHandler::class,
'params' => [
'level' => Logger::INFO,
'stream' => 'php://stdout',
],
'formatter' => $formatter,
],
],
'processors' => $processors,
],
],
'dependencies' => [
'factories' => [
'Logger_Shlink' => Common\Logger\LoggerFactory::class,
'Logger_Access' => Common\Logger\LoggerFactory::class,
'Logger_Shlink' => [DiContainerLoggerFactory::class, 'Shlink'],
'Logger_Access' => [DiContainerLoggerFactory::class, 'Access'],
],
'aliases' => [
'logger' => 'Logger_Shlink',
@@ -70,10 +78,11 @@ return [
],
],
'zend-expressive-swoole' => [
'mezzio-swoole' => [
'swoole-http-server' => [
'logger' => [
'logger-name' => 'Logger_Access',
'format' => '%h %l %u "%r" %>s %b',
],
],
],

View File

@@ -1,4 +1,5 @@
<?php
declare(strict_types=1);
use Monolog\Handler\StreamHandler;
@@ -7,34 +8,28 @@ use Monolog\Logger;
$isSwoole = extension_loaded('swoole');
// For swoole, send logs to standard output
$logger = $isSwoole ? [
'handlers' => [
'shlink_rotating_handler' => [
'level' => Logger::EMERGENCY, // This basically disables regular file logs
],
'shlink_stdout_handler' => [
'class' => StreamHandler::class,
$handler = $isSwoole
? [
'name' => StreamHandler::class,
'params' => [
'level' => Logger::DEBUG,
'stream' => 'php://stdout',
'formatter' => 'dashed',
],
],
'loggers' => [
'Shlink' => [
'handlers' => ['shlink_stdout_handler'],
],
],
] : [
'handlers' => [
'shlink_rotating_handler' => [
]
: [
'params' => [
'level' => Logger::DEBUG,
],
],
];
];
return [
'logger' => $logger,
'logger' => [
'Shlink' => [
'handlers' => [
'shlink_handler' => $handler,
],
],
],
];

View File

@@ -0,0 +1,36 @@
<?php
declare(strict_types=1);
use Laminas\ServiceManager\Proxy\LazyServiceFactory;
use Shlinkio\Shlink\Common\Mercure\LcobucciJwtProvider;
use Symfony\Component\Mercure\Publisher;
use Symfony\Component\Mercure\PublisherInterface;
return [
'mercure' => [
'public_hub_url' => null,
'internal_hub_url' => null,
'jwt_secret' => null,
'jwt_issuer' => 'Shlink',
],
'dependencies' => [
'delegators' => [
LcobucciJwtProvider::class => [
LazyServiceFactory::class,
],
Publisher::class => [
LazyServiceFactory::class,
],
],
'lazy_services' => [
'class_map' => [
LcobucciJwtProvider::class => LcobucciJwtProvider::class,
Publisher::class => PublisherInterface::class,
],
],
],
];

View File

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

View File

@@ -4,53 +4,70 @@ declare(strict_types=1);
namespace Shlinkio\Shlink;
use Zend\Expressive;
use Zend\Stratigility\Middleware\ErrorHandler;
use Laminas\Stratigility\Middleware\ErrorHandler;
use Mezzio\Helper;
use Mezzio\ProblemDetails;
use Mezzio\Router;
use PhpMiddleware\RequestId\RequestIdMiddleware;
return [
'middleware_pipeline' => [
'pre-routing' => [
'error-handler' => [
'middleware' => [
Helper\ContentLengthMiddleware::class,
ErrorHandler::class,
Expressive\Helper\ContentLengthMiddleware::class,
Common\Middleware\CloseDbConnectionMiddleware::class,
],
'priority' => 12,
],
'pre-routing-rest' => [
'error-handler-rest' => [
'path' => '/rest',
'middleware' => [
Rest\Middleware\PathVersionMiddleware::class,
Rest\Middleware\ShortUrl\ShortCodePathMiddleware::class,
Rest\Middleware\CrossDomainMiddleware::class,
RequestIdMiddleware::class,
ProblemDetails\ProblemDetailsMiddleware::class,
],
],
'pre-routing' => [
'middleware' => [
Common\Middleware\CloseDbConnectionMiddleware::class,
],
'priority' => 11,
],
'routing' => [
'middleware' => [
Expressive\Router\Middleware\RouteMiddleware::class,
Router\Middleware\RouteMiddleware::class,
Router\Middleware\ImplicitHeadMiddleware::class,
],
'priority' => 10,
],
'rest' => [
'path' => '/rest',
'middleware' => [
Rest\Middleware\CrossDomainMiddleware::class,
Expressive\Router\Middleware\ImplicitOptionsMiddleware::class,
Router\Middleware\ImplicitOptionsMiddleware::class,
Rest\Middleware\BodyParserMiddleware::class,
Rest\Middleware\AuthenticationMiddleware::class,
],
'priority' => 5,
],
'post-routing' => [
'dispatch' => [
'middleware' => [
Expressive\Router\Middleware\DispatchMiddleware::class,
Core\Response\NotFoundHandler::class,
Router\Middleware\DispatchMiddleware::class,
],
],
'not-found-rest' => [
'path' => '/rest',
'middleware' => [
ProblemDetails\ProblemDetailsNotFoundHandler::class,
],
],
'not-found' => [
'middleware' => [
Core\ErrorHandler\NotFoundRedirectHandler::class,
Core\ErrorHandler\NotFoundTemplateHandler::class,
],
'priority' => 1,
],
],
];

View File

@@ -1,12 +0,0 @@
<?php
declare(strict_types=1);
/* @deprecated */
return [
'preview_generation' => [
'files_location' => 'data/cache',
],
];

View File

@@ -5,7 +5,7 @@ declare(strict_types=1);
return [
'not_found_redirects' => [
'invalid_short_url' => null, // Formerly url_shortener.not_found_short_url.redirect_to
'invalid_short_url' => null,
'regular_404' => null,
'base_url' => null,
],

View File

@@ -1,13 +1,16 @@
<?php
declare(strict_types=1);
return [
'redis' => [
'servers' => 'tcp://shlink_redis:6379',
// 'servers' => [
// 'tcp://shlink_redis:6379',
// ],
'cache' => [
'redis' => [
'servers' => 'tcp://shlink_redis:6379',
// 'servers' => [
// 'tcp://shlink_redis:6379',
// ],
],
],
'dependencies' => [

View File

@@ -0,0 +1,38 @@
<?php
declare(strict_types=1);
use Laminas\ServiceManager\AbstractFactory\ConfigAbstractFactory;
use Laminas\ServiceManager\Factory\InvokableFactory;
use PhpMiddleware\RequestId;
return [
'request_id' => [
'allow_override' => true,
'header_name' => 'X-Request-Id',
],
'dependencies' => [
'factories' => [
RequestId\Generator\RamseyUuid4StaticGenerator::class => InvokableFactory::class,
RequestId\RequestIdProviderFactory::class => ConfigAbstractFactory::class,
RequestId\RequestIdMiddleware::class => ConfigAbstractFactory::class,
RequestId\MonologProcessor::class => ConfigAbstractFactory::class,
],
],
ConfigAbstractFactory::class => [
RequestId\RequestIdProviderFactory::class => [
RequestId\Generator\RamseyUuid4StaticGenerator::class,
'config.request_id.allow_override',
'config.request_id.header_name',
],
RequestId\RequestIdMiddleware::class => [
RequestId\RequestIdProviderFactory::class,
'config.request_id.header_name',
],
RequestId\MonologProcessor::class => [RequestId\RequestIdMiddleware::class],
],
];

View File

@@ -2,7 +2,7 @@
declare(strict_types=1);
use Zend\Expressive\Router\FastRouteRouter;
use Mezzio\Router\FastRouteRouter;
return [

View File

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

View File

@@ -4,8 +4,9 @@ declare(strict_types=1);
return [
'zend-expressive-swoole' => [
'enable_coroutine' => true,
'mezzio-swoole' => [
// Setting this to true can have unexpected behaviors when running several concurrent slow DB queries
'enable_coroutine' => false,
'swoole-http-server' => [
'host' => '0.0.0.0',

View File

@@ -1,21 +1,13 @@
<?php
declare(strict_types=1);
use Zend\Expressive\Swoole\HotCodeReload\FileWatcher\InotifyFileWatcher;
use Zend\ServiceManager\Factory\InvokableFactory;
declare(strict_types=1);
return [
'zend-expressive-swoole' => [
'mezzio-swoole' => [
'hot-code-reload' => [
'enable' => true,
],
],
'dependencies' => [
'factories' => [
InotifyFileWatcher::class => InvokableFactory::class,
],
],
];

View File

@@ -2,16 +2,23 @@
declare(strict_types=1);
use function Shlinkio\Shlink\Common\env;
use const Shlinkio\Shlink\Core\DEFAULT_REDIRECT_CACHE_LIFETIME;
use const Shlinkio\Shlink\Core\DEFAULT_REDIRECT_STATUS_CODE;
use const Shlinkio\Shlink\Core\DEFAULT_SHORT_CODES_LENGTH;
return [
'url_shortener' => [
'domain' => [
'schema' => env('SHORTENED_URL_SCHEMA', 'http'),
'hostname' => env('SHORTENED_URL_HOSTNAME'),
'schema' => 'https',
'hostname' => '',
],
'validate_url' => true,
'validate_url' => false,
'anonymize_remote_addr' => true,
'visits_webhooks' => [],
'default_short_codes_length' => DEFAULT_SHORT_CODES_LENGTH,
'redirect_status_code' => DEFAULT_REDIRECT_STATUS_CODE,
'redirect_cache_lifetime' => DEFAULT_REDIRECT_CACHE_LIFETIME,
],
];

View File

@@ -0,0 +1,14 @@
<?php
declare(strict_types=1);
return [
'url_shortener' => [
'domain' => [
'schema' => 'http',
'hostname' => 'localhost:8080',
],
],
];

View File

@@ -1,14 +0,0 @@
<?php
declare(strict_types=1);
return [
'wkhtmltopdf' => [
'images' => [
'binary' => __DIR__ . '/../../bin/wkhtmltoimage',
'type' => 'jpg',
],
],
];

View File

@@ -5,10 +5,9 @@ declare(strict_types=1);
use Doctrine\ORM\EntityManager;
use Doctrine\ORM\Tools\Console\ConsoleRunner;
use Psr\Container\ContainerInterface;
use Zend\ServiceManager\ServiceManager;
return (function () {
/** @var ContainerInterface|ServiceManager $container */
/** @var ContainerInterface $container */
$container = include __DIR__ . '/container.php';
$em = $container->get(EntityManager::class);

View File

@@ -4,30 +4,33 @@ declare(strict_types=1);
namespace Shlinkio\Shlink;
use Acelaya\ExpressiveErrorHandler;
use Zend\ConfigAggregator;
use Zend\Expressive;
use Laminas\ConfigAggregator;
use Laminas\Diactoros;
use Mezzio;
use Mezzio\ProblemDetails;
use function Shlinkio\Shlink\Common\env;
return (new ConfigAggregator\ConfigAggregator([
Expressive\ConfigProvider::class,
Expressive\Router\ConfigProvider::class,
Expressive\Router\FastRouteRouter\ConfigProvider::class,
Expressive\Plates\ConfigProvider::class,
Expressive\Swoole\ConfigProvider::class,
ExpressiveErrorHandler\ConfigProvider::class,
Mezzio\ConfigProvider::class,
Mezzio\Router\ConfigProvider::class,
Mezzio\Router\FastRouteRouter\ConfigProvider::class,
Mezzio\Plates\ConfigProvider::class,
Mezzio\Swoole\ConfigProvider::class,
ProblemDetails\ConfigProvider::class,
Diactoros\ConfigProvider::class,
Common\ConfigProvider::class,
Config\ConfigProvider::class,
Importer\ConfigProvider::class,
IpGeolocation\ConfigProvider::class,
EventDispatcher\ConfigProvider::class,
Core\ConfigProvider::class,
CLI\ConfigProvider::class,
Rest\ConfigProvider::class,
EventDispatcher\ConfigProvider::class,
PreviewGenerator\ConfigProvider::class,
new ConfigAggregator\PhpFileProvider('config/autoload/{{,*.}global,{,*.}local}.php'),
env('APP_ENV') === 'test'
? new ConfigAggregator\PhpFileProvider('config/test/*.global.php')
: new ConfigAggregator\ZendConfigProvider('config/params/{generated_config.php,*.config.{php,json}}'),
: new ConfigAggregator\LaminasConfigProvider('config/params/{generated_config.php,*.config.{php,json}}'),
], 'data/cache/app_config.php', [
Core\Config\SimplifiedConfigParser::class,
Core\Config\BasePathPrefixer::class,

View File

@@ -2,23 +2,26 @@
declare(strict_types=1);
use Symfony\Component\Dotenv\Dotenv;
use Zend\ServiceManager\ServiceManager;
use Laminas\ServiceManager\ServiceManager;
use Symfony\Component\Lock;
use const Shlinkio\Shlink\Core\LOCAL_LOCK_FACTORY;
chdir(dirname(__DIR__));
require 'vendor/autoload.php';
// If the Dotenv class exists, load env vars and enable errors
if (class_exists(Dotenv::class)) {
error_reporting(E_ALL);
ini_set('display_errors', '1');
$dotenv = new Dotenv();
$dotenv->load(__DIR__ . '/../.env');
// This class alias tricks the ConfigAbstractFactory to return Lock\Factory instances even with a different service name
// It needs to be placed here as individual config files will not be loaded once config is cached
if (! class_exists(LOCAL_LOCK_FACTORY)) {
class_alias(Lock\LockFactory::class, LOCAL_LOCK_FACTORY);
}
// Build container
$config = require __DIR__ . '/config.php';
$container = new ServiceManager($config['dependencies']);
$container->setService('config', $config);
return $container;
return (function () {
$config = require __DIR__ . '/config.php';
$container = new ServiceManager($config['dependencies']);
$container->setService('config', $config);
return $container;
})();

15
config/run.php Normal file
View File

@@ -0,0 +1,15 @@
<?php
declare(strict_types=1);
use Mezzio\Application;
use Psr\Container\ContainerInterface;
use Symfony\Component\Console\Application as CliApp;
return function (bool $isCli = false): void {
/** @var ContainerInterface $container */
$container = include __DIR__ . '/container.php';
$app = $container->get($isCli ? CliApp::class : Application::class);
$app->run();
};

View File

@@ -7,22 +7,28 @@ namespace Shlinkio\Shlink\TestUtils;
use Doctrine\ORM\EntityManager;
use Psr\Container\ContainerInterface;
use function file_exists;
use function touch;
use function register_shutdown_function;
use function sprintf;
// Create an empty .env file
if (! file_exists('.env')) {
touch('.env');
}
use const ShlinkioTest\Shlink\SWOOLE_TESTING_HOST;
use const ShlinkioTest\Shlink\SWOOLE_TESTING_PORT;
/** @var ContainerInterface $container */
$container = require __DIR__ . '/../container.php';
$testHelper = $container->get(Helper\TestHelper::class);
$config = $container->get('config');
$em = $container->get(EntityManager::class);
$httpClient = $container->get('shlink_test_api_client');
// Start code coverage collecting on swoole process, and stop it when process shuts down
$httpClient->request('GET', sprintf('http://%s:%s/api-tests/start-coverage', SWOOLE_TESTING_HOST, SWOOLE_TESTING_PORT));
register_shutdown_function(function () use ($httpClient): void {
$httpClient->request(
'GET',
sprintf('http://%s:%s/api-tests/stop-coverage', SWOOLE_TESTING_HOST, SWOOLE_TESTING_PORT),
);
});
$testHelper->createTestDb();
ApiTest\ApiTestCase::setApiClient($container->get('shlink_test_api_client'));
ApiTest\ApiTestCase::setSeedFixturesCallback(function () use ($testHelper, $em, $config) {
$testHelper->seedFixtures($em, $config['data_fixtures'] ?? []);
});
ApiTest\ApiTestCase::setApiClient($httpClient);
ApiTest\ApiTestCase::setSeedFixturesCallback(fn () => $testHelper->seedFixtures($em, $config['data_fixtures'] ?? []));

View File

@@ -6,14 +6,6 @@ namespace Shlinkio\Shlink\TestUtils;
use Psr\Container\ContainerInterface;
use function file_exists;
use function touch;
// Create an empty .env file
if (! file_exists('.env')) {
touch('.env');
}
/** @var ContainerInterface $container */
$container = require __DIR__ . '/../container.php';
$container->get(Helper\TestHelper::class)->createTestDb();

View File

@@ -0,0 +1,8 @@
<?php
declare(strict_types=1);
namespace ShlinkioTest\Shlink;
const SWOOLE_TESTING_HOST = '127.0.0.1';
const SWOOLE_TESTING_PORT = 9999;

View File

@@ -5,23 +5,40 @@ declare(strict_types=1);
namespace Shlinkio\Shlink;
use GuzzleHttp\Client;
use Laminas\ConfigAggregator\ConfigAggregator;
use Laminas\Diactoros\Response\EmptyResponse;
use Laminas\ServiceManager\Factory\InvokableFactory;
use Laminas\Stdlib\Glob;
use PDO;
use Zend\ConfigAggregator\ConfigAggregator;
use Zend\ServiceManager\Factory\InvokableFactory;
use PHPUnit\Runner\Version;
use SebastianBergmann\CodeCoverage\CodeCoverage;
use SebastianBergmann\CodeCoverage\Driver\Selector;
use SebastianBergmann\CodeCoverage\Filter;
use SebastianBergmann\CodeCoverage\Report\PHP;
use SebastianBergmann\CodeCoverage\Report\Xml\Facade as Xml;
use function Laminas\Stratigility\middleware;
use function Shlinkio\Shlink\Common\env;
use function sprintf;
use function sys_get_temp_dir;
$swooleTestingHost = '127.0.0.1';
$swooleTestingPort = 9999;
use const ShlinkioTest\Shlink\SWOOLE_TESTING_HOST;
use const ShlinkioTest\Shlink\SWOOLE_TESTING_PORT;
$isApiTest = env('TEST_ENV') === 'api';
if ($isApiTest) {
$filter = new Filter();
foreach (Glob::glob(__DIR__ . '/../../module/*/src') as $item) {
$filter->includeDirectory($item);
}
$coverage = new CodeCoverage((new Selector())->forLineCoverage($filter), $filter);
}
$buildDbConnection = function (): array {
$driver = env('DB_DRIVER', 'sqlite');
$isCi = env('TRAVIS', false);
$getMysqlHost = function (string $driver) {
return sprintf('shlink_db%s', $driver === 'mysql' ? '' : '_maria');
};
$getMysqlHost = fn (string $driver) => sprintf('shlink_db%s', $driver === 'mysql' ? '' : '_maria');
$getCiMysqlPort = fn (string $driver) => $driver === 'mysql' ? '3307' : '3308';
$driverConfigMap = [
'sqlite' => [
@@ -31,22 +48,32 @@ $buildDbConnection = function (): array {
'mysql' => [
'driver' => 'pdo_mysql',
'host' => $isCi ? '127.0.0.1' : $getMysqlHost($driver),
'port' => $isCi ? $getCiMysqlPort($driver) : '3306',
'user' => 'root',
'password' => $isCi ? '' : 'root',
'password' => 'root',
'dbname' => 'shlink_test',
'charset' => 'utf8',
'driverOptions' => [
PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8',
PDO::MYSQL_ATTR_USE_BUFFERED_QUERY => true,
],
],
'postgres' => [
'driver' => 'pdo_pgsql',
'host' => $isCi ? '127.0.0.1' : 'shlink_db_postgres',
'port' => $isCi ? '5433' : '5432',
'user' => 'postgres',
'password' => $isCi ? '' : 'root',
'password' => 'root',
'dbname' => 'shlink_test',
'charset' => 'utf8',
],
'mssql' => [
'driver' => 'pdo_sqlsrv',
'host' => $isCi ? '127.0.0.1' : 'shlink_db_ms',
'user' => 'sa',
'password' => 'Passw0rd!',
'dbname' => 'shlink_test',
],
];
$driverConfigMap['maria'] = $driverConfigMap['mysql'];
@@ -63,27 +90,61 @@ return [
'schema' => 'http',
'hostname' => 'doma.in',
],
'validate_url' => true,
],
'zend-expressive-swoole' => [
'mezzio-swoole' => [
'enable_coroutine' => false,
'swoole-http-server' => [
'host' => $swooleTestingHost,
'port' => $swooleTestingPort,
'host' => SWOOLE_TESTING_HOST,
'port' => SWOOLE_TESTING_PORT,
'process-name' => 'shlink_test',
'options' => [
'pid_file' => sys_get_temp_dir() . '/shlink-test-swoole.pid',
'worker_num' => 1,
'task_worker_num' => 1,
'enable_coroutine' => false,
],
],
],
'routes' => !$isApiTest ? [] : [
[
'name' => 'start_collecting_coverage',
'path' => '/api-tests/start-coverage',
'middleware' => middleware(static function () use (&$coverage) {
if ($coverage) {
$coverage->start('API tests');
}
return new EmptyResponse();
}),
'allowed_methods' => ['GET'],
],
[
'name' => 'dump_coverage',
'path' => '/api-tests/stop-coverage',
'middleware' => middleware(static function () use (&$coverage) {
if ($coverage) {
$basePath = __DIR__ . '/../../build/coverage-api';
$coverage->stop();
(new PHP())->process($coverage, $basePath . '.cov');
(new Xml(Version::getVersionString()))->process($coverage, $basePath . '/coverage-xml');
}
return new EmptyResponse();
}),
'allowed_methods' => ['GET'],
],
],
'mercure' => [
'public_hub_url' => null,
'internal_hub_url' => null,
'jwt_secret' => null,
],
'dependencies' => [
'services' => [
'shlink_test_api_client' => new Client([
'base_uri' => sprintf('http://%s:%s/', $swooleTestingHost, $swooleTestingPort),
'base_uri' => sprintf('http://%s:%s/', SWOOLE_TESTING_HOST, SWOOLE_TESTING_PORT),
'http_errors' => false,
]),
],

View File

@@ -0,0 +1,9 @@
#!/usr/bin/env bash
set -ex
curl https://packages.microsoft.com/keys/microsoft.asc | apt-key add -
curl https://packages.microsoft.com/config/ubuntu/16.04/prod.list > /etc/apt/sources.list.d/mssql-release.list
apt-get update
ACCEPT_EULA=Y apt-get install msodbcsql17
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/php7.2-fpm.sock;
fastcgi_pass unix:/var/run/php/php7.4-fpm.sock;
fastcgi_index index.php;
include fastcgi.conf;
}

View File

@@ -8,7 +8,7 @@
# Description: Shlink non-blocking server with swoole
### END INIT INFO
SCRIPT=/path/to/shlink/vendor/bin/zend-expressive-swoole\ start
SCRIPT=/path/to/shlink/vendor/bin/mezzio-swoole\ start
RUNAS=root
PIDFILE=/var/run/shlink_swoole.pid

View File

@@ -0,0 +1,17 @@
server {
listen 80 default_server;
error_log /home/shlink/www/data/infra/nginx/mercure_proxy.error.log;
location / {
proxy_pass http://shlink_mercure;
proxy_read_timeout 24h;
proxy_http_version 1.1;
proxy_set_header Connection "";
## Be sure to set USE_FORWARDED_HEADERS=1 to allow the hub to use those headers ##
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Proto $scheme;
}
}

View File

@@ -1,18 +1,19 @@
FROM php:7.3.11-fpm-alpine3.10
FROM php:7.4.11-alpine3.12
MAINTAINER Alejandro Celaya <alejandro@alejandrocelaya.com>
ENV APCU_VERSION 5.1.18
ENV APCU_BC_VERSION 1.0.5
ENV XDEBUG_VERSION 2.8.0
RUN apk update
# Install common php extensions
RUN docker-php-ext-install pdo_mysql
RUN docker-php-ext-install iconv
RUN docker-php-ext-install mbstring
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
@@ -29,6 +30,9 @@ RUN docker-php-ext-install gd
RUN apk add --no-cache postgresql-dev
RUN docker-php-ext-install pdo_pgsql
RUN apk add --no-cache gmp-dev
RUN docker-php-ext-install gmp
# 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\
@@ -53,20 +57,17 @@ RUN rm /tmp/apcu_bc.tar.gz
RUN rm /usr/local/etc/php/conf.d/docker-php-ext-apcu.ini
RUN echo extension=apcu.so > /usr/local/etc/php/conf.d/20-php-ext-apcu.ini
# Install xdebug
ADD https://pecl.php.net/get/xdebug-$XDEBUG_VERSION /tmp/xdebug.tar.gz
RUN mkdir -p /usr/src/php/ext/xdebug\
&& tar xf /tmp/xdebug.tar.gz -C /usr/src/php/ext/xdebug --strip-components=1
# configure and install
RUN docker-php-ext-configure xdebug\
&& docker-php-ext-install xdebug
# cleanup
RUN rm /tmp/xdebug.tar.gz
# Install pcov and sqlsrv driver
RUN wget https://download.microsoft.com/download/e/4/e/e4e67866-dffd-428c-aac7-8d28ddafb39b/msodbcsql17_17.5.1.1-1_amd64.apk && \
apk add --allow-untrusted msodbcsql17_17.5.1.1-1_amd64.apk && \
apk add --no-cache --virtual .phpize-deps $PHPIZE_DEPS unixodbc-dev && \
pecl install pdo_sqlsrv pcov && \
docker-php-ext-enable pdo_sqlsrv pcov && \
apk del .phpize-deps && \
rm msodbcsql17_17.5.1.1-1_amd64.apk
# Install composer
RUN php -r "readfile('https://getcomposer.org/installer');" | php
RUN chmod +x composer.phar
RUN mv composer.phar /usr/local/bin/composer
COPY --from=composer:2 /usr/bin/composer /usr/local/bin/composer
# Make home directory writable by anyone
RUN chmod 777 /home

View File

@@ -1 +1,8 @@
date.timezone = Europe/Madrid
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,19 +1,21 @@
FROM php:7.3.11-alpine3.10
FROM php:7.4.11-alpine3.12
MAINTAINER Alejandro Celaya <alejandro@alejandrocelaya.com>
ENV APCU_VERSION 5.1.18
ENV APCU_BC_VERSION 1.0.5
ENV INOTIFY_VERSION 2.0.0
ENV SWOOLE_VERSION 4.4.12
ENV SWOOLE_VERSION 4.5.5
RUN apk update
# Install common php extensions
RUN docker-php-ext-install pdo_mysql
RUN docker-php-ext-install iconv
RUN docker-php-ext-install mbstring
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
@@ -30,6 +32,9 @@ RUN docker-php-ext-install gd
RUN apk add --no-cache postgresql-dev
RUN docker-php-ext-install pdo_pgsql
RUN apk add --no-cache gmp-dev
RUN docker-php-ext-install gmp
# 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\
@@ -64,17 +69,17 @@ RUN docker-php-ext-configure inotify\
# cleanup
RUN rm /tmp/inotify.tar.gz
# Install swoole
# First line fixes an error when installing pecl extensions. Found in https://github.com/docker-library/php/issues/233
RUN apk add --no-cache --virtual .phpize-deps $PHPIZE_DEPS && \
pecl install swoole-${SWOOLE_VERSION} && \
docker-php-ext-enable swoole && \
apk del .phpize-deps
# Install swoole, pcov and mssql driver
RUN wget https://download.microsoft.com/download/e/4/e/e4e67866-dffd-428c-aac7-8d28ddafb39b/msodbcsql17_17.5.1.1-1_amd64.apk && \
apk add --allow-untrusted msodbcsql17_17.5.1.1-1_amd64.apk && \
apk add --no-cache --virtual .phpize-deps $PHPIZE_DEPS unixodbc-dev && \
pecl install swoole-${SWOOLE_VERSION} pdo_sqlsrv pcov && \
docker-php-ext-enable swoole pdo_sqlsrv pcov && \
apk del .phpize-deps && \
rm msodbcsql17_17.5.1.1-1_amd64.apk
# Install composer
RUN php -r "readfile('https://getcomposer.org/installer');" | php
RUN chmod +x composer.phar
RUN mv composer.phar /usr/local/bin/composer
COPY --from=composer:2 /usr/bin/composer /usr/local/bin/composer
# Make home directory writable by anyone
RUN chmod 777 /home
@@ -90,4 +95,4 @@ CMD \
if [[ ! -d "./vendor" ]]; then /usr/local/bin/composer install ; fi && \
# When restarting the container, swoole might think it is already in execution
# This forces the app to be started every second until the exit code is 0
until php ./vendor/bin/zend-expressive-swoole start; do sleep 1 ; done
until php ./vendor/bin/mezzio-swoole start; do sleep 1 ; done

View File

@@ -0,0 +1,14 @@
server {
listen 80 default_server;
error_log /home/shlink/www/data/infra/nginx/swoole_proxy.error.log;
location / {
proxy_http_version 1.1;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_pass http://shlink_swoole:8080;
proxy_read_timeout 90s;
}
}

View File

@@ -5,7 +5,7 @@ declare(strict_types=1);
namespace ShlinkMigrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\DBAL\Types\Type;
use Doctrine\DBAL\Types\Types;
use Doctrine\Migrations\AbstractMigration;
/**
@@ -30,12 +30,12 @@ class Version20160820191203 extends AbstractMigration
private function createTagsTable(Schema $schema): void
{
$table = $schema->createTable('tags');
$table->addColumn('id', Type::BIGINT, [
$table->addColumn('id', Types::BIGINT, [
'unsigned' => true,
'autoincrement' => true,
'notnull' => true,
]);
$table->addColumn('name', Type::STRING, [
$table->addColumn('name', Types::STRING, [
'length' => 255,
'notnull' => true,
]);
@@ -47,11 +47,11 @@ class Version20160820191203 extends AbstractMigration
private function createShortUrlsInTagsTable(Schema $schema): void
{
$table = $schema->createTable('short_urls_in_tags');
$table->addColumn('short_url_id', Type::BIGINT, [
$table->addColumn('short_url_id', Types::BIGINT, [
'unsigned' => true,
'notnull' => true,
]);
$table->addColumn('tag_id', Type::BIGINT, [
$table->addColumn('tag_id', Types::BIGINT, [
'unsigned' => true,
'notnull' => true,
]);

View File

@@ -6,7 +6,7 @@ namespace ShlinkMigrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\DBAL\Schema\SchemaException;
use Doctrine\DBAL\Types\Type;
use Doctrine\DBAL\Types\Types;
use Doctrine\Migrations\AbstractMigration;
/**
@@ -24,10 +24,10 @@ class Version20171021093246 extends AbstractMigration
return;
}
$shortUrls->addColumn('valid_since', Type::DATETIME, [
$shortUrls->addColumn('valid_since', Types::DATETIME_MUTABLE, [
'notnull' => false,
]);
$shortUrls->addColumn('valid_until', Type::DATETIME, [
$shortUrls->addColumn('valid_until', Types::DATETIME_MUTABLE, [
'notnull' => false,
]);
}

View File

@@ -6,7 +6,7 @@ namespace ShlinkMigrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\DBAL\Schema\SchemaException;
use Doctrine\DBAL\Types\Type;
use Doctrine\DBAL\Types\Types;
use Doctrine\Migrations\AbstractMigration;
/**
@@ -24,7 +24,7 @@ class Version20171022064541 extends AbstractMigration
return;
}
$shortUrls->addColumn('max_visits', Type::INTEGER, [
$shortUrls->addColumn('max_visits', Types::INTEGER, [
'unsigned' => true,
'notnull' => false,
]);

View File

@@ -17,7 +17,6 @@ final class Version20180801183328 extends AbstractMigration
private const OLD_SIZE = 10;
/**
* @param Schema $schema
* @throws SchemaException
*/
public function up(Schema $schema): void
@@ -26,7 +25,6 @@ final class Version20180801183328 extends AbstractMigration
}
/**
* @param Schema $schema
* @throws SchemaException
*/
public function down(Schema $schema): void
@@ -35,8 +33,6 @@ final class Version20180801183328 extends AbstractMigration
}
/**
* @param Schema $schema
* @param int $size
* @throws SchemaException
*/
private function setSize(Schema $schema, int $size): void

View File

@@ -17,7 +17,6 @@ use Shlinkio\Shlink\Common\Util\IpAddress;
final class Version20180913205455 extends AbstractMigration
{
/**
* @param Schema $schema
*/
public function up(Schema $schema): void
{
@@ -25,7 +24,6 @@ final class Version20180913205455 extends AbstractMigration
}
/**
* @param Schema $schema
* @throws DBALException
*/
public function postUp(Schema $schema): void
@@ -67,7 +65,6 @@ final class Version20180913205455 extends AbstractMigration
}
/**
* @param Schema $schema
*/
public function down(Schema $schema): void
{

View File

@@ -19,7 +19,6 @@ final class Version20180915110857 extends AbstractMigration
];
/**
* @param Schema $schema
* @throws SchemaException
*/
public function up(Schema $schema): void
@@ -39,7 +38,7 @@ final class Version20180915110857 extends AbstractMigration
[
'onDelete' => self::ON_DELETE_MAP[$foreignTable],
'onUpdate' => 'RESTRICT',
]
],
);
}
}

View File

@@ -8,7 +8,7 @@ use Doctrine\DBAL\DBALException;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\DBAL\Schema\SchemaException;
use Doctrine\DBAL\Schema\Table;
use Doctrine\DBAL\Types\Type;
use Doctrine\DBAL\Types\Types;
use Doctrine\Migrations\AbstractMigration;
/**
@@ -24,7 +24,6 @@ final class Version20181020060559 extends AbstractMigration
];
/**
* @param Schema $schema
* @throws SchemaException
*/
public function up(Schema $schema): void
@@ -36,7 +35,7 @@ final class Version20181020060559 extends AbstractMigration
{
foreach ($columnNames as $name) {
if (! $visitLocations->hasColumn($name)) {
$visitLocations->addColumn($name, Type::STRING, ['notnull' => false]);
$visitLocations->addColumn($name, Types::STRING, ['notnull' => false]);
}
}
}

View File

@@ -6,7 +6,7 @@ namespace ShlinkMigrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\DBAL\Schema\SchemaException;
use Doctrine\DBAL\Types\Type;
use Doctrine\DBAL\Types\Types;
use Doctrine\Migrations\AbstractMigration;
final class Version20190930165521 extends AbstractMigration
@@ -22,19 +22,19 @@ final class Version20190930165521 extends AbstractMigration
}
$domains = $schema->createTable('domains');
$domains->addColumn('id', Type::BIGINT, [
$domains->addColumn('id', Types::BIGINT, [
'unsigned' => true,
'autoincrement' => true,
'notnull' => true,
]);
$domains->addColumn('authority', Type::STRING, [
$domains->addColumn('authority', Types::STRING, [
'length' => 512,
'notnull' => true,
]);
$domains->addUniqueIndex(['authority']);
$domains->setPrimaryKey(['id']);
$shortUrls->addColumn('domain_id', Type::BIGINT, [
$shortUrls->addColumn('domain_id', Types::BIGINT, [
'unsigned' => true,
'notnull' => false,
]);

View File

@@ -0,0 +1,96 @@
<?php
declare(strict_types=1);
namespace ShlinkMigrations;
use Doctrine\DBAL\DBALException;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\DBAL\Types\Types;
use Doctrine\Migrations\AbstractMigration;
use function Functional\some;
final class Version20200105165647 extends AbstractMigration
{
private const COLUMNS = ['lat' => 'latitude', 'lon' => 'longitude'];
/**
* @throws DBALException
*/
public function preUp(Schema $schema): void
{
$visitLocations = $schema->getTable('visit_locations');
$this->skipIf(some(
self::COLUMNS,
fn (string $v, string $newColName) => $visitLocations->hasColumn($newColName),
), 'New columns already exist');
foreach (self::COLUMNS as $columnName) {
$qb = $this->connection->createQueryBuilder();
$qb->update('visit_locations')
->set($columnName, ':zeroValue')
->where($qb->expr()->orX(
$qb->expr()->eq($columnName, ':emptyString'),
$qb->expr()->isNull($columnName),
))
->setParameters([
'zeroValue' => '0',
'emptyString' => '',
])
->execute();
}
}
/**
* @throws DBALException
*/
public function up(Schema $schema): void
{
$visitLocations = $schema->getTable('visit_locations');
foreach (self::COLUMNS as $newName => $oldName) {
$visitLocations->addColumn($newName, Types::FLOAT, [
'default' => '0.0',
]);
}
}
/**
* @throws DBALException
*/
public function postUp(Schema $schema): void
{
$platformName = $this->connection->getDatabasePlatform()->getName();
$castType = $platformName === 'postgres' ? 'DOUBLE PRECISION' : 'DECIMAL(9,2)';
foreach (self::COLUMNS as $newName => $oldName) {
$qb = $this->connection->createQueryBuilder();
$qb->update('visit_locations')
->set($newName, 'CAST(' . $oldName . ' AS ' . $castType . ')')
->execute();
}
}
public function preDown(Schema $schema): void
{
foreach (self::COLUMNS as $newName => $oldName) {
$qb = $this->connection->createQueryBuilder();
$qb->update('visit_locations')
->set($oldName, $newName)
->execute();
}
}
/**
* @throws DBALException
*/
public function down(Schema $schema): void
{
$visitLocations = $schema->getTable('visit_locations');
foreach (self::COLUMNS as $colName => $oldName) {
$visitLocations->dropColumn($colName);
}
}
}

View File

@@ -0,0 +1,47 @@
<?php
declare(strict_types=1);
namespace ShlinkMigrations;
use Doctrine\DBAL\DBALException;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\DBAL\Types\Types;
use Doctrine\Migrations\AbstractMigration;
use function Functional\none;
final class Version20200106215144 extends AbstractMigration
{
private const COLUMNS = ['latitude', 'longitude'];
/**
* @throws DBALException
*/
public function up(Schema $schema): void
{
$visitLocations = $schema->getTable('visit_locations');
$this->skipIf(none(
self::COLUMNS,
fn (string $oldColName) => $visitLocations->hasColumn($oldColName),
), 'Old columns do not exist');
foreach (self::COLUMNS as $colName) {
$visitLocations->dropColumn($colName);
}
}
/**
* @throws DBALException
*/
public function down(Schema $schema): void
{
$visitLocations = $schema->getTable('visit_locations');
foreach (self::COLUMNS as $colName) {
$visitLocations->addColumn($colName, Types::STRING, [
'notnull' => false,
]);
}
}
}

View File

@@ -0,0 +1,53 @@
<?php
declare(strict_types=1);
namespace ShlinkMigrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
use function Functional\each;
use function Functional\partial_left;
final class Version20200110182849 extends AbstractMigration
{
private const DEFAULT_EMPTY_VALUE = '';
private const COLUMN_DEFAULTS_MAP = [
'visits' => [
'referer',
'user_agent',
],
'visit_locations' => [
'timezone',
'country_code',
'country_name',
'region_name',
'city_name',
],
];
public function up(Schema $schema): void
{
each(
self::COLUMN_DEFAULTS_MAP,
fn (array $columns, string $tableName) =>
each($columns, partial_left([$this, 'setDefaultValueForColumnInTable'], $tableName)),
);
}
public function setDefaultValueForColumnInTable(string $tableName, string $columnName): void
{
$qb = $this->connection->createQueryBuilder();
$qb->update($tableName)
->set($columnName, ':emptyValue')
->setParameter('emptyValue', self::DEFAULT_EMPTY_VALUE)
->where($qb->expr()->isNull($columnName))
->execute();
}
public function down(Schema $schema): void
{
// No need (and no way) to undo this migration
}
}

View File

@@ -0,0 +1,45 @@
<?php
declare(strict_types=1);
namespace ShlinkMigrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\DBAL\Types\Types;
use Doctrine\Migrations\AbstractMigration;
final class Version20200323190014 extends AbstractMigration
{
public function up(Schema $schema): void
{
$visitLocations = $schema->getTable('visit_locations');
$this->skipIf($visitLocations->hasColumn('is_empty'));
$visitLocations->addColumn('is_empty', Types::BOOLEAN, ['default' => false]);
}
public function postUp(Schema $schema): void
{
$qb = $this->connection->createQueryBuilder();
$qb->update('visit_locations')
->set('is_empty', ':isEmpty')
->where($qb->expr()->eq('country_code', ':emptyString'))
->andWhere($qb->expr()->eq('country_name', ':emptyString'))
->andWhere($qb->expr()->eq('region_name', ':emptyString'))
->andWhere($qb->expr()->eq('city_name', ':emptyString'))
->andWhere($qb->expr()->eq('timezone', ':emptyString'))
->andWhere($qb->expr()->eq('lat', 0))
->andWhere($qb->expr()->eq('lon', 0))
->setParameter('isEmpty', true)
->setParameter('emptyString', '')
->execute();
}
public function down(Schema $schema): void
{
$visitLocations = $schema->getTable('visit_locations');
$this->skipIf(!$visitLocations->hasColumn('is_empty'));
$visitLocations->dropColumn('is_empty');
}
}

View File

@@ -0,0 +1,27 @@
<?php
declare(strict_types=1);
namespace ShlinkMigrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
final class Version20200503170404 extends AbstractMigration
{
private const INDEX_NAME = 'IDX_visits_date';
public function up(Schema $schema): void
{
$visits = $schema->getTable('visits');
$this->skipIf($visits->hasIndex(self::INDEX_NAME));
$visits->addIndex(['date'], self::INDEX_NAME);
}
public function down(Schema $schema): void
{
$visits = $schema->getTable('visits');
$this->skipIf(! $visits->hasIndex(self::INDEX_NAME));
$visits->dropIndex(self::INDEX_NAME);
}
}

View File

@@ -0,0 +1,44 @@
<?php
declare(strict_types=1);
namespace ShlinkMigrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\DBAL\Types\Types;
use Doctrine\Migrations\AbstractMigration;
final class Version20201023090929 extends AbstractMigration
{
private const IMPORT_SOURCE_COLUMN = 'import_source';
public function up(Schema $schema): void
{
$shortUrls = $schema->getTable('short_urls');
$this->skipIf($shortUrls->hasColumn(self::IMPORT_SOURCE_COLUMN));
$shortUrls->addColumn(self::IMPORT_SOURCE_COLUMN, Types::STRING, [
'length' => 255,
'notnull' => false,
]);
$shortUrls->addColumn('import_original_short_code', Types::STRING, [
'length' => 255,
'notnull' => false,
]);
$shortUrls->addUniqueIndex(
[self::IMPORT_SOURCE_COLUMN, 'import_original_short_code', 'domain_id'],
'unique_imports',
);
}
public function down(Schema $schema): void
{
$shortUrls = $schema->getTable('short_urls');
$this->skipIf(! $shortUrls->hasColumn(self::IMPORT_SOURCE_COLUMN));
$shortUrls->dropColumn(self::IMPORT_SOURCE_COLUMN);
$shortUrls->dropColumn('import_original_short_code');
$shortUrls->dropIndex('unique_imports');
}
}

View File

@@ -0,0 +1,86 @@
<?php
declare(strict_types=1);
namespace ShlinkMigrations;
use Cake\Chronos\Chronos;
use Doctrine\DBAL\Driver\Result;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\DBAL\Types\Types;
use Doctrine\Migrations\AbstractMigration;
final class Version20201102113208 extends AbstractMigration
{
private const API_KEY_COLUMN = 'author_api_key_id';
public function up(Schema $schema): void
{
$shortUrls = $schema->getTable('short_urls');
$this->skipIf($shortUrls->hasColumn(self::API_KEY_COLUMN));
$shortUrls->addColumn(self::API_KEY_COLUMN, Types::BIGINT, [
'unsigned' => true,
'notnull' => false,
]);
$shortUrls->addForeignKeyConstraint('api_keys', [self::API_KEY_COLUMN], ['id'], [
'onDelete' => 'SET NULL',
'onUpdate' => 'RESTRICT',
], 'FK_' . self::API_KEY_COLUMN);
}
public function postUp(Schema $schema): void
{
// If there's only one API key and it's active, link all existing URLs with it
$qb = $this->connection->createQueryBuilder();
$qb->select('id')
->from('api_keys')
->where($qb->expr()->eq('enabled', ':enabled'))
->andWhere($qb->expr()->or(
$qb->expr()->isNull('expiration_date'),
$qb->expr()->gt('expiration_date', ':expiration'),
))
->setParameters([
'enabled' => true,
'expiration' => Chronos::now()->toDateTimeString(),
]);
/** @var Result $result */
$result = $qb->execute();
$id = $this->resolveOneApiKeyId($result);
if ($id === null) {
return;
}
$qb = $this->connection->createQueryBuilder();
$qb->update('short_urls')
->set(self::API_KEY_COLUMN, ':apiKeyId')
->setParameter('apiKeyId', $id)
->execute();
}
private function resolveOneApiKeyId(Result $result): ?string
{
$results = [];
while ($row = $result->fetchAssociative()) {
// As soon as we have to iterate more than once, then we cannot resolve a single API key
if (! empty($results)) {
return null;
}
$results[] = $row['id'] ?? null;
}
return $results[0] ?? null;
}
public function down(Schema $schema): void
{
$shortUrls = $schema->getTable('short_urls');
$this->skipIf(! $shortUrls->hasColumn(self::API_KEY_COLUMN));
$shortUrls->removeForeignKey('FK_' . self::API_KEY_COLUMN);
$shortUrls->dropColumn(self::API_KEY_COLUMN);
}
}

View File

@@ -7,7 +7,7 @@ namespace <namespace>;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
final class Version<version> extends AbstractMigration
final class <className> extends AbstractMigration
{
public function up(Schema $schema): void
{

14
docker-compose.ci.yml Normal file
View File

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

View File

@@ -3,7 +3,7 @@ version: '3'
services:
shlink_nginx:
container_name: shlink_nginx
image: nginx:1.15.9-alpine
image: nginx:1.17.10-alpine
ports:
- "8000:80"
volumes:
@@ -25,7 +25,23 @@ services:
- shlink_db
- shlink_db_postgres
- shlink_db_maria
- shlink_db_ms
- shlink_redis
- shlink_mercure
- shlink_mercure_proxy
environment:
LC_ALL: C
shlink_swoole_proxy:
container_name: shlink_swoole_proxy
image: nginx:1.17.10-alpine
ports:
- "8002:80"
volumes:
- ./:/home/shlink/www
- ./data/infra/swoole_proxy_vhost.conf:/etc/nginx/conf.d/default.conf
links:
- shlink_swoole
shlink_swoole:
container_name: shlink_swoole
@@ -37,11 +53,17 @@ services:
- "9001:9001"
volumes:
- ./:/home/shlink
- ./data/infra/php.ini:/usr/local/etc/php/php.ini
links:
- shlink_db
- shlink_db_postgres
- shlink_db_maria
- shlink_db_ms
- shlink_redis
- shlink_mercure
- shlink_mercure_proxy
environment:
LC_ALL: C
shlink_db:
container_name: shlink_db
@@ -57,7 +79,7 @@ services:
shlink_db_postgres:
container_name: shlink_db_postgres
image: postgres:10.7-alpine
image: postgres:12.2-alpine
ports:
- "5433:5432"
volumes:
@@ -70,7 +92,7 @@ services:
shlink_db_maria:
container_name: shlink_db_maria
image: mariadb:10.2
image: mariadb:10.5
ports:
- "3308:3306"
volumes:
@@ -81,8 +103,38 @@ services:
MYSQL_DATABASE: shlink
MYSQL_INITDB_SKIP_TZINFO: 1
shlink_db_ms:
container_name: shlink_db_ms
image: mcr.microsoft.com/mssql/server:2019-latest
ports:
- "1433:1433"
environment:
ACCEPT_EULA: Y
SA_PASSWORD: "Passw0rd!"
shlink_redis:
container_name: shlink_redis
image: redis:5.0-alpine
image: redis:6.0-alpine
ports:
- "6380:6379"
shlink_mercure_proxy:
container_name: shlink_mercure_proxy
image: nginx:1.17.10-alpine
ports:
- "8001:80"
volumes:
- ./:/home/shlink/www
- ./data/infra/mercure_proxy_vhost.conf:/etc/nginx/conf.d/default.conf
links:
- shlink_mercure
shlink_mercure:
container_name: shlink_mercure
image: dunglas/mercure:v0.9
ports:
- "3080:80"
environment:
CORS_ALLOWED_ORIGINS: "*"
JWT_KEY: "mercure_jwt_key"
USE_FORWARDED_HEADERS: "1"

View File

@@ -1,6 +1,5 @@
# Shlink Docker image
[![Docker build status](https://img.shields.io/docker/cloud/build/shlinkio/shlink.svg?style=flat-square)](https://hub.docker.com/r/shlinkio/shlink/)
[![Docker pulls](https://img.shields.io/docker/pulls/shlinkio/shlink.svg?style=flat-square)](https://hub.docker.com/r/shlinkio/shlink/)
This image provides an easy way to set up [shlink](https://shlink.io) on a container-based runtime.
@@ -19,7 +18,7 @@ It also expects these two env vars to be provided, in order to properly generate
So based on this, to run shlink on a local docker service, you should run a command like this:
```bash
docker run --name shlink -p 8080:8080 -e SHORT_DOMAIN_HOST=doma.in -e SHORT_DOMAIN_SCHEMA=https shlinkio/shlink
docker run --name shlink -p 8080:8080 -e SHORT_DOMAIN_HOST=doma.in -e SHORT_DOMAIN_SCHEMA=https -e GEOLITE_LICENSE_KEY=kjh23ljkbndskj345 shlinkio/shlink:stable
```
### Interact with shlink's CLI on a running container.
@@ -38,10 +37,10 @@ Or you can list all tags with:
docker exec -it shlink_container shlink tag:list
```
Or process remaining visits with:
Or locate remaining visits with:
```bash
docker exec -it shlink_container shlink visit:process
docker exec -it shlink_container shlink visit:locate
```
All shlink commands will work the same way.
@@ -56,9 +55,9 @@ docker exec -it shlink_container shlink
The image comes with a working sqlite database, but in production you will probably want to usa a distributed database.
It is possible to use a set of env vars to make this shlink instance interact with an external MySQL, MariaDB or PostgreSQL database.
It is possible to use a set of env vars to make this shlink instance interact with an external MySQL, MariaDB, PostgreSQL or Microsoft SQL Server database.
* `DB_DRIVER`: **[Mandatory]**. Use the value **mysql**, **maria** or **postgres** to prevent the sqlite database to be used.
* `DB_DRIVER`: **[Mandatory]**. Use the value **mysql**, **maria**, **postgres** or **mssql** to prevent the sqlite database to be used.
* `DB_NAME`: [Optional]. The database name to be used. Defaults to **shlink**.
* `DB_USER`: **[Mandatory]**. The username credential for the database server.
* `DB_PASSWORD`: **[Mandatory]**. The password credential for the database server.
@@ -67,62 +66,12 @@ It is possible to use a set of env vars to make this shlink instance interact wi
* Default value is based on the value provided for `DB_DRIVER`:
* **mysql** or **maria** -> `3306`
* **postgres** -> `5432`
* **mssql** -> `1433`
> PostgreSQL is supported since v1.16.1 of this image. Do not try to use it with previous versions.
> PostgreSQL is supported since v1.16.1 and Microsoft SQL server since v2.1.0. Do not try to use them with previous versions.
Taking this into account, you could run shlink on a local docker service like this:
```bash
docker run --name shlink -p 8080:8080 -e SHORT_DOMAIN_HOST=doma.in -e SHORT_DOMAIN_SCHEMA=https -e DB_DRIVER=mysql -e DB_USER=root -e DB_PASSWORD=123abc -e DB_HOST=something.rds.amazonaws.com shlinkio/shlink
```
You could even link to a local database running on a different container:
```bash
docker run --name shlink -p 8080:8080 [...] -e DB_HOST=some_mysql_container --link some_mysql_container shlinkio/shlink
```
> If you have considered using SQLite but sharing the database file with a volume, read [this issue](https://github.com/shlinkio/shlink-docker-image/issues/40) first.
## Supported env vars
A few env vars have been already used in previous examples, but this image supports others that can be used to customize its behavior.
This is the complete list of supported env vars:
* `SHORT_DOMAIN_HOST`: The custom short domain used for this shlink instance. For example **doma.in**.
* `SHORT_DOMAIN_SCHEMA`: Either **http** or **https**.
* `DB_DRIVER`: **sqlite** (which is the default value), **mysql**, **maria** or **postgres**.
* `DB_NAME`: The database name to be used when using an external database driver. Defaults to **shlink**.
* `DB_USER`: The username credential to be used when using an external database driver.
* `DB_PASSWORD`: The password credential to be used when using an external database driver.
* `DB_HOST`: The host name of the database server when using an external database driver.
* `DB_PORT`: The port in which the database service is running when using an external database driver.
* Default value is based on the value provided for `DB_DRIVER`:
* **mysql** or **maria** -> `3306`
* **postgres** -> `5432`
* `DISABLE_TRACK_PARAM`: The name of a query param that can be used to visit short URLs avoiding the visit to be tracked. This feature won't be available if not value is provided.
* `DELETE_SHORT_URL_THRESHOLD`: The amount of visits on short URLs which will not allow them to be deleted. Defaults to `15`.
* `VALIDATE_URLS`: Boolean which tells if shlink should validate a status 20x (after following redirects) is returned when trying to shorten a URL. Defaults to `true`.
* `INVALID_SHORT_URL_REDIRECT_TO`: If a URL is provided here, when a user tries to access an invalid short URL, he/she will be redirected to this value. If this env var is not provided, the user will see a generic `404 - not found` page.
* `REGULAR_404_REDIRECT_TO`: If a URL is provided here, when a user tries to access a URL not matching any one supported by the router, he/she will be redirected to this value. If this env var is not provided, the user will see a generic `404 - not found` page.
* `BASE_URL_REDIRECT_TO`: If a URL is provided here, when a user tries to access Shlink's base URL, he/she will be redirected to this value. If this env var is not provided, the user will see a generic `404 - not found` page.
* `BASE_PATH`: The base path from which you plan to serve shlink, in case you don't want to serve it from the root of the domain. Defaults to `''`.
* `WEB_WORKER_NUM`: The amount of concurrent http requests this shlink instance will be able to server. Defaults to 16.
* `TASK_WORKER_NUM`: The amount of concurrent background tasks this shlink instance will be able to execute. Defaults to 16.
* `REDIS_SERVERS`: A comma-separated list of redis servers where Shlink locks are stored (locks are used to prevent some operations to be run more than once in parallel).
This is important when running more than one Shlink instance ([Multi instance considerations](#multi-instance-considerations)). If not provided, Shlink stores locks on every instance separately.
If more than one server is provided, Shlink will expect them to be configured as a [redis cluster](https://redis.io/topics/cluster-tutorial).
In the future, these redis servers could be used for other caching operations performed by shlink.
* `NOT_FOUND_REDIRECT_TO`: **Deprecated since v1.20 in favor of `INVALID_SHORT_URL_REDIRECT_TO`** If a URL is provided here, when a user tries to access an invalid short URL, he/she will be redirected to this value. If this env var is not provided, the user will see a generic `404 - not found` page.
* `SHORTCODE_CHARS`: **Ignored when using Shlink 1.20 or newer**. A charset to use when building short codes. Only needed when using more than one shlink instance ([Multi instance considerations](#multi-instance-considerations)).
An example using all env vars could look like this:
```bash
docker run \
--name shlink \
@@ -130,6 +79,115 @@ docker run \
-e SHORT_DOMAIN_HOST=doma.in \
-e SHORT_DOMAIN_SCHEMA=https \
-e DB_DRIVER=mysql \
-e DB_USER=root \
-e DB_PASSWORD=123abc \
-e DB_HOST=something.rds.amazonaws.com \
shlinkio/shlink:stable
```
You could even link to a local database running on a different container:
```bash
docker run \
--name shlink \
-p 8080:8080 \
[...] \
-e DB_HOST=some_mysql_container \
--link some_mysql_container \
shlinkio/shlink:stable
```
> If you have considered using SQLite but sharing the database file with a volume, read [this issue](https://github.com/shlinkio/shlink-docker-image/issues/40) first.
## Other integrations
### Use an external redis server
If you plan to run more than one Shlink instance, there are some resources that should be shared ([Multi instance considerations](#multi-instance-considerations)).
One of those resources are the locks Shlink generates to prevent some operations to be run more than once in parallel (in the future, these redis servers could be used for other caching operations).
In order to share those locks, you should use an external redis server (or a cluster of redis servers), by providing the `REDIS_SERVERS` env var.
It can be either one server name or a comma-separated list of servers.
> If more than one redis server is provided, Shlink will expect them to be configured as a [redis cluster](https://redis.io/topics/cluster-tutorial).
### Integrate with a mercure hub server
One way to get real time updates when certain events happen in Shlink is by integrating it with a [mercure hub](https://mercure.rocks/) server.
If you do that, Shlink will publish updates and other clients can subscribe to those.
There are three env vars you need to provide if you want to enable this:
* `MERCURE_PUBLIC_HUB_URL`: **[Mandatory]**. The public URL of a mercure hub server to which Shlink will sent updates. This URL will also be served to consumers that want to subscribe to those updates.
* `MERCURE_INTERNAL_HUB_URL`: **[Optional]**. An internal URL for a mercure hub. Will be used only when publishing updates to mercure, and does not need to be public. If this is not provided, the `MERCURE_PUBLIC_HUB_URL` one will be used to publish updates.
* `MERCURE_JWT_SECRET`: **[Mandatory]**. The secret key that was provided to the mercure hub server, in order to be able to generate valid JWTs for publishing/subscribing to that server.
So in order to run shlink with mercure integration, you would do it like this:
```bash
docker run \
--name shlink \
-p 8080:8080 \
-e SHORT_DOMAIN_HOST=doma.in \
-e SHORT_DOMAIN_SCHEMA=https \
-e "MERCURE_PUBLIC_HUB_URL=https://example.com"
-e "MERCURE_INTERNAL_HUB_URL=http://my-mercure-hub.prod.svc.cluster.local"
-e MERCURE_JWT_SECRET=super_secret_key
shlinkio/shlink:stable
```
## All supported env vars
A few env vars have been already used in previous examples, but this image supports others that can be used to customize its behavior.
This is the complete list of supported env vars:
* `SHORT_DOMAIN_HOST`: The custom short domain used for this shlink instance. For example **doma.in**.
* `SHORT_DOMAIN_SCHEMA`: Either **http** or **https**.
* `DB_DRIVER`: **sqlite** (which is the default value), **mysql**, **maria**, **postgres** or **mssql**.
* `DB_NAME`: The database name to be used when using an external database driver. Defaults to **shlink**.
* `DB_USER`: The username credential to be used when using an external database driver.
* `DB_PASSWORD`: The password credential to be used when using an external database driver.
* `DB_HOST`: The host name of the database server when using an external database driver.
* `DB_PORT`: The port in which the database service is running when using an external database driver.
* Default value is based on the value provided for `DB_DRIVER`:
* **mysql** or **maria** -> `3306`
* **postgres** -> `5432`
* **mssql** -> `1433`
* `DISABLE_TRACK_PARAM`: The name of a query param that can be used to visit short URLs avoiding the visit to be tracked. This feature won't be available if not value is provided.
* `DELETE_SHORT_URL_THRESHOLD`: The amount of visits on short URLs which will not allow them to be deleted. Defaults to `15`.
* `VALIDATE_URLS`: Boolean which tells if shlink should validate a status 20x is returned (after following redirects) when trying to shorten a URL. Defaults to `false`.
* `INVALID_SHORT_URL_REDIRECT_TO`: If a URL is provided here, when a user tries to access an invalid short URL, he/she will be redirected to this value. If this env var is not provided, the user will see a generic `404 - not found` page.
* `REGULAR_404_REDIRECT_TO`: If a URL is provided here, when a user tries to access a URL not matching any one supported by the router, he/she will be redirected to this value. If this env var is not provided, the user will see a generic `404 - not found` page.
* `BASE_URL_REDIRECT_TO`: If a URL is provided here, when a user tries to access Shlink's base URL, he/she will be redirected to this value. If this env var is not provided, the user will see a generic `404 - not found` page.
* `BASE_PATH`: The base path from which you plan to serve shlink, in case you don't want to serve it from the root of the domain. Defaults to `''`.
* `WEB_WORKER_NUM`: The amount of concurrent http requests this shlink instance will be able to server. Defaults to 16.
* `TASK_WORKER_NUM`: The amount of concurrent background tasks this shlink instance will be able to execute. Defaults to 16.
* `VISITS_WEBHOOKS`: A comma-separated list of URLs that will receive a `POST` request when a short URL receives a visit.
* `DEFAULT_SHORT_CODES_LENGTH`: The length you want generated short codes to have. It defaults to 5 and has to be at least 4, so any value smaller than that will fall back to 4.
* `GEOLITE_LICENSE_KEY`: The license key used to download new GeoLite2 database files. This is not mandatory, as a default license key is provided, but it is **strongly recommended** that you provide your own. Go to [https://shlink.io/documentation/geolite-license-key](https://shlink.io/documentation/geolite-license-key) to know how to generate it.
* `REDIS_SERVERS`: A comma-separated list of redis servers where Shlink locks are stored (locks are used to prevent some operations to be run more than once in parallel).
* `MERCURE_PUBLIC_HUB_URL`: The public URL of a mercure hub server to which Shlink will sent updates. This URL will also be served to consumers that want to subscribe to those updates.
* `MERCURE_INTERNAL_HUB_URL`: An internal URL for a mercure hub. Will be used only when publishing updates to mercure, and does not need to be public. If this is not provided but `MERCURE_PUBLIC_HUB_URL` was, the former one will be used to publish updates.
* `MERCURE_JWT_SECRET`: The secret key that was provided to the mercure hub server, in order to be able to generate valid JWTs for publishing/subscribing to that server.
* `ANONYMIZE_REMOTE_ADDR`: Tells if IP addresses from visitors should be obfuscated before storing them in the database. Default value is `true`. **Careful!** Setting this to `false` will make your Shlink instance no longer be in compliance with the GDPR and other similar data protection regulations.
* `REDIRECT_STATUS_CODE`: Either **301** or **302**. Used to determine if redirects from short to long URLs should be done with a 301 or 302 status. Defaults to 302.
* `REDIRECT_CACHE_LIFETIME`: Allows to set the amount of seconds that redirects should be cached when redirect status is 301. Default values is 30.
* `PORT`: Can be used to set the port in which shlink listens. Defaults to 8080 (Some cloud providers, like Google cloud or Heroku, expect to be able to customize exposed port by providing this env var).
An example using all env vars could look like this:
```bash
docker run \
--name shlink \
-p 8080:8888 \
-e SHORT_DOMAIN_HOST=doma.in \
-e SHORT_DOMAIN_SCHEMA=https \
-e PORT=8888 \
-e DB_DRIVER=mysql \
-e DB_NAME=shlink \
-e DB_USER=root \
-e DB_PASSWORD=123abc \
@@ -137,7 +195,7 @@ docker run \
-e DB_PORT=3306 \
-e DISABLE_TRACK_PARAM="no-track" \
-e DELETE_SHORT_URL_THRESHOLD=30 \
-e VALIDATE_URLS=false \
-e VALIDATE_URLS=true \
-e "INVALID_SHORT_URL_REDIRECT_TO=https://my-landing-page.com" \
-e "REGULAR_404_REDIRECT_TO=https://my-landing-page.com" \
-e "BASE_URL_REDIRECT_TO=https://my-landing-page.com" \
@@ -145,7 +203,16 @@ docker run \
-e "BASE_PATH=/my-campaign" \
-e WEB_WORKER_NUM=64 \
-e TASK_WORKER_NUM=32 \
shlinkio/shlink
-e "VISITS_WEBHOOKS=http://my-api.com/api/v2.3/notify,https://third-party.io/foo" \
-e DEFAULT_SHORT_CODES_LENGTH=6 \
-e GEOLITE_LICENSE_KEY=kjh23ljkbndskj345 \
-e "MERCURE_PUBLIC_HUB_URL=https://example.com" \
-e "MERCURE_INTERNAL_HUB_URL=http://my-mercure-hub.prod.svc.cluster.local" \
-e MERCURE_JWT_SECRET=super_secret_key \
-e ANONYMIZE_REMOTE_ADDR=false \
-e REDIRECT_STATUS_CODE=301 \
-e REDIRECT_CACHE_LIFETIME=90 \
shlinkio/shlink:stable
```
## Provide config via volumes
@@ -162,17 +229,22 @@ The whole configuration should have this format, but it can be split into multip
"delete_short_url_threshold": 30,
"short_domain_schema": "https",
"short_domain_host": "doma.in",
"validate_url": false,
"validate_url": true,
"invalid_short_url_redirect_to": "https://my-landing-page.com",
"regular_404_redirect_to": "https://my-landing-page.com",
"base_url_redirect_to": "https://my-landing-page.com",
"base_path": "/my-campaign",
"web_worker_num": 64,
"task_worker_num": 32,
"default_short_codes_length": 6,
"redis_servers": [
"tcp://172.20.0.1:6379",
"tcp://172.20.0.2:6379"
],
"visits_webhooks": [
"http://my-api.com/api/v2.3/notify",
"https://third-party.io/foo"
],
"db_config": {
"driver": "pdo_mysql",
"dbname": "shlink",
@@ -181,21 +253,32 @@ The whole configuration should have this format, but it can be split into multip
"host": "something.rds.amazonaws.com",
"port": "3306"
},
"not_found_redirect_to": "https://my-landing-page.com"
"geolite_license_key": "kjh23ljkbndskj345",
"mercure_public_hub_url": "https://example.com",
"mercure_internal_hub_url": "http://my-mercure-hub.prod.svc.cluster.local",
"mercure_jwt_secret": "super_secret_key",
"anonymize_remote_addr": false,
"redirect_status_code": 301,
"redirect_cache_lifetime": 90,
"port": 8888
}
```
> This is internally parsed to how shlink expects the config. If you are using a version previous to 1.17.0, this parser is not present and you need to provide a config structure like the one [documented previously](https://github.com/shlinkio/shlink-docker-image/tree/v1.16.3#provide-config-via-volumes).
> The `not_found_redirect_to` option has been deprecated in v1.20. Use `invalid_short_url_redirect_to` instead (however, it will still work for backwards compatibility).
Once created just run shlink with the volume:
```bash
docker run --name shlink -p 8080:8080 -v ${PWD}/my/config/dir:/etc/shlink/config/params shlinkio/shlink
docker run --name shlink -p 8080:8080 -v ${PWD}/my/config/dir:/etc/shlink/config/params shlinkio/shlink:stable
```
## Multi instance considerations
## Multi-architecture
Starting on v2.3.0, Shlink's docker image is built for multiple architectures.
The only limitation is that images for architectures other than `amd64` will not have support for Microsoft SQL databases, since there are no official binaries.
## Multi-instance considerations
These are some considerations to take into account when running multiple instances of shlink.
@@ -205,20 +288,12 @@ These are some considerations to take into account when running multiple instanc
You can (and should) make the locks to be shared by all Shlink instances by using a redis server/cluster. Just define the `REDIS_SERVERS` env var with the list of servers.
* **Ignore this if using Shlink 1.20 or newer**. The first time shlink is run, it generates a charset used to generate short codes, which is a shuffled base62 charset.
If you are using several shlink instances, you will probably want all of them to use the same charset.
You can get a shuffled base62 charset by going to [https://shlink.io/short-code-chars](https://shlink.io/short-code-chars), and then you just need to pass it to all shlink instances using the `SHORTCODE_CHARS` env var.
If you don't do this, each shlink instance will use a different charset. However this shouldn't be a problem in practice, since the chances to get a collision will be very low.
## Versions
Versioning on this docker image works as follows:
* `X.X.X`: when providing a specific version number, the image version will match the shlink version it contains. For example, installing `shlinkio/shlink:1.15.0`, you will get an image containing shlink v1.15.0.
* `stable`: always holds the latest stable tag. For example, if latest shlink version is 1.20.0, installing `shlinkio/shlink:stable`, you will get an image containing shlink v1.20.0
* `latest`: always holds the latest contents in master, and it's considered unstable and not suitable for production.
* `stable`: always holds the latest stable tag. For example, if latest shlink version is 2.0.0, installing `shlinkio/shlink:stable`, you will get an image containing shlink v2.0.0
* `latest`: always holds the latest contents, and it's considered unstable and not suitable for production.
> **Important**: The docker image was introduced with shlink v1.15.0, so there are no official images previous to that versions.

25
docker/build Executable file
View File

@@ -0,0 +1,25 @@
#!/bin/bash
set -ex
PLATFORMS="linux/arm/v7,linux/arm64/v8,linux/amd64"
DOCKER_IMAGE="shlinkio/shlink"
# If ref is not develop, then this is a tag. Build that docker tag and also "stable"
if [[ "$GITHUB_REF" != *"develop"* ]]; then
VERSION=${GITHUB_REF#refs/tags/v}
TAGS="-t ${DOCKER_IMAGE}:${VERSION}"
# Push stable tag only if this is not an alpha or beta tag
[[ $GITHUB_REF != *"alpha"* && $GITHUB_REF != *"beta"* ]] && TAGS="${TAGS} -t ${DOCKER_IMAGE}:stable"
docker buildx build --push \
--build-arg SHLINK_VERSION=${VERSION} \
--platform ${PLATFORMS} \
${TAGS} .
# If build branch is develop, build latest
elif [[ "$GITHUB_REF" == *"develop"* ]]; then
docker buildx build --push \
--platform ${PLATFORMS} \
-t ${DOCKER_IMAGE}:latest .
fi

3
docker/config/php.ini Normal file
View File

@@ -0,0 +1,3 @@
log_errors_max_len=0
zend.assertions=1
assert.exception=1

View File

@@ -8,64 +8,29 @@ use Monolog\Handler\StreamHandler;
use Monolog\Logger;
use function explode;
use function file_exists;
use function file_get_contents;
use function file_put_contents;
use function Functional\contains;
use function implode;
use function Shlinkio\Shlink\Common\env;
use function sprintf;
use function str_shuffle;
use function substr;
use function sys_get_temp_dir;
use const Shlinkio\Shlink\Core\DEFAULT_DELETE_SHORT_URL_THRESHOLD;
use const Shlinkio\Shlink\Core\DEFAULT_REDIRECT_CACHE_LIFETIME;
use const Shlinkio\Shlink\Core\DEFAULT_REDIRECT_STATUS_CODE;
use const Shlinkio\Shlink\Core\DEFAULT_SHORT_CODES_LENGTH;
use const Shlinkio\Shlink\Core\MIN_SHORT_CODES_LENGTH;
$helper = new class {
private const BASE62 = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
private const DB_DRIVERS_MAP = [
'mysql' => 'pdo_mysql',
'maria' => 'pdo_mysql',
'postgres' => 'pdo_pgsql',
'mssql' => 'pdo_sqlsrv',
];
private const DB_PORTS_MAP = [
'mysql' => '3306',
'maria' => '3306',
'postgres' => '5432',
'mssql' => '1433',
];
/** @var string */
private $secretKey;
public function __construct()
{
[, $this->secretKey] = $this->initShlinkSecretKey();
}
private function initShlinkSecretKey(): array
{
$keysFile = sprintf('%s/shlink.keys', sys_get_temp_dir());
if (file_exists($keysFile)) {
return explode(',', file_get_contents($keysFile));
}
$keys = [
'', // This was the SHORTCODE_CHARS. Kept as empty string for BC
env('SECRET_KEY', $this->generateSecretKey()), // Deprecated
];
file_put_contents($keysFile, implode(',', $keys));
return $keys;
}
private function generateSecretKey(): string
{
return substr(str_shuffle(self::BASE62), 0, 32);
}
public function getSecretKey(): string
{
return $this->secretKey;
}
public function getDbConfig(): array
{
$driver = env('DB_DRIVER');
@@ -79,6 +44,8 @@ $helper = new class {
$driverOptions = ! contains(['maria', 'mysql'], $driver) ? [] : [
// 1002 -> PDO::MYSQL_ATTR_INIT_COMMAND
1002 => 'SET NAMES utf8',
// 1000 -> PDO::MYSQL_ATTR_USE_BUFFERED_QUERY
1000 => true,
];
return [
'driver' => self::DB_DRIVERS_MAP[$driver],
@@ -94,11 +61,40 @@ $helper = new class {
public function getNotFoundRedirectsConfig(): array
{
return [
'invalid_short_url' => env('INVALID_SHORT_URL_REDIRECT_TO', env('NOT_FOUND_REDIRECT_TO')),
'invalid_short_url' => env('INVALID_SHORT_URL_REDIRECT_TO'),
'regular_404' => env('REGULAR_404_REDIRECT_TO'),
'base_url' => env('BASE_URL_REDIRECT_TO'),
];
}
public function getVisitsWebhooks(): array
{
$webhooks = env('VISITS_WEBHOOKS');
return $webhooks === null ? [] : explode(',', $webhooks);
}
public function getRedisConfig(): ?array
{
$redisServers = env('REDIS_SERVERS');
return $redisServers === null ? null : ['servers' => $redisServers];
}
public function getDefaultShortCodesLength(): int
{
$value = (int) env('DEFAULT_SHORT_CODES_LENGTH', DEFAULT_SHORT_CODES_LENGTH);
return $value < MIN_SHORT_CODES_LENGTH ? MIN_SHORT_CODES_LENGTH : $value;
}
public function getMercureConfig(): array
{
$publicUrl = env('MERCURE_PUBLIC_HUB_URL');
return [
'public_hub_url' => $publicUrl,
'internal_hub_url' => env('MERCURE_INTERNAL_HUB_URL', $publicUrl),
'jwt_secret' => env('MERCURE_JWT_SECRET'),
];
}
};
return [
@@ -106,13 +102,12 @@ return [
'config_cache_enabled' => false,
'app_options' => [
'secret_key' => $helper->getSecretKey(),
'disable_track_param' => env('DISABLE_TRACK_PARAM'),
],
'delete_short_urls' => [
'check_visits_threshold' => true,
'visits_threshold' => (int) env('DELETE_SHORT_URL_THRESHOLD', 15),
'visits_threshold' => (int) env('DELETE_SHORT_URL_THRESHOLD', DEFAULT_DELETE_SHORT_URL_THRESHOLD),
],
'entity_manager' => [
@@ -124,27 +119,26 @@ return [
'schema' => env('SHORT_DOMAIN_SCHEMA', 'http'),
'hostname' => env('SHORT_DOMAIN_HOST', ''),
],
'validate_url' => (bool) env('VALIDATE_URLS', true),
'validate_url' => (bool) env('VALIDATE_URLS', false),
'anonymize_remote_addr' => (bool) env('ANONYMIZE_REMOTE_ADDR', true),
'visits_webhooks' => $helper->getVisitsWebhooks(),
'default_short_codes_length' => $helper->getDefaultShortCodesLength(),
'redirect_status_code' => (int) env('REDIRECT_STATUS_CODE', DEFAULT_REDIRECT_STATUS_CODE),
'redirect_cache_lifetime' => (int) env('REDIRECT_CACHE_LIFETIME', DEFAULT_REDIRECT_CACHE_LIFETIME),
],
'not_found_redirects' => $helper->getNotFoundRedirectsConfig(),
'logger' => [
'handlers' => [
'shlink_rotating_handler' => [
'level' => Logger::EMERGENCY, // This basically disables regular file logs
],
'shlink_stdout_handler' => [
'class' => StreamHandler::class,
'level' => Logger::INFO,
'stream' => 'php://stdout',
'formatter' => 'dashed',
],
],
'loggers' => [
'Shlink' => [
'handlers' => ['shlink_stdout_handler'],
'Shlink' => [
'handlers' => [
'shlink_handler' => [
'name' => StreamHandler::class,
'params' => [
'level' => Logger::INFO,
'stream' => 'php://stdout',
],
],
],
],
],
@@ -155,16 +149,17 @@ return [
],
],
'redis' => [
'servers' => env('REDIS_SERVERS'),
'cache' => [
'redis' => $helper->getRedisConfig(),
],
'router' => [
'base_path' => env('BASE_PATH', ''),
],
'zend-expressive-swoole' => [
'mezzio-swoole' => [
'swoole-http-server' => [
'port' => (int) env('PORT', 8080),
'options' => [
'worker_num' => (int) env('WEB_WORKER_NUM', 16),
'task_worker_num' => (int) env('TASK_WORKER_NUM', 16),
@@ -172,4 +167,10 @@ return [
],
],
'geolite2' => [
'license_key' => env('GEOLITE_LICENSE_KEY', 'G4Lm0C60yJsnkdPi'),
],
'mercure' => $helper->getMercureConfig(),
];

View File

@@ -12,6 +12,9 @@ php bin/cli db:migrate -n -q
echo "Generating proxies..."
php vendor/doctrine/orm/bin/doctrine.php orm:generate-proxies -n -q
echo "Clearing entities cache..."
php vendor/doctrine/orm/bin/doctrine.php orm:clear-cache:metadata -n -q
# When restarting the container, swoole might think it is already in execution
# This forces the app to be started every second until the exit code is 0
until php vendor/zendframework/zend-expressive-swoole/bin/zend-expressive-swoole start; do sleep 1 ; done
until php vendor/mezzio/mezzio-swoole/bin/mezzio-swoole start; do sleep 1 ; done

View File

@@ -0,0 +1,210 @@
{
"asyncapi": "2.0.0",
"info": {
"title": "Shlink",
"version": "2.0.0",
"description": "Shlink, the self-hosted URL shortener",
"license": {
"name": "MIT",
"url": "https://github.com/shlinkio/shlink/blob/develop/LICENSE"
}
},
"defaultContentType": "application/json",
"channels": {
"http://shlink.io/new-visit": {
"subscribe": {
"summary": "Receive information about any new visit occurring on any short URL.",
"operationId": "newVisit",
"message": {
"payload": {
"type": "object",
"additionalProperties": false,
"properties": {
"shortUrl": {
"$ref": "#/components/schemas/ShortUrl"
},
"visit": {
"$ref": "#/components/schemas/Visit"
}
}
}
}
}
},
"http://shlink.io/new-visit/{shortCode}": {
"parameters": {
"shortCode": {
"description": "The short code of the short URL",
"schema": {
"type": "string"
}
}
},
"subscribe": {
"summary": "Receive information about any new visit occurring on a specific short URL.",
"operationId": "newShortUrlVisit",
"message": {
"payload": {
"type": "object",
"additionalProperties": false,
"properties": {
"shortUrl": {
"$ref": "#/components/schemas/ShortUrl"
},
"visit": {
"$ref": "#/components/schemas/Visit"
}
}
}
}
}
}
},
"components": {
"schemas": {
"ShortUrl": {
"type": "object",
"properties": {
"shortCode": {
"type": "string",
"description": "The short code for this short URL."
},
"shortUrl": {
"type": "string",
"description": "The short URL."
},
"longUrl": {
"type": "string",
"description": "The original long URL."
},
"dateCreated": {
"type": "string",
"format": "date-time",
"description": "The date in which the short URL was created in ISO format."
},
"visitsCount": {
"type": "integer",
"description": "The number of visits that this short URL has recieved."
},
"tags": {
"type": "array",
"items": {
"type": "string"
},
"description": "A list of tags applied to this short URL"
},
"meta": {
"$ref": "#/components/schemas/ShortUrlMeta"
},
"domain": {
"type": "string",
"description": "The domain in which the short URL was created. Null if it belongs to default domain."
}
},
"example": {
"shortCode": "12C18",
"shortUrl": "https://doma.in/12C18",
"longUrl": "https://store.steampowered.com",
"dateCreated": "2016-08-21T20:34:16+02:00",
"visitsCount": 328,
"tags": [
"games",
"tech"
],
"meta": {
"validSince": "2017-01-21T00:00:00+02:00",
"validUntil": null,
"maxVisits": 100
},
"domain": "example.com"
}
},
"ShortUrlMeta": {
"type": "object",
"required": [
"validSince",
"validUntil",
"maxVisits"
],
"properties": {
"validSince": {
"description": "The date (in ISO-8601 format) from which this short code will be valid",
"type": "string",
"nullable": true
},
"validUntil": {
"description": "The date (in ISO-8601 format) until which this short code will be valid",
"type": "string",
"nullable": true
},
"maxVisits": {
"description": "The maximum number of allowed visits for this short code",
"type": "number",
"nullable": true
}
}
},
"Visit": {
"type": "object",
"properties": {
"referer": {
"type": "string",
"description": "The origin from which the visit was performed"
},
"date": {
"type": "string",
"format": "date-time",
"description": "The date in which the visit was performed"
},
"userAgent": {
"type": "string",
"description": "The user agent from which the visit was performed"
},
"visitLocation": {
"$ref": "#/components/schemas/VisitLocation"
}
},
"example": {
"referer": "https://t.co",
"date": "2015-08-20T05:05:03+04:00",
"userAgent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.103 Safari/537.36",
"visitLocation": {
"cityName": "Cupertino",
"countryCode": "US",
"countryName": "United States",
"latitude": 37.3042,
"longitude": -122.0946,
"regionName": "California",
"timezone": "America/Los_Angeles"
}
}
},
"VisitLocation": {
"type": "object",
"properties": {
"cityName": {
"type": "string"
},
"countryCode": {
"type": "string"
},
"countryName": {
"type": "string"
},
"latitude": {
"type": "number"
},
"longitude": {
"type": "number"
},
"regionName": {
"type": "string"
},
"timezone": {
"type": "string"
}
}
}
}
}
}

View File

@@ -1,13 +1,22 @@
{
"type": "object",
"required": ["type", "title", "detail", "status"],
"properties": {
"code": {
"type": {
"type": "string",
"description": "A machine unique code"
},
"message": {
"title": {
"type": "string",
"description": "A human-friendly error message"
"description": "A unique title"
},
"detail": {
"type": "string",
"description": "A human-friendly error description"
},
"status": {
"type": "number",
"description": "HTTP response status code"
}
}
}

View File

@@ -0,0 +1,18 @@
{
"type": "object",
"required": ["mercureHubUrl", "jwt", "jwtExpiration"],
"properties": {
"mercureHubUrl": {
"type": "string",
"description": "The public URL of the mercure hub that can be used to get real-time updates published by Shlink"
},
"jwt": {
"type": "string",
"description": "A JWT with subscribe permissions which is valid with the mercure hub"
},
"jwtExpiration": {
"type": "string",
"description": "The date (in ISO-8601 format) in which the JWT will expire"
}
}
}

View File

@@ -32,10 +32,9 @@
"meta": {
"$ref": "./ShortUrlMeta.json"
},
"originalUrl": {
"deprecated": true,
"domain": {
"type": "string",
"description": "The original long URL. [DEPRECATED. Use longUrl instead]"
"description": "The domain in which the short URL was created. Null if it belongs to default domain."
}
}
}

View File

@@ -0,0 +1,17 @@
{
"type": "object",
"properties": {
"tag": {
"type": "string",
"description": "The unique tag name"
},
"shortUrlsCount": {
"type": "number",
"description": "The amount of short URLs using this tag"
},
"userAgent": {
"type": "number",
"description": "The combined amount of visits received by short URLs with this tag"
}
}
}

View File

@@ -16,11 +16,6 @@
},
"visitLocation": {
"$ref": "./VisitLocation.json"
},
"remoteAddr": {
"type": "string",
"description": "This value is deprecated and will always be null",
"deprecated": true
}
}
}

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