mirror of
https://github.com/shlinkio/shlink.git
synced 2026-02-28 12:13:13 +08:00
Compare commits
334 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3266a0f85c | ||
|
|
4629f1b03f | ||
|
|
fbd0c6cbea | ||
|
|
8260051c30 | ||
|
|
c061c9c3ff | ||
|
|
8961191b2e | ||
|
|
fc0d99be41 | ||
|
|
6834e72c4a | ||
|
|
efe655f880 | ||
|
|
3d5ddce621 | ||
|
|
a3de3e15cb | ||
|
|
619999d4f8 | ||
|
|
7acf27dd38 | ||
|
|
ba517eeeb5 | ||
|
|
fdd3e24967 | ||
|
|
a570ce202a | ||
|
|
0a220bbc7a | ||
|
|
e0e511f56d | ||
|
|
d375dece0e | ||
|
|
f801f265ed | ||
|
|
1b4fc89b07 | ||
|
|
3ac2b77bf0 | ||
|
|
b2ca4ad66b | ||
|
|
25a7c7bc7f | ||
|
|
6b009a4de4 | ||
|
|
0b80a86e88 | ||
|
|
b03f24d59a | ||
|
|
78ea13d366 | ||
|
|
8c2bdfba1c | ||
|
|
3289968a93 | ||
|
|
73ae754aa7 | ||
|
|
20a6e7e210 | ||
|
|
4cf433a994 | ||
|
|
e36c4d397c | ||
|
|
26037327f9 | ||
|
|
da6aa1d697 | ||
|
|
dada6aa3d1 | ||
|
|
fa5ebb1677 | ||
|
|
f071df325d | ||
|
|
3c042c4011 | ||
|
|
7e8109caa3 | ||
|
|
d3add6d8e4 | ||
|
|
1b089749c0 | ||
|
|
791d6b7e57 | ||
|
|
233bb603cf | ||
|
|
db8a816524 | ||
|
|
eff50ca202 | ||
|
|
ceabb5ab2c | ||
|
|
122c2fd5e6 | ||
|
|
cd27a72982 | ||
|
|
19b0f0d7dc | ||
|
|
6ce2049935 | ||
|
|
53b937be63 | ||
|
|
71c8f99dab | ||
|
|
9eb3fca726 | ||
|
|
019bd4dec8 | ||
|
|
be1ce06c00 | ||
|
|
074bfe3db2 | ||
|
|
34e72b42dc | ||
|
|
97d24d76d8 | ||
|
|
4d1af867a4 | ||
|
|
fc6b4c12b2 | ||
|
|
405c6de591 | ||
|
|
47bfa5fcc0 | ||
|
|
67d91d5fc5 | ||
|
|
f832c56adb | ||
|
|
1aa9ae680e | ||
|
|
c4b30db82d | ||
|
|
abd9f3c6be | ||
|
|
3de3594282 | ||
|
|
ed5816d464 | ||
|
|
3d43bdbb49 | ||
|
|
1ab492ce5b | ||
|
|
de30c6ad79 | ||
|
|
f5a48ff98d | ||
|
|
8493ee5b83 | ||
|
|
52a6d55e5d | ||
|
|
7142295aa5 | ||
|
|
8b65be26a6 | ||
|
|
60f5deb494 | ||
|
|
0fc09e6dd3 | ||
|
|
0c4ccf4e3e | ||
|
|
a0e79bf446 | ||
|
|
aa356ad7c7 | ||
|
|
9e0e384d46 | ||
|
|
a20b99e643 | ||
|
|
fe4237b2b1 | ||
|
|
4146835f6f | ||
|
|
5201ea4516 | ||
|
|
fba7b36245 | ||
|
|
353ac0fc0c | ||
|
|
00002b1e24 | ||
|
|
aa32830671 | ||
|
|
12b8100d89 | ||
|
|
72e56d271d | ||
|
|
2b69f5eff4 | ||
|
|
f224bb98c4 | ||
|
|
ec17eb3fbc | ||
|
|
358b600713 | ||
|
|
aacb5c39ba | ||
|
|
9ae8804095 | ||
|
|
2d51bd895d | ||
|
|
18f656fed2 | ||
|
|
eea76999b2 | ||
|
|
bd495adf22 | ||
|
|
e8c7bee924 | ||
|
|
24b06c24dc | ||
|
|
6fdd764a35 | ||
|
|
2400d1f265 | ||
|
|
cdef430b0b | ||
|
|
6074e4ae2c | ||
|
|
6ada704bc3 | ||
|
|
e8f7daac6f | ||
|
|
404455928e | ||
|
|
bca3e62ced | ||
|
|
e79391907a | ||
|
|
54a23cc7fa | ||
|
|
e8ebe77923 | ||
|
|
68e0aa1ea9 | ||
|
|
ceaf64c9b3 | ||
|
|
e015b1bec5 | ||
|
|
ca9726c997 | ||
|
|
85c79abd30 | ||
|
|
54c1c7ad84 | ||
|
|
af15e31b42 | ||
|
|
99b4f9f4dd | ||
|
|
9a0e5ea626 | ||
|
|
984205e02c | ||
|
|
e11bf6ac67 | ||
|
|
e029d91544 | ||
|
|
011856cbfa | ||
|
|
9ce8164013 | ||
|
|
dca6b7bbf5 | ||
|
|
2511ec3395 | ||
|
|
9f6ffc7186 | ||
|
|
622f0217fa | ||
|
|
09eba49bab | ||
|
|
c20c3801a8 | ||
|
|
f8208b7288 | ||
|
|
3db8a65ddb | ||
|
|
0495b6f298 | ||
|
|
52c55f385d | ||
|
|
fe28d6fba0 | ||
|
|
0294e49d4a | ||
|
|
cbaf51d3ef | ||
|
|
efb604a381 | ||
|
|
da87f05126 | ||
|
|
21534b78cb | ||
|
|
ab65593f7d | ||
|
|
3a82691503 | ||
|
|
430e2ff0b5 | ||
|
|
7d572e7988 | ||
|
|
1449e24b66 | ||
|
|
6a671760da | ||
|
|
613bdd82b0 | ||
|
|
01bae358f9 | ||
|
|
3a8e560dc5 | ||
|
|
a0c538d9ee | ||
|
|
07c30f86e9 | ||
|
|
c22e38f9a0 | ||
|
|
7502e8a1e4 | ||
|
|
5a25211371 | ||
|
|
6983f9b2bf | ||
|
|
5affe64b61 | ||
|
|
c52f3c396b | ||
|
|
e1ebbaa52f | ||
|
|
7abe6af5ec | ||
|
|
c98ea6055b | ||
|
|
3e3d255edf | ||
|
|
816d4851e7 | ||
|
|
79af315b9f | ||
|
|
4110c702c0 | ||
|
|
57eb29c3c8 | ||
|
|
5267c4eee6 | ||
|
|
1453ebe8ca | ||
|
|
3b5cea5768 | ||
|
|
a89f67348d | ||
|
|
af1ae0399c | ||
|
|
5bf84144e7 | ||
|
|
d9adff5749 | ||
|
|
a1cd8baf3e | ||
|
|
87cadce0ac | ||
|
|
f22f50afa2 | ||
|
|
d0fa6f7e03 | ||
|
|
d29c58dce5 | ||
|
|
9ea8f3b590 | ||
|
|
ffffc68144 | ||
|
|
086de9f2a0 | ||
|
|
1b731aa4a3 | ||
|
|
12913f6b90 | ||
|
|
1d4186392c | ||
|
|
48d3ab0cb4 | ||
|
|
a9d04729eb | ||
|
|
7adf2292bd | ||
|
|
c8f55f9c05 | ||
|
|
93de62f81d | ||
|
|
9766231d41 | ||
|
|
9df80e5bec | ||
|
|
81b00e4302 | ||
|
|
25ac7c31c4 | ||
|
|
11c6c9a2b8 | ||
|
|
066268765a | ||
|
|
8d965655a8 | ||
|
|
3ace4952e6 | ||
|
|
299f9f3a10 | ||
|
|
0e6790cdab | ||
|
|
1f90af3aec | ||
|
|
cdb18a5baf | ||
|
|
8adb6596fb | ||
|
|
dd6bcd68cc | ||
|
|
1c9ce0ede0 | ||
|
|
6b409b06cc | ||
|
|
361e864415 | ||
|
|
d5606114cd | ||
|
|
afca66d655 | ||
|
|
33a6c9fda7 | ||
|
|
a198484ab6 | ||
|
|
dd5bce9694 | ||
|
|
bef17ff76d | ||
|
|
7202605fc8 | ||
|
|
747dac531d | ||
|
|
07d24f70e1 | ||
|
|
d0546a2ea2 | ||
|
|
9e9621e7b2 | ||
|
|
d39f3b4265 | ||
|
|
223339cd61 | ||
|
|
dc430bae10 | ||
|
|
661b07e12f | ||
|
|
0727c7bdfb | ||
|
|
b4c52116b4 | ||
|
|
89dc6108b7 | ||
|
|
492eba3a8b | ||
|
|
77fee1390f | ||
|
|
bfb54189b8 | ||
|
|
fb43885d85 | ||
|
|
7c1f705e64 | ||
|
|
fe1fa7689a | ||
|
|
8b79eee081 | ||
|
|
4a3e04ced9 | ||
|
|
61618250ec | ||
|
|
60c0ca3ae5 | ||
|
|
3436405c55 | ||
|
|
d43c3ec865 | ||
|
|
545da96d15 | ||
|
|
f53305c404 | ||
|
|
199d976e3d | ||
|
|
a1366f0ef1 | ||
|
|
91192a8a8f | ||
|
|
c6f16b0558 | ||
|
|
0d37eb65c9 | ||
|
|
f7e3a74794 | ||
|
|
976b07cd61 | ||
|
|
cff9cd5fb8 | ||
|
|
f0fd947046 | ||
|
|
7f4ada9c4b | ||
|
|
db4ef328b1 | ||
|
|
b438802e71 | ||
|
|
632a19ceeb | ||
|
|
629f8ece7a | ||
|
|
9215f9beb5 | ||
|
|
154431e86c | ||
|
|
8cfb14198b | ||
|
|
2ed475fc76 | ||
|
|
34512da2fb | ||
|
|
5b3c6f7752 | ||
|
|
f4dd27ca3f | ||
|
|
ce47d8c591 | ||
|
|
b941ee9aa9 | ||
|
|
45de3f0128 | ||
|
|
41d3826c1a | ||
|
|
f2ff6e6a70 | ||
|
|
e47c90c645 | ||
|
|
d2fef20239 | ||
|
|
3b359cfc4f | ||
|
|
acfc5a4676 | ||
|
|
a6b1647f27 | ||
|
|
d5851bbb6a | ||
|
|
397bbe2655 | ||
|
|
95d8d3ef72 | ||
|
|
1b51a1aedd | ||
|
|
ff75b3cd1f | ||
|
|
2abcaf02e2 | ||
|
|
d0c9f5a776 | ||
|
|
a46d510e2b | ||
|
|
2d861b4077 | ||
|
|
a667c957ee | ||
|
|
107c09604a | ||
|
|
2b0567b368 | ||
|
|
d00a56bec0 | ||
|
|
ead8cc6cec | ||
|
|
806ff9daaf | ||
|
|
b3863a3e10 | ||
|
|
5559107776 | ||
|
|
af1cf806f0 | ||
|
|
0cf33c6119 | ||
|
|
b38b8a3365 | ||
|
|
e998c8434d | ||
|
|
4b90cf93d3 | ||
|
|
3dd4e33758 | ||
|
|
6caeb11598 | ||
|
|
11a383b7e5 | ||
|
|
fd2a2530b1 | ||
|
|
2f42b2d072 | ||
|
|
775f58f972 | ||
|
|
5c0abb3d96 | ||
|
|
3dc46bc5a3 | ||
|
|
e2871fc048 | ||
|
|
44e3f9b49f | ||
|
|
d3f4263639 | ||
|
|
9dec05f62d | ||
|
|
0447aa07fa | ||
|
|
0e25af790d | ||
|
|
d8484e777f | ||
|
|
665a3dbcbf | ||
|
|
103af2e2c1 | ||
|
|
d0daeb0078 | ||
|
|
a9aa49c2e5 | ||
|
|
aad24389a7 | ||
|
|
4b4f6f3201 | ||
|
|
81f82d3b73 | ||
|
|
4103ccf791 | ||
|
|
e2ed11f960 | ||
|
|
8e1cd67a3d | ||
|
|
18b4caa55e | ||
|
|
5e781a9010 | ||
|
|
277d817429 | ||
|
|
970f202757 | ||
|
|
2c6b2b47a4 | ||
|
|
5c8be4b21f | ||
|
|
558a4a2b30 | ||
|
|
203ad7d594 | ||
|
|
04cf1aed9c | ||
|
|
8c14526f85 | ||
|
|
1ff241411b |
@@ -9,6 +9,7 @@ data/GeoLite2-City*
|
||||
data/database.sqlite
|
||||
data/shlink-tests.db
|
||||
CHANGELOG.md
|
||||
CONTRIBUTING.md
|
||||
UPGRADE.md
|
||||
composer.lock
|
||||
vendor
|
||||
|
||||
2
.github/ISSUE_TEMPLATE/Bug.md
vendored
2
.github/ISSUE_TEMPLATE/Bug.md
vendored
@@ -18,7 +18,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
|
||||
* How do you serve Shlink: Self-hosted Apache|Self-hosted nginx|Self-hosted openswoole|Docker image
|
||||
* Database engine used: MySQL|MariaDB|PostgreSQL|MicrosoftSQL|SQLite (x.y.z)
|
||||
|
||||
#### Summary
|
||||
|
||||
2
.github/ISSUE_TEMPLATE/Question_Support.md
vendored
2
.github/ISSUE_TEMPLATE/Question_Support.md
vendored
@@ -18,7 +18,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
|
||||
* How do you serve Shlink: Self-hosted Apache|Self-hosted nginx|Self-hosted openswoole|Docker image
|
||||
* Database engine used: MySQL|MariaDB|PostgreSQL|MicrosoftSQL|SQLite (x.y.z)
|
||||
|
||||
#### Summary
|
||||
|
||||
53
.github/workflows/ci.yml
vendored
53
.github/workflows/ci.yml
vendored
@@ -6,13 +6,14 @@ on:
|
||||
branches:
|
||||
- main
|
||||
- develop
|
||||
- 2.x
|
||||
|
||||
jobs:
|
||||
static-analysis:
|
||||
runs-on: ubuntu-20.04
|
||||
runs-on: ubuntu-22.04
|
||||
strategy:
|
||||
matrix:
|
||||
php-version: ['8.0']
|
||||
php-version: ['8.1']
|
||||
command: ['cs', 'stan', 'swagger:validate']
|
||||
steps:
|
||||
- name: Checkout code
|
||||
@@ -22,16 +23,17 @@ jobs:
|
||||
with:
|
||||
php-version: ${{ matrix.php-version }}
|
||||
tools: composer
|
||||
extensions: openswoole-4.8.1
|
||||
extensions: openswoole-4.11.1
|
||||
coverage: none
|
||||
- run: composer install --no-interaction --prefer-dist
|
||||
- name: Install dependencies
|
||||
run: composer install --no-interaction --prefer-dist
|
||||
- run: composer ${{ matrix.command }}
|
||||
|
||||
tests:
|
||||
runs-on: ubuntu-20.04
|
||||
runs-on: ubuntu-22.04
|
||||
strategy:
|
||||
matrix:
|
||||
php-version: ['8.0', '8.1']
|
||||
php-version: ['8.1']
|
||||
test-group: ['unit', 'api']
|
||||
steps:
|
||||
- name: Checkout code
|
||||
@@ -44,13 +46,14 @@ jobs:
|
||||
with:
|
||||
php-version: ${{ matrix.php-version }}
|
||||
tools: composer
|
||||
extensions: openswoole-4.8.1
|
||||
extensions: openswoole-4.11.1
|
||||
coverage: pcov
|
||||
ini-values: pcov.directory=module
|
||||
- run: composer install --no-interaction --prefer-dist
|
||||
- name: Install dependencies
|
||||
run: composer install --no-interaction --prefer-dist
|
||||
- run: composer test:${{ matrix.test-group }}:ci
|
||||
- uses: actions/upload-artifact@v2
|
||||
if: ${{ matrix.php-version == '8.0' }}
|
||||
if: ${{ matrix.php-version == '8.1' }}
|
||||
with:
|
||||
name: coverage-${{ matrix.test-group }}
|
||||
path: |
|
||||
@@ -58,10 +61,10 @@ jobs:
|
||||
build/coverage-${{ matrix.test-group }}.cov
|
||||
|
||||
db-tests:
|
||||
runs-on: ubuntu-20.04
|
||||
runs-on: ubuntu-22.04
|
||||
strategy:
|
||||
matrix:
|
||||
php-version: ['8.0', '8.1']
|
||||
php-version: ['8.1']
|
||||
platform: ['sqlite:ci', 'mysql', 'maria', 'postgres', 'ms']
|
||||
env:
|
||||
LC_ALL: C
|
||||
@@ -79,10 +82,11 @@ jobs:
|
||||
with:
|
||||
php-version: ${{ matrix.php-version }}
|
||||
tools: composer
|
||||
extensions: openswoole-4.8.1, pdo_sqlsrv-5.10.0beta2
|
||||
extensions: openswoole-4.11.1, pdo_sqlsrv-5.10.1
|
||||
coverage: pcov
|
||||
ini-values: pcov.directory=module
|
||||
- run: composer install --no-interaction --prefer-dist
|
||||
- name: Install dependencies
|
||||
run: composer install --no-interaction --prefer-dist
|
||||
- name: Create test database
|
||||
if: ${{ matrix.platform == 'ms' }}
|
||||
run: docker-compose exec -T shlink_db_ms /opt/mssql-tools/bin/sqlcmd -S localhost -U sa -P 'Passw0rd!' -Q "CREATE DATABASE shlink_test;"
|
||||
@@ -90,7 +94,7 @@ jobs:
|
||||
run: composer test:db:${{ matrix.platform }}
|
||||
- name: Upload code coverage
|
||||
uses: actions/upload-artifact@v2
|
||||
if: ${{ matrix.php-version == '8.0' && matrix.platform == 'sqlite:ci' }}
|
||||
if: ${{ matrix.php-version == '8.1' && matrix.platform == 'sqlite:ci' }}
|
||||
with:
|
||||
name: coverage-db
|
||||
path: |
|
||||
@@ -101,10 +105,10 @@ jobs:
|
||||
needs:
|
||||
- tests
|
||||
- db-tests
|
||||
runs-on: ubuntu-20.04
|
||||
runs-on: ubuntu-22.04
|
||||
strategy:
|
||||
matrix:
|
||||
php-version: ['8.0', '8.1']
|
||||
php-version: ['8.1']
|
||||
test-group: ['unit', 'db', 'api']
|
||||
steps:
|
||||
- name: Checkout code
|
||||
@@ -114,10 +118,11 @@ jobs:
|
||||
with:
|
||||
php-version: ${{ matrix.php-version }}
|
||||
tools: composer
|
||||
extensions: openswoole-4.8.1
|
||||
extensions: openswoole-4.11.1
|
||||
coverage: pcov
|
||||
ini-values: pcov.directory=module
|
||||
- run: composer install --no-interaction --prefer-dist
|
||||
- name: Install dependencies
|
||||
run: composer install --no-interaction --prefer-dist
|
||||
- uses: actions/download-artifact@v2
|
||||
with:
|
||||
path: build
|
||||
@@ -132,10 +137,10 @@ jobs:
|
||||
needs:
|
||||
- tests
|
||||
- db-tests
|
||||
runs-on: ubuntu-20.04
|
||||
runs-on: ubuntu-22.04
|
||||
strategy:
|
||||
matrix:
|
||||
php-version: ['8.0']
|
||||
php-version: ['8.1']
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
@@ -151,8 +156,8 @@ jobs:
|
||||
- run: mv build/coverage-unit/coverage-unit.cov build/coverage-unit.cov
|
||||
- run: mv build/coverage-db/coverage-db.cov build/coverage-db.cov
|
||||
- run: mv build/coverage-api/coverage-api.cov build/coverage-api.cov
|
||||
- run: wget https://phar.phpunit.de/phpcov-8.2.0.phar
|
||||
- run: php phpcov-8.2.0.phar merge build --clover build/clover.xml
|
||||
- run: wget https://phar.phpunit.de/phpcov-8.2.1.phar
|
||||
- run: php phpcov-8.2.1.phar merge build --clover build/clover.xml
|
||||
- name: Publish coverage
|
||||
uses: codecov/codecov-action@v1
|
||||
with:
|
||||
@@ -162,7 +167,7 @@ jobs:
|
||||
needs:
|
||||
- mutation-tests
|
||||
- upload-coverage
|
||||
runs-on: ubuntu-20.04
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: geekyeggo/delete-artifact@v1
|
||||
with:
|
||||
@@ -172,7 +177,7 @@ jobs:
|
||||
coverage-api
|
||||
|
||||
build-docker-image:
|
||||
runs-on: ubuntu-20.04
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
|
||||
2
.github/workflows/docker-image-build.yml
vendored
2
.github/workflows/docker-image-build.yml
vendored
@@ -9,7 +9,7 @@ on:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-20.04
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
|
||||
14
.github/workflows/publish-release.yml
vendored
14
.github/workflows/publish-release.yml
vendored
@@ -7,10 +7,10 @@ on:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-20.04
|
||||
runs-on: ubuntu-22.04
|
||||
strategy:
|
||||
matrix:
|
||||
php-version: ['8.0', '8.1']
|
||||
php-version: ['8.1']
|
||||
swoole: ['yes', 'no']
|
||||
steps:
|
||||
- name: Checkout code
|
||||
@@ -20,7 +20,7 @@ jobs:
|
||||
with:
|
||||
php-version: ${{ matrix.php-version }}
|
||||
tools: composer
|
||||
extensions: openswoole-4.8.1
|
||||
extensions: openswoole-4.11.1
|
||||
- if: ${{ matrix.swoole == 'yes' }}
|
||||
run: ./build.sh ${GITHUB_REF#refs/tags/v}
|
||||
- if: ${{ matrix.swoole == 'no' }}
|
||||
@@ -32,7 +32,7 @@ jobs:
|
||||
|
||||
publish:
|
||||
needs: ['build']
|
||||
runs-on: ubuntu-20.04
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
@@ -50,11 +50,11 @@ jobs:
|
||||
|
||||
delete-artifacts:
|
||||
needs: ['publish']
|
||||
runs-on: ubuntu-20.04
|
||||
runs-on: ubuntu-22.04
|
||||
strategy:
|
||||
matrix:
|
||||
php-version: [ '8.0', '8.1' ]
|
||||
swoole: [ 'yes', 'no' ]
|
||||
php-version: ['8.1']
|
||||
swoole: ['yes', 'no']
|
||||
steps:
|
||||
- uses: geekyeggo/delete-artifact@v1
|
||||
with:
|
||||
|
||||
8
.github/workflows/publish-swagger-spec.yml
vendored
8
.github/workflows/publish-swagger-spec.yml
vendored
@@ -7,10 +7,10 @@ on:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-20.04
|
||||
runs-on: ubuntu-22.04
|
||||
strategy:
|
||||
matrix:
|
||||
php-version: ['8.0']
|
||||
php-version: ['8.1']
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
@@ -23,12 +23,12 @@ jobs:
|
||||
with:
|
||||
php-version: ${{ matrix.php-version }}
|
||||
tools: composer
|
||||
extensions: openswoole-4.8.1
|
||||
extensions: openswoole-4.11.1
|
||||
coverage: none
|
||||
- run: composer install --no-interaction --prefer-dist
|
||||
- run: composer swagger:inline
|
||||
- run: mkdir ${{ steps.determine_version.outputs.version }}
|
||||
- run: mv docs/swagger/swagger-inlined.json ${{ steps.determine_version.outputs.version }}/oas.json
|
||||
- run: mv docs/swagger/swagger-inlined.json ${{ steps.determine_version.outputs.version }}/open-api-spec.json
|
||||
- name: Publish spec
|
||||
uses: JamesIves/github-pages-deploy-action@4.1.7
|
||||
with:
|
||||
|
||||
214
CHANGELOG.md
214
CHANGELOG.md
@@ -4,6 +4,210 @@ All notable changes to this project will be documented in this file.
|
||||
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com), and this project adheres to [Semantic Versioning](https://semver.org).
|
||||
|
||||
## [3.2.0] - 2022-08-05
|
||||
### Added
|
||||
* [#854](https://github.com/shlinkio/shlink/issues/854) Added support for multi-segment custom slugs.
|
||||
|
||||
The feature is disabled by default, but you can optionally opt in. If you do, you will be able to create short URLs with multiple segments in the custom slug, like `https://example.com/foo/bar/baz`.
|
||||
|
||||
* [#1280](https://github.com/shlinkio/shlink/issues/1280) Added missing visit-related commands.
|
||||
|
||||
Now you can run `tag:visits`, `domain:visits`, `visit:orphan` or `visit:non-orphan` to get the corresponding list of visits from the command line.
|
||||
|
||||
* [#962](https://github.com/shlinkio/shlink/issues/962) Added new real-time update for new short URLs.
|
||||
|
||||
You can now subscribe to the `https://shlink.io/new-short-url` topic on any of the supported async updates technologies in order to get notified when a short URL is created.
|
||||
|
||||
* [#1367](https://github.com/shlinkio/shlink/issues/1367) Added support to publish real-time updates in redis pub/sub.
|
||||
|
||||
The publishing will happen in the same redis instance/cluster configured for caching.
|
||||
|
||||
### Changed
|
||||
* [#1452](https://github.com/shlinkio/shlink/issues/1452) Updated to monolog 3
|
||||
* [#1485](https://github.com/shlinkio/shlink/issues/1485) Changed payload published in RabbitMQ for all visits events, in order to conform with the Async API spec.
|
||||
|
||||
Since this is a breaking change, also provided a new `RABBITMQ_LEGACY_VISITS_PUBLISHING=true` env var that can be provided in order to keep the old payload.
|
||||
|
||||
This env var is considered deprecated and will be removed in Shlink 4, when the legacy format will no longer be supported.
|
||||
|
||||
### Deprecated
|
||||
* *Nothing*
|
||||
|
||||
### Removed
|
||||
* [#1280](https://github.com/shlinkio/shlink/issues/1280) Dropped support for PHP 8.0
|
||||
|
||||
### Fixed
|
||||
* [#1471](https://github.com/shlinkio/shlink/issues/1471) Fixed error when running `visit:locate` command with any extra parameter (like `--retry`).
|
||||
|
||||
|
||||
## [3.1.2] - 2022-06-04
|
||||
### Added
|
||||
* *Nothing*
|
||||
|
||||
### Changed
|
||||
* *Nothing*
|
||||
|
||||
### Deprecated
|
||||
* *Nothing*
|
||||
|
||||
### Removed
|
||||
* *Nothing*
|
||||
|
||||
### Fixed
|
||||
* [#1448](https://github.com/shlinkio/shlink/issues/1448) Fixed HTML entities not being properly parsed when auto-resolving page titles.
|
||||
* [#1458](https://github.com/shlinkio/shlink/issues/1458) Fixed 500 error when filtering short URLs by ALL tags and search term.
|
||||
|
||||
|
||||
## [3.1.1] - 2022-05-09
|
||||
### Added
|
||||
* *Nothing*
|
||||
|
||||
### Changed
|
||||
* [#1444](https://github.com/shlinkio/shlink/issues/1444) Updated docker image to openswoole 4.11.1, in an attempt to fix error.
|
||||
|
||||
### Deprecated
|
||||
* *Nothing*
|
||||
|
||||
### Removed
|
||||
* *Nothing*
|
||||
|
||||
### Fixed
|
||||
* [#1439](https://github.com/shlinkio/shlink/issues/1439) Fixed crash when trying to auto-resolve titles for URLs which serve large binary files.
|
||||
|
||||
|
||||
## [3.1.0] - 2022-04-23
|
||||
### Added
|
||||
* [#1294](https://github.com/shlinkio/shlink/issues/1294) Allowed to provide a specific domain when importing URLs from YOURLS.
|
||||
* [#1416](https://github.com/shlinkio/shlink/issues/1416) Added support to import URLs from Kutt.it.
|
||||
* [#1418](https://github.com/shlinkio/shlink/issues/1418) Added support to customize the timezone used by Shlink, falling back to the default one set in PHP config.
|
||||
|
||||
The timezone can be set via the `TIMEZONE` env var, or using the installer tool.
|
||||
|
||||
* [#1309](https://github.com/shlinkio/shlink/issues/1309) Improved URL importing, ensuring individual errors do not make the whole process fail, and instead, failing URLs are skipped.
|
||||
* [#1162](https://github.com/shlinkio/shlink/issues/1162) Added new endpoint to get visits by domain.
|
||||
|
||||
The endpoint is `GET /domains/{domain}/visits`, and it has the same capabilities as any other visits endpoint, allowing pagination and filtering.
|
||||
|
||||
### Changed
|
||||
* [#1359](https://github.com/shlinkio/shlink/issues/1359) Hidden database commands.
|
||||
* [#1385](https://github.com/shlinkio/shlink/issues/1385) Prevented a big error message from being logged when using Shlink without mercure.
|
||||
* [#1398](https://github.com/shlinkio/shlink/issues/1398) Increased required mutation score for unit tests to 85%.
|
||||
* [#1419](https://github.com/shlinkio/shlink/issues/1419) Input dates are now parsed to Shlink's configured timezone or default timezone before using them for database queries.
|
||||
* [#1428](https://github.com/shlinkio/shlink/issues/1428) Updated native dependencies in docker image and base image to PHP v8.1.5.
|
||||
|
||||
### Deprecated
|
||||
* [#1340](https://github.com/shlinkio/shlink/issues/1340) Deprecated webhooks. New events will only be added to other real-time updates approaches, and webhooks will be completely removed in Shlink 4.0.0.
|
||||
|
||||
### Removed
|
||||
* *Nothing*
|
||||
|
||||
### Fixed
|
||||
* [#1397](https://github.com/shlinkio/shlink/issues/1397) Fixed `db:create` command always reporting the schema exists if the `db:migrate` command has been run before by mistake.
|
||||
* [#1402](https://github.com/shlinkio/shlink/issues/1402) Fixed the base path getting appended with the default domain by mistake, causing multiple side effects in several places.
|
||||
|
||||
|
||||
## [3.0.3] - 2022-02-19
|
||||
### Added
|
||||
* *Nothing*
|
||||
|
||||
### Changed
|
||||
* [#1382](https://github.com/shlinkio/shlink/issues/1382) Updated docker image to PHP 8.1.3.
|
||||
|
||||
### Deprecated
|
||||
* *Nothing*
|
||||
|
||||
### Removed
|
||||
* *Nothing*
|
||||
|
||||
### Fixed
|
||||
* [#1377](https://github.com/shlinkio/shlink/issues/1377) Fixed installer always setting delete threshold with value 1.
|
||||
* [#1379](https://github.com/shlinkio/shlink/issues/1379) Ensured API keys cannot be created with a domain-only role linked to default domain.
|
||||
|
||||
|
||||
## [3.0.2] - 2022-02-10
|
||||
### Added
|
||||
* *Nothing*
|
||||
|
||||
### Changed
|
||||
* *Nothing*
|
||||
|
||||
### Deprecated
|
||||
* *Nothing*
|
||||
|
||||
### Removed
|
||||
* *Nothing*
|
||||
|
||||
### Fixed
|
||||
* [#1373](https://github.com/shlinkio/shlink/issues/1373) Fixed incorrect config import when updating from Shlink 2.x using SQLite.
|
||||
* [#1369](https://github.com/shlinkio/shlink/issues/1369) Fixed slow regexps in `.htaccess` file.
|
||||
|
||||
|
||||
## [3.0.1] - 2022-02-04
|
||||
### Added
|
||||
* *Nothing*
|
||||
|
||||
### Changed
|
||||
* *Nothing*
|
||||
|
||||
### Deprecated
|
||||
* *Nothing*
|
||||
|
||||
### Removed
|
||||
* *Nothing*
|
||||
|
||||
### Fixed
|
||||
* [#1363](https://github.com/shlinkio/shlink/issues/1363) Fixed titles being resolved no matter what when `validateUrl` is not set or is explicitly set to true.
|
||||
* [#1352](https://github.com/shlinkio/shlink/issues/1352) Updated to stable pdo_sqlsrv in docker image.
|
||||
|
||||
|
||||
## [3.0.0] - 2022-01-28
|
||||
### Added
|
||||
* [#767](https://github.com/shlinkio/shlink/issues/767) Added full support to use emojis everywhere, whether it is custom slugs, titles, referrers, etc.
|
||||
* [#1274](https://github.com/shlinkio/shlink/issues/1274) Added support to filter short URLs lists by all provided tags.
|
||||
|
||||
The `GET /short-urls` endpoint now accepts a `tagsMode=all` param which will make only short URLs matching **all** the tags in the `tags[]` query param, to be returned.
|
||||
|
||||
The `short-urls:list` command now accepts a `-i`/`--including-all-tags` flag which behaves the same.
|
||||
|
||||
* [#1273](https://github.com/shlinkio/shlink/issues/1273) Added support for pagination in tags lists, allowing to improve performance by loading subsets of tags.
|
||||
|
||||
For backwards compatibility, lists continue returning all items by default, but the `GET /tags` endpoint now supports `page` and `itemsPerPage` query params, to make sure only a subset of the tags is returned.
|
||||
|
||||
This is supported both when invoking the endpoint with and without `withStats=true` query param.
|
||||
|
||||
Additionally, the endpoint also supports filtering by `searchTerm` query param. When provided, only tags matching it will be returned.
|
||||
|
||||
* [#1063](https://github.com/shlinkio/shlink/issues/1063) Added new endpoint that allows fetching all existing non-orphan visits, in case you need a global view of all visits received by your Shlink instance.
|
||||
|
||||
This can be achieved using the `GET /visits/non-orphan` endpoint.
|
||||
|
||||
### Changed
|
||||
* [#1277](https://github.com/shlinkio/shlink/issues/1277) Reduced docker image size to 45% of the original size.
|
||||
* [#1268](https://github.com/shlinkio/shlink/issues/1268) Updated dependencies, including symfony/console 6 and mezzio/mezzio-swoole 4.
|
||||
* [#1283](https://github.com/shlinkio/shlink/issues/1283) Changed behavior of `DELETE_SHORT_URL_THRESHOLD` env var, disabling the feature if a value was not provided.
|
||||
* [#1300](https://github.com/shlinkio/shlink/issues/1300) Changed default ordering for short URLs list, returning always from newest to oldest.
|
||||
* [#1299](https://github.com/shlinkio/shlink/issues/1299) Updated to the latest base docker images, based in PHP 8.1.1, and bumped openswoole to v4.9.1.
|
||||
* [#1282](https://github.com/shlinkio/shlink/issues/1282) Env vars now have precedence over installer options.
|
||||
* [#1328](https://github.com/shlinkio/shlink/issues/1328) Refactored ShortUrlsRepository to use DTOs in methods with too many arguments.
|
||||
|
||||
### Deprecated
|
||||
* [#1315](https://github.com/shlinkio/shlink/issues/1315) Deprecated `GET /tags?withStats=true` endpoint. Use `GET /tags/stats` instead.
|
||||
|
||||
### Removed
|
||||
* [#1275](https://github.com/shlinkio/shlink/issues/1275) Removed everything that was deprecated in Shlink 2.x.
|
||||
|
||||
See [UPGRADE](UPGRADE.md#from-v2x-to-v3x) doc in order to get details on how to migrate to this version.
|
||||
|
||||
* [#1347](https://github.com/shlinkio/shlink/issues/1347) Dropped support for regular swoole in favor of openswoole.
|
||||
|
||||
Since openswoole support was introduced in the previous release version, Shlink will still consider the swoole extension as openswoole, as at the moment, functionality hasn't deviated too much, and will simplify migrating to Shlink 3.0.0
|
||||
|
||||
However, there's no longer active testing with regular swoole, and it is considered no longer supported. If some incompatibility arises, the only supported solution will be to migrate to openswoole.
|
||||
|
||||
### Fixed
|
||||
* *Nothing*
|
||||
|
||||
|
||||
## [2.10.3] - 2022-01-23
|
||||
### Added
|
||||
* *Nothing*
|
||||
@@ -437,7 +641,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com), and this
|
||||
* *Nothing*
|
||||
|
||||
### Fixed
|
||||
* [#979](https://github.com/shlinkio/shlink/issues/979) Added missing `itemsPerPage` query param to swagger docs for short RULs list.
|
||||
* [#979](https://github.com/shlinkio/shlink/issues/979) Added missing `itemsPerPage` query param to swagger docs for short URLs list.
|
||||
* [#980](https://github.com/shlinkio/shlink/issues/980) Fixed value used for `Access-Control-Allow-Origin`, that could not work as expected when including an IP address.
|
||||
* [#947](https://github.com/shlinkio/shlink/issues/947) Fixed incorrect value returned in `Access-Control-Allow-Methods` header, which always contained all methods.
|
||||
|
||||
@@ -906,7 +1110,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com), and this
|
||||
* Preview generation feature completely removed.
|
||||
* Authentication against REST API using JWT is no longer supported.
|
||||
|
||||
See [UPGRADE](UPGRADE.md) doc in order to get details on how to migrate to this version.
|
||||
See [UPGRADE](UPGRADE.md#from-v1x-to-v2x) doc in order to get details on how to migrate to this version.
|
||||
|
||||
### Fixed
|
||||
* [#600](https://github.com/shlinkio/shlink/issues/600) Fixed health action so that it works with and without version in the path.
|
||||
@@ -1085,7 +1289,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com), and this
|
||||
|
||||
Endpoints and commands which create short URLs support providing the `domain` now (via query param or CLI flag). If not provided, the short URLs will still be "attached" to the default domain.
|
||||
|
||||
Custom slugs can be created on multiple domains, allowing to share links like `https://doma.in/my-compaign` and `https://example.com/my-campaign`, under the same shlink instance.
|
||||
Custom slugs can be created on multiple domains, allowing to share links like `https://doma.in/my-campaign` and `https://example.com/my-campaign`, under the same shlink instance.
|
||||
|
||||
When resolving a short URL to redirect end users, the following rules are applied:
|
||||
|
||||
@@ -1335,7 +1539,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com), and this
|
||||
|
||||
### Fixed
|
||||
* [#309](https://github.com/shlinkio/shlink/issues/309) Added missing favicon to prevent 404 errors logged when an error page is loaded in a browser.
|
||||
* [#310](https://github.com/shlinkio/shlink/issues/310) Fixed execution context not being properly detected, making `CloseDbConnectionMiddlware` to be always piped. Now the check is not even made, which simplifies everything.
|
||||
* [#310](https://github.com/shlinkio/shlink/issues/310) Fixed execution context not being properly detected, making `CloseDbConnectionMiddleware` to be always piped. Now the check is not even made, which simplifies everything.
|
||||
|
||||
|
||||
## [1.15.0] - 2018-12-02
|
||||
@@ -1400,7 +1604,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com), and this
|
||||
|
||||
### Changed
|
||||
* [#241](https://github.com/shlinkio/shlink/issues/241) Fixed columns in `visit_locations` table, to be snake_case instead of camelCase.
|
||||
* [#228](https://github.com/shlinkio/shlink/issues/228) Updated how exceptions are serialized into logs, by using monlog's `PsrLogMessageProcessor`.
|
||||
* [#228](https://github.com/shlinkio/shlink/issues/228) Updated how exceptions are serialized into logs, by using monolog's `PsrLogMessageProcessor`.
|
||||
* [#225](https://github.com/shlinkio/shlink/issues/225) Performance and maintainability slightly improved by enforcing via code sniffer that all global namespace classes, functions and constants are explicitly imported.
|
||||
* [#196](https://github.com/shlinkio/shlink/issues/196) Reduced anemic model in entities, defining more expressive public APIs instead.
|
||||
* [#249](https://github.com/shlinkio/shlink/issues/249) Added [functional-php](https://github.com/lstrojny/functional-php) to ease collections handling.
|
||||
|
||||
@@ -31,7 +31,7 @@ Then you will have to follow these steps:
|
||||
* 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.
|
||||
Once you finish this, you will have the project exposed in ports `8000` through nginx+php-fpm and `8080` through openswoole.
|
||||
|
||||
> Note: The `indocker` shell script is a helper tool used to run commands inside the main docker container.
|
||||
|
||||
@@ -46,9 +46,7 @@ This is a simplified version of the project structure:
|
||||
```
|
||||
shlink
|
||||
├── bin
|
||||
│ ├── cli
|
||||
│ ├── install
|
||||
│ └── update
|
||||
│ └── cli
|
||||
├── config
|
||||
│ ├── autoload
|
||||
│ ├── params
|
||||
@@ -75,12 +73,12 @@ shlink
|
||||
|
||||
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.
|
||||
* `bin`: It contains the CLI tools. The `cli` one is the main entry point to run shlink from the command line.
|
||||
* `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 or architectural decision records.
|
||||
* `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.
|
||||
* `module`: Contains a sub-folder 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 openswoole.
|
||||
|
||||
## Project tests
|
||||
|
||||
@@ -96,7 +94,7 @@ In order to ensure stability and no regressions are introduced while developing
|
||||
|
||||
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 with swoole, and test it from the outside by interacting with the REST API.
|
||||
* **API tests**: These are E2E tests that spin up an instance of the app with openswoole, and test it from the outside by interacting with the REST API.
|
||||
|
||||
These are the best tests to catch regressions, and to verify everything behaves as expected.
|
||||
|
||||
@@ -125,12 +123,6 @@ Depending on the kind of contribution, maybe not all kinds of tests are needed,
|
||||
* Run `./indocker composer ci` to run all previous commands together. This command is run during the project's continuous integration.
|
||||
* Run `./indocker composer ci:parallel` to do the same as in previous case, but parallelizing non-conflicting tasks as much as possible.
|
||||
|
||||
> 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
|
||||
|
||||
**Important!**: Before starting to work on a pull request, make sure you always [open an issue](https://github.com/shlinkio/shlink/issues/new/choose) first.
|
||||
|
||||
52
Dockerfile
52
Dockerfile
@@ -1,9 +1,9 @@
|
||||
FROM php:8.1.0-alpine3.15 as base
|
||||
FROM php:8.1.9-alpine3.16 as base
|
||||
|
||||
ARG SHLINK_VERSION=latest
|
||||
ENV SHLINK_VERSION ${SHLINK_VERSION}
|
||||
ENV OPENSWOOLE_VERSION 4.8.1
|
||||
ENV PDO_SQLSRV_VERSION 5.10.0beta2
|
||||
ENV OPENSWOOLE_VERSION 4.11.1
|
||||
ENV PDO_SQLSRV_VERSION 5.10.1
|
||||
ENV MS_ODBC_SQL_VERSION 17.5.2.2
|
||||
ENV LC_ALL "C"
|
||||
|
||||
@@ -11,42 +11,28 @@ WORKDIR /etc/shlink
|
||||
|
||||
# Install required PHP extensions
|
||||
RUN \
|
||||
# Install extensions with no extra dependencies
|
||||
docker-php-ext-install -j"$(nproc)" pdo_mysql calendar sockets bcmath && \
|
||||
# Install sqlite
|
||||
apk add --no-cache sqlite-libs sqlite-dev && \
|
||||
# Temp install dev dependencies needed to compile the extensions
|
||||
apk add --no-cache --virtual .dev-deps sqlite-dev postgresql-dev icu-dev libzip-dev zlib-dev libpng-dev && \
|
||||
docker-php-ext-install -j"$(nproc)" pdo_mysql pdo_pgsql intl calendar sockets bcmath zip gd && \
|
||||
apk add --no-cache sqlite-libs && \
|
||||
docker-php-ext-install -j"$(nproc)" pdo_sqlite && \
|
||||
# Install postgres
|
||||
apk add --no-cache postgresql-dev && \
|
||||
docker-php-ext-install -j"$(nproc)" pdo_pgsql && \
|
||||
# Install intl
|
||||
apk add --no-cache icu-dev && \
|
||||
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 && \
|
||||
# Install gmp
|
||||
apk add --no-cache gmp-dev && \
|
||||
docker-php-ext-install -j"$(nproc)" gmp
|
||||
# Remove temp dev extensions, and install prod equivalents that are required at runtime
|
||||
apk del .dev-deps && \
|
||||
apk add --no-cache postgresql icu libzip libpng
|
||||
|
||||
# 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_${MS_ODBC_SQL_VERSION}-1_amd64.apk && \
|
||||
apk add --allow-untrusted msodbcsql17_${MS_ODBC_SQL_VERSION}-1_amd64.apk && \
|
||||
apk add --no-cache --virtual .phpize-deps ${PHPIZE_DEPS} unixodbc-dev && \
|
||||
pecl install pdo_sqlsrv-${PDO_SQLSRV_VERSION} && \
|
||||
docker-php-ext-enable pdo_sqlsrv && \
|
||||
apk del .phpize-deps && \
|
||||
rm msodbcsql17_${MS_ODBC_SQL_VERSION}-1_amd64.apk ; \
|
||||
fi
|
||||
|
||||
# Install openswoole
|
||||
RUN apk add --no-cache --virtual .phpize-deps ${PHPIZE_DEPS} && \
|
||||
# Install openswoole and sqlsrv driver for x86_64 builds
|
||||
RUN apk add --no-cache --virtual .phpize-deps ${PHPIZE_DEPS} unixodbc-dev && \
|
||||
pecl install openswoole-${OPENSWOOLE_VERSION} && \
|
||||
docker-php-ext-enable openswoole && \
|
||||
if [ $(uname -m) == "x86_64" ]; then \
|
||||
wget https://download.microsoft.com/download/e/4/e/e4e67866-dffd-428c-aac7-8d28ddafb39b/msodbcsql17_${MS_ODBC_SQL_VERSION}-1_amd64.apk && \
|
||||
apk add --no-cache --allow-untrusted msodbcsql17_${MS_ODBC_SQL_VERSION}-1_amd64.apk && \
|
||||
pecl install pdo_sqlsrv-${PDO_SQLSRV_VERSION} && \
|
||||
docker-php-ext-enable pdo_sqlsrv && \
|
||||
rm msodbcsql17_${MS_ODBC_SQL_VERSION}-1_amd64.apk ; \
|
||||
fi; \
|
||||
apk del .phpize-deps
|
||||
|
||||
|
||||
# Install shlink
|
||||
FROM base as builder
|
||||
COPY . .
|
||||
|
||||
36
README.md
36
README.md
@@ -6,9 +6,10 @@
|
||||
[](https://packagist.org/packages/shlinkio/shlink)
|
||||
[](https://hub.docker.com/r/shlinkio/shlink/)
|
||||
[](https://github.com/shlinkio/shlink/blob/main/LICENSE)
|
||||
[](https://twitter.com/shlinkio)
|
||||
[](https://slnk.to/donate)
|
||||
|
||||
A PHP-based self-hosted URL shortener that can be used to serve shortened URLs under your own custom domain.
|
||||
A PHP-based self-hosted URL shortener that can be used to serve shortened URLs under your own domain.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
@@ -26,7 +27,7 @@ This document contains the very basics to get started with Shlink. If you want t
|
||||
|
||||
## Docker image
|
||||
|
||||
Starting with version 1.15.0, an official docker image is provided. You can learn how to use it by reading [the docs](https://shlink.io/documentation/install-docker-image/).
|
||||
You can learn how to use the official docker image by reading [the docs](https://shlink.io/documentation/install-docker-image/).
|
||||
|
||||
The idea is that you can just generate a container using the image and provide the custom config via env vars.
|
||||
|
||||
@@ -34,13 +35,14 @@ The idea is that you can just generate a container using the image and provide t
|
||||
|
||||
First, make sure the host where you are going to run shlink fulfills these requirements:
|
||||
|
||||
* PHP 8.0 or 8.1
|
||||
* The next PHP extensions: json, curl, pdo, intl, gd and gmp.
|
||||
* apcu extension is recommended if you don't plan to use swoole or openswoole.
|
||||
* PHP 8.1
|
||||
* The next PHP extensions: json, curl, pdo, intl, gd and gmp/bcmath.
|
||||
* apcu extension is recommended if you don't plan to use openswoole.
|
||||
* xml extension is required if you want to generate QR codes in svg format.
|
||||
* sockets and bcmath extensions are required if you want to integrate with a RabbitMQ instance.
|
||||
* MySQL, MariaDB, PostgreSQL, Microsoft SQL Server or SQLite.
|
||||
* The web server of your choice with PHP integration (Apache or Nginx recommended).
|
||||
* MySQL, MariaDB, PostgreSQL, MicrosoftSQL or SQLite.
|
||||
* You will also need the corresponding pdo variation for the database you are planning to use: `pdo_mysql`, `pdo_pgsql`, `pdo_sqlsrv` or `pdo_sqlite`.
|
||||
* The [openswoole](https://openswoole.com/) PHP extension (if you plan to serve Shlink with openswoole) or the web server of your choice with PHP integration (like Apache or Nginx).
|
||||
|
||||
### Download
|
||||
|
||||
@@ -50,7 +52,7 @@ In order to run Shlink, you will need a built version of the project. There are
|
||||
|
||||
The easiest way to install shlink is by using one of the pre-bundled distributable packages.
|
||||
|
||||
Go to the [latest version](https://github.com/shlinkio/shlink/releases/latest) and download the `shlink*_dist.zip` file that suits your needs. You will find one for every supported PHP version and with/without swoole/openswoole integration.
|
||||
Go to the [latest version](https://github.com/shlinkio/shlink/releases/latest) and download the `shlink*_dist.zip` file that suits your needs. You will find one for every supported PHP version and with/without openswoole integration.
|
||||
|
||||
Finally, decompress the file in the location of your choice.
|
||||
|
||||
@@ -60,11 +62,13 @@ In order to run Shlink, you will need a built version of the project. There are
|
||||
|
||||
* Clone the project with git (`git clone https://github.com/shlinkio/shlink.git`), or download it by clicking the **Clone or download** green button.
|
||||
* 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 used as part of the generated dist file name, and to set the value returned when running `shlink -V` from the command line).
|
||||
* Run `./build.sh 3.0.0`, replacing the version with the version number you are going to build (the version number is used as part of the generated dist file name, and to set the value returned when running `shlink -V` from the command line).
|
||||
|
||||
After that, you will have a dist file inside the `build` directory, that you need to decompress in the location of 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 a [GitHub workflow](https://github.com/shlinkio/shlink/actions?query=workflow%3A%22Publish+release%22), attaching the generated dist file to it.
|
||||
> **Note**
|
||||
>
|
||||
> This is the process used when releasing new Shlink versions. After tagging the new version with git, the GitHub release is automatically created by a [GitHub workflow](https://github.com/shlinkio/shlink/actions?query=workflow%3A%22Publish+release%22), attaching the generated dist file to it.
|
||||
|
||||
### Configure
|
||||
|
||||
@@ -72,24 +76,24 @@ Despite how you built the project, you now need to configure it, by following th
|
||||
|
||||
* 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.**
|
||||
* 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.
|
||||
* Set up the application by running the `vendor/bin/shlink-installer 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.**
|
||||
* 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.
|
||||
|
||||
## Using shlink
|
||||
|
||||
Once shlink is installed, there are two main ways to interact with it:
|
||||
|
||||
* **The command line**. Try running `bin/cli` and see all the [available commands](#shlink-cli-help).
|
||||
* **The command line**: Try running `bin/cli` to see all the available commands.
|
||||
|
||||
All of those commands can be run with the `--help`/`-h` flag in order to see how to use them and all the available options.
|
||||
All of them can be run with the `--help`/`-h` flag in order to see how to use them and all the available options.
|
||||
|
||||
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/documentation/api-docs), and a sandbox which also documents every endpoint can be found in the [API Spec](https://api-spec.shlink.io/) portal.
|
||||
* **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 hosted by yourself.
|
||||
|
||||
Both the API and CLI allow you to do the same operations, except for API key management, which can be done from the command line interface only.
|
||||
Both the API and CLI allow you to do mostly the same operations, except for API key management, which can be done from the command line interface only.
|
||||
|
||||
## Contributing
|
||||
|
||||
|
||||
50
UPGRADE.md
50
UPGRADE.md
@@ -1,5 +1,53 @@
|
||||
# Upgrading
|
||||
|
||||
## From v2.x to v3.x
|
||||
|
||||
### Changes in REST API
|
||||
|
||||
* The `type` property returned when trying to delete a URL that reached the visits threshold, when using the `DELETE /short-urls/{shortCode}` endpoint, is now `INVALID_SHORT_URL_DELETION` instead of `INVALID_SHORTCODE_DELETION`.
|
||||
* The `INVALID_AUTHORIZATION` error no longer includes the `expectedTypes` property. Use `expectedHeaders` one instead.
|
||||
* The `GET /rest/v2/short-urls` endpoint no longer allows ordering by `visitsCount`, `visitCount` or `originalUrl`. Use `visits` instead of the first two, and `longUrl` instead of the last one.
|
||||
* The `GET /rest/v2/short-urls` endpoint no longer allows providing the ordering params with array notation, as in `/shortUrls?orderBy[longUrl]=DESC`. Instead, use the following notation `/shortUrls?orderBy=longUrl-DESC`.
|
||||
* The `GET /rest/v2/short-urls` endpoint now has a default ordering of newest-to-oldest. Use `/shortUrls?orderBy=dateCreated-ASC` in order to keep the oldest-to-newest behavior.
|
||||
* Requests expecting a body no longer support url-encoded payloads. Instead, always use JSON bodies with `Content-Type: application/json`.
|
||||
* The next endpoints have been removed:
|
||||
* `PUT /rest/v2/short-urls/{shortCode}/tags`: Use the `PATCH /rest/v2/short-urls/{shortCode}` endpoint to set the short URL tags.
|
||||
* `POST /rest/v2/tags`: Use `POST /rest/v2/short-urls` or `PATCH /rest/v2/short-urls/{shortCodes}` to create new tags already attached to a short URL. Creating orphan tags makes no sense.
|
||||
|
||||
### Changes in CLI
|
||||
|
||||
* The next commands have been removed:
|
||||
* `short-url:generate`: Use `short-url:create` instead.
|
||||
* `tag:create`: Creating orphan tags makes no sense.
|
||||
* Params in camelCase format are no longer supported. They all have an equivalent kebab-case replacement. (for example, from `--startDate` to `--start-date`).
|
||||
* The `short-url:create` command no longer accepts the `--no-validate-url` flag. Now URLs are never validated, unless `--validate-url` is passed.
|
||||
* The CLI installer tool entry-points have changed.
|
||||
* `bin/install`: replaced by `vendor/bin/shlink-installer install`
|
||||
* `bin/update`: replaced by `vendor/bin/shlink-installer update`
|
||||
* `bin/set-option`: replaced by `vendor/bin/shlink-installer set-option`
|
||||
|
||||
### Changes in config
|
||||
|
||||
* The next env vars have been removed:
|
||||
* `INVALID_SHORT_URL_REDIRECT_TO`: Replaced by `DEFAULT_INVALID_SHORT_URL_REDIRECT`.
|
||||
* `REGULAR_404_REDIRECT_TO`: Replaced by `DEFAULT_REGULAR_404_REDIRECT`.
|
||||
* `BASE_URL_REDIRECT_TO`: Replaced by `DEFAULT_BASE_URL_REDIRECT`.
|
||||
* `SHORT_DOMAIN_HOST`: Replaced by `DEFAULT_DOMAIN`.
|
||||
* `SHORT_DOMAIN_SCHEMA`: Replaced by `IS_HTTPS_ENABLED`.
|
||||
* `USE_HTTPS`: Replaced by `IS_HTTPS_ENABLED`.
|
||||
* `VALIDATE_URLS`: There's no replacement. URLs are not validated, unless explicitly requested during creation or edition.
|
||||
* The next env vars behavior has changed:
|
||||
* `DELETE_SHORT_URL_THRESHOLD`: Now, if this env var is not provided, the "visits threshold" won't be checked at all when deleting short URLs. Make sure you explicitly provide a value if you want to enable this feature.
|
||||
* Environment variables now have precedence over configuration set via the installer tool.
|
||||
|
||||
### Other changes
|
||||
|
||||
* A default GeoLite2 license key is no longer provided. If you don't provide your own as explained in [the docs](https://shlink.io/documentation/geolite-license-key/), Shlink will not try to update the file anymore.
|
||||
* The docker image no longer accepts providing configuration via json files mounted in the `config/params` folder. Only env vars are supported now.
|
||||
* If you were manually serving Shlink with swoole, the entry script has to be changed from `/path/to/shlink/vendor/bin/mezzio-swoole start` to `/path/to/shlink/vendor/bin/laminas mezzio:swoole:start`
|
||||
* The `GET /{shortCode}/qr-code/{size}` url has been removed. Use `GET /{shortCode}/qr-code?size={size}` instead.
|
||||
* Regular swoole extension is no longer supported. Use openswoole instead, as a direct replacement. In most of the cases you just need to uninstall one and install the other, the rest is transparent.
|
||||
|
||||
## From v1.x to v2.x
|
||||
|
||||
### PHP 7.4 required
|
||||
@@ -28,7 +76,7 @@ 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.
|
||||
When using the old ones, a 404 status will be returned now.
|
||||
|
||||
### Removed command and route aliases
|
||||
|
||||
|
||||
@@ -1,51 +0,0 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @deprecated To be removed with Shlink 3.0.0
|
||||
* This script is provided to keep backwards compatibility on how to run shlink with swoole while being still able to
|
||||
* update to mezzio/mezzio-swoole 3.x
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mezzio\Swoole\Command;
|
||||
|
||||
use Laminas\ServiceManager\ServiceManager;
|
||||
use PackageVersions\Versions;
|
||||
use Symfony\Component\Console\Application as CommandLine;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\CommandLoader\ContainerCommandLoader;
|
||||
|
||||
use function explode;
|
||||
use function Functional\filter;
|
||||
use function str_starts_with;
|
||||
use function strstr;
|
||||
|
||||
/** @var ServiceManager $container */
|
||||
$container = require __DIR__ . '/../../config/container.php';
|
||||
$version = strstr(Versions::getVersion('mezzio/mezzio-swoole'), '@', true);
|
||||
$commandsPrefix = 'mezzio:swoole:';
|
||||
$commands = filter(
|
||||
$container->get('config')['laminas-cli']['commands'] ?? [],
|
||||
fn ($c, string $command) => str_starts_with($command, $commandsPrefix),
|
||||
);
|
||||
$registeredCommands = [];
|
||||
|
||||
foreach ($commands as $newName => $commandServiceName) {
|
||||
[, $oldName] = explode($commandsPrefix, $newName);
|
||||
$registeredCommands[$oldName] = $commandServiceName;
|
||||
|
||||
$container->addDelegator($commandServiceName, static function ($c, $n, callable $factory) use ($oldName) {
|
||||
/** @var Command $command */
|
||||
$command = $factory();
|
||||
$command->setAliases([$oldName]);
|
||||
|
||||
return $command;
|
||||
});
|
||||
}
|
||||
|
||||
$commandLine = new CommandLine('Mezzio web server', $version);
|
||||
$commandLine->setAutoExit(true);
|
||||
$commandLine->setCommandLoader(new ContainerCommandLoader($container, $registeredCommands));
|
||||
$commandLine->run();
|
||||
12
bin/install
12
bin/install
@@ -1,12 +0,0 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Shlinkio\Shlink;
|
||||
|
||||
use function chdir;
|
||||
use function dirname;
|
||||
|
||||
chdir(dirname(__DIR__));
|
||||
[$install] = require __DIR__ . '/../vendor/shlinkio/shlink-installer/bin/run.php';
|
||||
$install();
|
||||
@@ -1,14 +0,0 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Shlinkio\Shlink;
|
||||
|
||||
use Shlinkio\Shlink\Installer\Command\SetOptionCommand;
|
||||
|
||||
use function chdir;
|
||||
use function dirname;
|
||||
|
||||
chdir(dirname(__DIR__));
|
||||
[,, $installer] = require __DIR__ . '/../vendor/shlinkio/shlink-installer/bin/run.php';
|
||||
$installer(SetOptionCommand::NAME);
|
||||
@@ -4,7 +4,10 @@ export DB_DRIVER=postgres
|
||||
export TEST_ENV=api
|
||||
export GENERATE_COVERAGE=${GENERATE_COVERAGE:-"no"}
|
||||
|
||||
# Reset logs
|
||||
rm -rf data/log/api-tests
|
||||
mkdir data/log/api-tests
|
||||
touch data/log/api-tests/output.log
|
||||
|
||||
# Try to stop server just in case it hanged in last execution
|
||||
vendor/bin/laminas mezzio:swoole:stop
|
||||
|
||||
12
bin/update
12
bin/update
@@ -1,12 +0,0 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Shlinkio\Shlink;
|
||||
|
||||
use function chdir;
|
||||
use function dirname;
|
||||
|
||||
chdir(dirname(__DIR__));
|
||||
[, $update] = require __DIR__ . '/../vendor/shlinkio/shlink-installer/bin/run.php';
|
||||
$update();
|
||||
7
build.sh
7
build.sh
@@ -10,7 +10,7 @@ fi
|
||||
version=$1
|
||||
noSwoole=$2
|
||||
phpVersion=$(php -r 'echo PHP_MAJOR_VERSION . "." . PHP_MINOR_VERSION;')
|
||||
[[ $noSwoole ]] && swooleSuffix="" || swooleSuffix="_swoole"
|
||||
[[ $noSwoole ]] && swooleSuffix="" || swooleSuffix="_openswoole"
|
||||
distId="shlink${version}_php${phpVersion}${swooleSuffix}_dist"
|
||||
builtContent="./build/${distId}"
|
||||
projectdir=$(pwd)
|
||||
@@ -34,11 +34,8 @@ ${composerBin} self-update
|
||||
${composerBin} install --no-dev --prefer-dist $composerFlags
|
||||
|
||||
if [[ $noSwoole ]]; then
|
||||
# If generating a dist not for swoole, uninstall mezzio-swoole
|
||||
# If generating a dist not for openswoole, uninstall mezzio-swoole
|
||||
${composerBin} remove mezzio/mezzio-swoole --with-all-dependencies --update-no-dev $composerFlags
|
||||
else
|
||||
# Copy mezzio helper script to vendor (deprecated - Remove with Shlink 3.0.0)
|
||||
cp "${projectdir}/bin/helper/mezzio-swoole" "./vendor/bin"
|
||||
fi
|
||||
|
||||
# Delete development files
|
||||
|
||||
@@ -12,71 +12,65 @@
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": "^8.0",
|
||||
"php": "^8.1",
|
||||
"ext-json": "*",
|
||||
"ext-pdo": "*",
|
||||
"akrabat/ip-address-middleware": "^2.1",
|
||||
"cakephp/chronos": "^2.3",
|
||||
"cocur/slugify": "^4.0",
|
||||
"doctrine/migrations": "^3.3",
|
||||
"doctrine/orm": "^2.10",
|
||||
"doctrine/migrations": "^3.5",
|
||||
"doctrine/orm": "^2.12",
|
||||
"endroid/qr-code": "^4.4",
|
||||
"geoip2/geoip2": "^2.12",
|
||||
"guzzlehttp/guzzle": "^7.4",
|
||||
"happyr/doctrine-specification": "^2.0",
|
||||
"jaybizzle/crawler-detect": "^1.2.110",
|
||||
"laminas/laminas-config": "^3.7",
|
||||
"laminas/laminas-config-aggregator": "^1.7",
|
||||
"laminas/laminas-diactoros": "^2.8",
|
||||
"laminas/laminas-inputfilter": "^2.13",
|
||||
"laminas/laminas-servicemanager": "^3.10",
|
||||
"laminas/laminas-stdlib": "^3.6",
|
||||
"laminas/laminas-config-aggregator": "^1.8",
|
||||
"laminas/laminas-diactoros": "^2.14",
|
||||
"laminas/laminas-inputfilter": "^2.19",
|
||||
"laminas/laminas-servicemanager": "^3.16",
|
||||
"laminas/laminas-stdlib": "^3.11",
|
||||
"lcobucci/jwt": "^4.1",
|
||||
"league/uri": "^6.4",
|
||||
"league/uri": "^6.7",
|
||||
"lstrojny/functional-php": "^1.17",
|
||||
"mezzio/mezzio": "^3.7",
|
||||
"mezzio/mezzio-fastroute": "^3.3",
|
||||
"mezzio/mezzio-problem-details": "^1.5",
|
||||
"mezzio/mezzio-swoole": "^3.5",
|
||||
"mlocati/ip-lib": "^1.17",
|
||||
"monolog/monolog": "^2.3",
|
||||
"nikolaposa/monolog-factory": "^3.1",
|
||||
"ocramius/proxy-manager": "^2.11",
|
||||
"pagerfanta/core": "^3.5",
|
||||
"php-amqplib/php-amqplib": "^3.1",
|
||||
"mezzio/mezzio": "^3.11",
|
||||
"mezzio/mezzio-fastroute": "^3.5",
|
||||
"mezzio/mezzio-problem-details": "^1.6",
|
||||
"mezzio/mezzio-swoole": "^4.3",
|
||||
"mlocati/ip-lib": "^1.18",
|
||||
"ocramius/proxy-manager": "^2.14",
|
||||
"pagerfanta/core": "^3.6",
|
||||
"php-middleware/request-id": "^4.1",
|
||||
"predis/predis": "^1.1",
|
||||
"pugx/shortid-php": "^1.0",
|
||||
"ramsey/uuid": "^4.2",
|
||||
"shlinkio/shlink-common": "^4.4",
|
||||
"shlinkio/shlink-config": "^1.4",
|
||||
"shlinkio/shlink-event-dispatcher": "^2.3",
|
||||
"shlinkio/shlink-importer": "^2.5",
|
||||
"shlinkio/shlink-installer": "^6.3",
|
||||
"ramsey/uuid": "^4.3",
|
||||
"shlinkio/shlink-common": "^4.5",
|
||||
"shlinkio/shlink-config": "^1.6",
|
||||
"shlinkio/shlink-event-dispatcher": "^2.4",
|
||||
"shlinkio/shlink-importer": "^3.0",
|
||||
"shlinkio/shlink-installer": "^8.0",
|
||||
"shlinkio/shlink-ip-geolocation": "^2.2",
|
||||
"symfony/console": "^5.4",
|
||||
"symfony/filesystem": "^6.0 || ^5.4",
|
||||
"symfony/lock": "^6.0 || ^5.4",
|
||||
"symfony/mercure": "^0.6",
|
||||
"symfony/process": "^6.0 || ^5.4",
|
||||
"symfony/string": "^6.0 || ^5.4"
|
||||
"symfony/console": "^6.1",
|
||||
"symfony/filesystem": "^6.1",
|
||||
"symfony/lock": "^6.1",
|
||||
"symfony/process": "^6.1",
|
||||
"symfony/string": "^6.1"
|
||||
},
|
||||
"require-dev": {
|
||||
"cebe/php-openapi": "^1.5",
|
||||
"cebe/php-openapi": "^1.7",
|
||||
"devster/ubench": "^2.1",
|
||||
"dms/phpunit-arraysubset-asserts": "^0.3.0",
|
||||
"eaglewu/swoole-ide-helper": "dev-master",
|
||||
"infection/infection": "^0.25.4",
|
||||
"infection/infection": "^0.26.5",
|
||||
"openswoole/ide-helper": "~4.11.1",
|
||||
"phpspec/prophecy-phpunit": "^2.0",
|
||||
"phpstan/phpstan": "^1.2",
|
||||
"phpstan/phpstan-doctrine": "^1.0",
|
||||
"phpstan/phpstan-symfony": "^1.0",
|
||||
"phpstan/phpstan": "^1.8",
|
||||
"phpstan/phpstan-doctrine": "^1.3",
|
||||
"phpstan/phpstan-symfony": "^1.2",
|
||||
"phpunit/php-code-coverage": "^9.2",
|
||||
"phpunit/phpunit": "^9.5",
|
||||
"roave/security-advisories": "dev-master",
|
||||
"shlinkio/php-coding-standard": "~2.2.0",
|
||||
"shlinkio/shlink-test-utils": "^2.5",
|
||||
"symfony/var-dumper": "^6.0",
|
||||
"shlinkio/php-coding-standard": "~2.3.0",
|
||||
"shlinkio/shlink-test-utils": "^3.0.1",
|
||||
"symfony/var-dumper": "^6.1",
|
||||
"veewee/composer-run-parallel": "^1.1"
|
||||
},
|
||||
"autoload": {
|
||||
@@ -95,10 +89,8 @@
|
||||
"ShlinkioTest\\Shlink\\CLI\\": "module/CLI/test",
|
||||
"ShlinkioTest\\Shlink\\Rest\\": "module/Rest/test",
|
||||
"ShlinkioApiTest\\Shlink\\Rest\\": "module/Rest/test-api",
|
||||
"ShlinkioTest\\Shlink\\Core\\": [
|
||||
"module/Core/test",
|
||||
"module/Core/test-db"
|
||||
]
|
||||
"ShlinkioTest\\Shlink\\Core\\": "module/Core/test",
|
||||
"ShlinkioDbTest\\Shlink\\Core\\": "module/Core/test-db"
|
||||
},
|
||||
"files": [
|
||||
"config/test/constants.php"
|
||||
@@ -131,7 +123,7 @@
|
||||
],
|
||||
"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:unit:pretty": "@php vendor/bin/phpunit --order-by=random --colors=always --coverage-html build/coverage-unit/coverage-html",
|
||||
"test:unit:pretty": "@test:unit --coverage-html build/coverage-unit/coverage-html",
|
||||
"test:db": "@parallel test:db:sqlite:ci test:db:mysql test:db:maria test:db:postgres test:db:ms",
|
||||
"test:db:sqlite": "APP_ENV=test php vendor/bin/phpunit --order-by=random --colors=always --testdox -c phpunit-db.xml",
|
||||
"test:db:sqlite:ci": "@test:db:sqlite --coverage-php build/coverage-db.cov --coverage-xml=build/coverage-db/coverage-xml --log-junit=build/coverage-db/junit.xml",
|
||||
@@ -142,7 +134,7 @@
|
||||
"test:api": "bin/test/run-api-tests.sh",
|
||||
"test:api:ci": "GENERATE_COVERAGE=yes composer test:api",
|
||||
"infect:ci:base": "infection --threads=4 --log-verbosity=default --only-covered --only-covering-test-cases --skip-initial-tests",
|
||||
"infect:ci:unit": "@infect:ci:base --coverage=build/coverage-unit --min-msi=80",
|
||||
"infect:ci:unit": "@infect:ci:base --coverage=build/coverage-unit --min-msi=84",
|
||||
"infect:ci:db": "@infect:ci:base --coverage=build/coverage-db --min-msi=95 --configuration=infection-db.json",
|
||||
"infect:ci:api": "@infect:ci:base --coverage=build/coverage-api --min-msi=80 --configuration=infection-api.json",
|
||||
"infect:ci": "@parallel infect:ci:unit infect:ci:db infect:ci:api",
|
||||
@@ -179,7 +171,7 @@
|
||||
"test:db:mysql": "<fg=blue;options=bold>Runs database test suites on a MySQL database</>",
|
||||
"test:db:maria": "<fg=blue;options=bold>Runs database test suites on a MariaDB database</>",
|
||||
"test:db:postgres": "<fg=blue;options=bold>Runs database test suites on a PostgreSQL database</>",
|
||||
"test:db:ms": "<fg=blue;options=bold>Runs database test suites on a Miscrosoft SQL Server database</>",
|
||||
"test:db:ms": "<fg=blue;options=bold>Runs database test suites on a Microsoft SQL Server database</>",
|
||||
"test:api": "<fg=blue;options=bold>Runs API test suites</>",
|
||||
"test:api:ci": "<fg=blue;options=bold>Runs API test suites, and generates code coverage reports</>",
|
||||
"infect:ci": "<fg=blue;options=bold>Checks unit and db tests quality applying mutation testing with existing reports and logs</>",
|
||||
|
||||
11
config/autoload/app_options.local.php.dist
Normal file
11
config/autoload/app_options.local.php.dist
Normal file
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
return [
|
||||
|
||||
'app_options' => [
|
||||
'version' => 'latest',
|
||||
],
|
||||
|
||||
];
|
||||
@@ -8,8 +8,8 @@ return [
|
||||
|
||||
'debug' => false,
|
||||
|
||||
// Disabling config cache for cli, ensures it's never used for swoole and also that console commands don't generate
|
||||
// a cache file that's then used by non-swoole web executions
|
||||
// Disabling config cache for cli, ensures it's never used for openswoole and also that console commands don't
|
||||
// generate a cache file that's then used by non-openswoole web executions
|
||||
ConfigAggregator::ENABLE_CACHE => PHP_SAPI !== 'cli',
|
||||
|
||||
];
|
||||
|
||||
@@ -4,15 +4,17 @@ declare(strict_types=1);
|
||||
|
||||
namespace Shlinkio\Shlink;
|
||||
|
||||
use function Shlinkio\Shlink\Common\env;
|
||||
use Shlinkio\Shlink\Core\Config\EnvVars;
|
||||
|
||||
use const Shlinkio\Shlink\DEFAULT_DELETE_SHORT_URL_THRESHOLD;
|
||||
return (static function (): array {
|
||||
$threshold = EnvVars::DELETE_SHORT_URL_THRESHOLD->loadFromEnv();
|
||||
|
||||
return [
|
||||
return [
|
||||
|
||||
'delete_short_urls' => [
|
||||
'check_visits_threshold' => true,
|
||||
'visits_threshold' => (int) env('DELETE_SHORT_URL_THRESHOLD', DEFAULT_DELETE_SHORT_URL_THRESHOLD),
|
||||
],
|
||||
'delete_short_urls' => [
|
||||
'check_visits_threshold' => $threshold !== null,
|
||||
'visits_threshold' => (int) ($threshold ?? DEFAULT_DELETE_SHORT_URL_THRESHOLD),
|
||||
],
|
||||
|
||||
];
|
||||
];
|
||||
})();
|
||||
|
||||
@@ -3,12 +3,12 @@
|
||||
declare(strict_types=1);
|
||||
|
||||
use Happyr\DoctrineSpecification\Repository\EntitySpecificationRepository;
|
||||
use Shlinkio\Shlink\Core\Config\EnvVars;
|
||||
|
||||
use function Functional\contains;
|
||||
use function Shlinkio\Shlink\Common\env;
|
||||
|
||||
return (static function (): array {
|
||||
$driver = env('DB_DRIVER');
|
||||
$driver = EnvVars::DB_DRIVER->loadFromEnv();
|
||||
$isMysqlCompatible = contains(['maria', 'mysql'], $driver);
|
||||
|
||||
$resolveDriver = static fn () => match ($driver) {
|
||||
@@ -21,20 +21,27 @@ return (static function (): array {
|
||||
'mssql' => '1433',
|
||||
default => '3306',
|
||||
};
|
||||
$resolveConnection = static fn () => match (true) {
|
||||
$driver === null || $driver === 'sqlite' => [
|
||||
$resolveCharset = static fn () => match ($driver) {
|
||||
// This does not determine charsets or collations in tables or columns, but the charset used in the data
|
||||
// flowing in the connection, so it has to match what has been set in the database.
|
||||
'maria', 'mysql' => 'utf8mb4',
|
||||
'postgres' => 'utf8',
|
||||
default => null,
|
||||
};
|
||||
$resolveConnection = static fn () => match ($driver) {
|
||||
null, 'sqlite' => [
|
||||
'driver' => 'pdo_sqlite',
|
||||
'path' => 'data/database.sqlite',
|
||||
],
|
||||
default => [
|
||||
'driver' => $resolveDriver(),
|
||||
'dbname' => env('DB_NAME', 'shlink'),
|
||||
'user' => env('DB_USER'),
|
||||
'password' => env('DB_PASSWORD'),
|
||||
'host' => env('DB_HOST', $driver === 'postgres' ? env('DB_UNIX_SOCKET') : null),
|
||||
'port' => env('DB_PORT', $resolveDefaultPort()),
|
||||
'unix_socket' => $isMysqlCompatible ? env('DB_UNIX_SOCKET') : null,
|
||||
'charset' => 'utf8',
|
||||
'dbname' => EnvVars::DB_NAME->loadFromEnv('shlink'),
|
||||
'user' => EnvVars::DB_USER->loadFromEnv(),
|
||||
'password' => EnvVars::DB_PASSWORD->loadFromEnv(),
|
||||
'host' => EnvVars::DB_HOST->loadFromEnv(EnvVars::DB_UNIX_SOCKET->loadFromEnv()),
|
||||
'port' => EnvVars::DB_PORT->loadFromEnv($resolveDefaultPort()),
|
||||
'unix_socket' => $isMysqlCompatible ? EnvVars::DB_UNIX_SOCKET->loadFromEnv() : null,
|
||||
'charset' => $resolveCharset(),
|
||||
],
|
||||
};
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ return [
|
||||
'driver' => 'pdo_mysql',
|
||||
'host' => 'shlink_db_mysql',
|
||||
'dbname' => 'shlink',
|
||||
'charset' => 'utf8mb4',
|
||||
],
|
||||
],
|
||||
|
||||
|
||||
@@ -2,14 +2,14 @@
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use function Shlinkio\Shlink\Common\env;
|
||||
use Shlinkio\Shlink\Core\Config\EnvVars;
|
||||
|
||||
return [
|
||||
|
||||
'geolite2' => [
|
||||
'db_location' => __DIR__ . '/../../data/GeoLite2-City.mmdb',
|
||||
'temp_dir' => __DIR__ . '/../../data',
|
||||
'license_key' => env('GEOLITE_LICENSE_KEY', 'G4Lm0C60yJsnkdPi'), // Deprecated. Remove hardcoded license on v3
|
||||
'license_key' => EnvVars::GEOLITE_LICENSE_KEY->loadFromEnv(),
|
||||
],
|
||||
|
||||
];
|
||||
|
||||
@@ -18,22 +18,21 @@ return [
|
||||
Option\Database\DatabaseUserConfigOption::class,
|
||||
Option\Database\DatabasePasswordConfigOption::class,
|
||||
Option\Database\DatabaseUnixSocketConfigOption::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\Visit\OrphanVisitsWebhooksConfigOption::class,
|
||||
Option\Redirect\BaseUrlRedirectConfigOption::class,
|
||||
Option\Redirect\InvalidShortUrlRedirectConfigOption::class,
|
||||
Option\Redirect\Regular404RedirectConfigOption::class,
|
||||
Option\Visit\CheckVisitsThresholdConfigOption::class,
|
||||
Option\Visit\VisitsThresholdConfigOption::class,
|
||||
Option\BasePathConfigOption::class,
|
||||
Option\TimezoneConfigOption::class,
|
||||
Option\Worker\TaskWorkerNumConfigOption::class,
|
||||
Option\Worker\WebWorkerNumConfigOption::class,
|
||||
Option\RedisServersConfigOption::class,
|
||||
Option\Redis\RedisServersConfigOption::class,
|
||||
Option\Redis\RedisSentinelServiceConfigOption::class,
|
||||
Option\Redis\RedisPubSubConfigOption::class,
|
||||
Option\UrlShortener\ShortCodeLengthOption::class,
|
||||
Option\Mercure\EnableMercureConfigOption::class,
|
||||
Option\Mercure\MercurePublicUrlConfigOption::class,
|
||||
@@ -44,6 +43,7 @@ return [
|
||||
Option\UrlShortener\RedirectCacheLifeTimeConfigOption::class,
|
||||
Option\UrlShortener\AutoResolveTitlesConfigOption::class,
|
||||
Option\UrlShortener\AppendExtraPathConfigOption::class,
|
||||
Option\UrlShortener\EnableMultiSegmentSlugsConfigOption::class,
|
||||
Option\Tracking\IpAnonymizationConfigOption::class,
|
||||
Option\Tracking\OrphanVisitsTrackingConfigOption::class,
|
||||
Option\Tracking\DisableTrackParamConfigOption::class,
|
||||
@@ -66,13 +66,13 @@ return [
|
||||
],
|
||||
|
||||
'installation_commands' => [
|
||||
InstallationCommand::DB_CREATE_SCHEMA => [
|
||||
InstallationCommand::DB_CREATE_SCHEMA->value => [
|
||||
'command' => 'bin/cli ' . Command\Db\CreateDatabaseCommand::NAME,
|
||||
],
|
||||
InstallationCommand::DB_MIGRATE => [
|
||||
InstallationCommand::DB_MIGRATE->value => [
|
||||
'command' => 'bin/cli ' . Command\Db\MigrateDatabaseCommand::NAME,
|
||||
],
|
||||
InstallationCommand::GEOLITE_DOWNLOAD_DB => [
|
||||
InstallationCommand::GEOLITE_DOWNLOAD_DB->value => [
|
||||
'command' => 'bin/cli ' . Command\Visit\DownloadGeoLiteDbCommand::NAME,
|
||||
],
|
||||
],
|
||||
|
||||
@@ -3,12 +3,11 @@
|
||||
declare(strict_types=1);
|
||||
|
||||
use Laminas\ServiceManager\AbstractFactory\ConfigAbstractFactory;
|
||||
use Predis\ClientInterface as PredisClient;
|
||||
use Shlinkio\Shlink\Common\Cache\RedisFactory;
|
||||
use Shlinkio\Shlink\Common\Logger\LoggerAwareDelegatorFactory;
|
||||
use Shlinkio\Shlink\Core\Config\EnvVars;
|
||||
use Symfony\Component\Lock;
|
||||
|
||||
use function Shlinkio\Shlink\Common\env;
|
||||
|
||||
use const Shlinkio\Shlink\LOCAL_LOCK_FACTORY;
|
||||
|
||||
return [
|
||||
@@ -25,7 +24,7 @@ return [
|
||||
LOCAL_LOCK_FACTORY => ConfigAbstractFactory::class,
|
||||
],
|
||||
'aliases' => [
|
||||
'lock_store' => env('REDIS_SERVERS') === null ? 'local_lock_store' : 'redis_lock_store',
|
||||
'lock_store' => EnvVars::REDIS_SERVERS->existsInEnv() ? 'redis_lock_store' : 'local_lock_store',
|
||||
|
||||
'redis_lock_store' => Lock\Store\RedisStore::class,
|
||||
'local_lock_store' => Lock\Store\FlockStore::class,
|
||||
@@ -39,7 +38,7 @@ return [
|
||||
|
||||
ConfigAbstractFactory::class => [
|
||||
Lock\Store\FlockStore::class => ['config.locks.locks_dir'],
|
||||
Lock\Store\RedisStore::class => [PredisClient::class],
|
||||
Lock\Store\RedisStore::class => [RedisFactory::SERVICE_NAME],
|
||||
Lock\LockFactory::class => ['lock_store'],
|
||||
LOCAL_LOCK_FACTORY => ['local_lock_store'],
|
||||
],
|
||||
|
||||
@@ -4,72 +4,36 @@ declare(strict_types=1);
|
||||
|
||||
namespace Shlinkio\Shlink;
|
||||
|
||||
use Monolog\Formatter;
|
||||
use Monolog\Handler;
|
||||
use Monolog\Level;
|
||||
use Monolog\Logger;
|
||||
use Monolog\Processor;
|
||||
use MonologFactory\DiContainerLoggerFactory;
|
||||
use PhpMiddleware\RequestId;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Shlinkio\Shlink\Common\Logger\LoggerFactory;
|
||||
use Shlinkio\Shlink\Common\Logger\LoggerType;
|
||||
|
||||
use const PHP_EOL;
|
||||
|
||||
$processors = [
|
||||
'exception_with_new_line' => [
|
||||
'name' => Common\Logger\Processor\ExceptionWithNewLineProcessor::class,
|
||||
],
|
||||
'psr3' => [
|
||||
'name' => Processor\PsrLogMessageProcessor::class,
|
||||
],
|
||||
'request_id' => RequestId\MonologProcessor::class,
|
||||
];
|
||||
$formatter = [
|
||||
'name' => Formatter\LineFormatter::class,
|
||||
'params' => [
|
||||
'format' => '[%datetime%] [%extra.request_id%] %channel%.%level_name% - %message%' . PHP_EOL,
|
||||
'allow_inline_line_breaks' => true,
|
||||
],
|
||||
$common = [
|
||||
'level' => Level::Info->value,
|
||||
'processors' => [RequestId\MonologProcessor::class],
|
||||
'line_format' => '[%datetime%] [%extra.request_id%] %channel%.%level_name% - %message%',
|
||||
];
|
||||
|
||||
return [
|
||||
|
||||
'logger' => [
|
||||
'Shlink' => [
|
||||
'name' => 'Shlink',
|
||||
'handlers' => [
|
||||
'shlink_handler' => [
|
||||
'name' => Handler\RotatingFileHandler::class,
|
||||
'params' => [
|
||||
'level' => Logger::INFO,
|
||||
'filename' => 'data/log/shlink_log.log',
|
||||
'max_files' => 30,
|
||||
'file_permission' => 0666,
|
||||
],
|
||||
'formatter' => $formatter,
|
||||
],
|
||||
],
|
||||
'processors' => $processors,
|
||||
'type' => LoggerType::FILE->value,
|
||||
...$common,
|
||||
],
|
||||
'Access' => [
|
||||
'name' => 'Access',
|
||||
'handlers' => [
|
||||
'access_handler' => [
|
||||
'name' => Handler\StreamHandler::class,
|
||||
'params' => [
|
||||
'level' => Logger::INFO,
|
||||
'stream' => 'php://stdout',
|
||||
],
|
||||
'formatter' => $formatter,
|
||||
],
|
||||
],
|
||||
'processors' => $processors,
|
||||
'type' => LoggerType::STREAM->value,
|
||||
...$common,
|
||||
],
|
||||
],
|
||||
|
||||
'dependencies' => [
|
||||
'factories' => [
|
||||
'Logger_Shlink' => [DiContainerLoggerFactory::class, 'Shlink'],
|
||||
'Logger_Access' => [DiContainerLoggerFactory::class, 'Access'],
|
||||
'Logger_Shlink' => [LoggerFactory::class, 'Shlink'],
|
||||
'Logger_Access' => [LoggerFactory::class, 'Access'],
|
||||
],
|
||||
'aliases' => [
|
||||
'logger' => 'Logger_Shlink',
|
||||
|
||||
@@ -2,33 +2,18 @@
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Monolog\Handler\StreamHandler;
|
||||
use Monolog\Logger;
|
||||
use Monolog\Level;
|
||||
use Shlinkio\Shlink\Common\Logger\LoggerType;
|
||||
|
||||
$isSwoole = extension_loaded('openswoole');
|
||||
|
||||
// For swoole, send logs to standard output
|
||||
$handler = $isSwoole
|
||||
? [
|
||||
'name' => StreamHandler::class,
|
||||
'params' => [
|
||||
'level' => Logger::DEBUG,
|
||||
'stream' => 'php://stdout',
|
||||
],
|
||||
]
|
||||
: [
|
||||
'params' => [
|
||||
'level' => Logger::DEBUG,
|
||||
],
|
||||
];
|
||||
|
||||
return [
|
||||
|
||||
'logger' => [
|
||||
'Shlink' => [
|
||||
'handlers' => [
|
||||
'shlink_handler' => $handler,
|
||||
],
|
||||
// For swoole, send logs as stream
|
||||
'type' => $isSwoole ? LoggerType::STREAM->value : LoggerType::FILE->value,
|
||||
'level' => Level::Debug->value,
|
||||
],
|
||||
],
|
||||
|
||||
|
||||
@@ -4,20 +4,19 @@ declare(strict_types=1);
|
||||
|
||||
use Laminas\ServiceManager\Proxy\LazyServiceFactory;
|
||||
use Shlinkio\Shlink\Common\Mercure\LcobucciJwtProvider;
|
||||
use Shlinkio\Shlink\Core\Config\EnvVars;
|
||||
use Symfony\Component\Mercure\Hub;
|
||||
use Symfony\Component\Mercure\HubInterface;
|
||||
|
||||
use function Shlinkio\Shlink\Common\env;
|
||||
|
||||
return (static function (): array {
|
||||
$publicUrl = env('MERCURE_PUBLIC_HUB_URL');
|
||||
$publicUrl = EnvVars::MERCURE_PUBLIC_HUB_URL->loadFromEnv();
|
||||
|
||||
return [
|
||||
|
||||
'mercure' => [
|
||||
'public_hub_url' => $publicUrl,
|
||||
'internal_hub_url' => env('MERCURE_INTERNAL_HUB_URL', $publicUrl),
|
||||
'jwt_secret' => env('MERCURE_JWT_SECRET'),
|
||||
'internal_hub_url' => EnvVars::MERCURE_INTERNAL_HUB_URL->loadFromEnv($publicUrl),
|
||||
'jwt_secret' => EnvVars::MERCURE_JWT_SECRET->loadFromEnv(),
|
||||
'jwt_issuer' => 'Shlink',
|
||||
],
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use function Shlinkio\Shlink\Common\env;
|
||||
use Shlinkio\Shlink\Core\Config\EnvVars;
|
||||
|
||||
use const Shlinkio\Shlink\DEFAULT_QR_CODE_ERROR_CORRECTION;
|
||||
use const Shlinkio\Shlink\DEFAULT_QR_CODE_FORMAT;
|
||||
@@ -13,11 +13,15 @@ use const Shlinkio\Shlink\DEFAULT_QR_CODE_SIZE;
|
||||
return [
|
||||
|
||||
'qr_codes' => [
|
||||
'size' => (int) env('DEFAULT_QR_CODE_SIZE', DEFAULT_QR_CODE_SIZE),
|
||||
'margin' => (int) env('DEFAULT_QR_CODE_MARGIN', DEFAULT_QR_CODE_MARGIN),
|
||||
'format' => env('DEFAULT_QR_CODE_FORMAT', DEFAULT_QR_CODE_FORMAT),
|
||||
'error_correction' => env('DEFAULT_QR_CODE_ERROR_CORRECTION', DEFAULT_QR_CODE_ERROR_CORRECTION),
|
||||
'round_block_size' => (bool) env('DEFAULT_QR_CODE_ROUND_BLOCK_SIZE', DEFAULT_QR_CODE_ROUND_BLOCK_SIZE),
|
||||
'size' => (int) EnvVars::DEFAULT_QR_CODE_SIZE->loadFromEnv(DEFAULT_QR_CODE_SIZE),
|
||||
'margin' => (int) EnvVars::DEFAULT_QR_CODE_MARGIN->loadFromEnv(DEFAULT_QR_CODE_MARGIN),
|
||||
'format' => EnvVars::DEFAULT_QR_CODE_FORMAT->loadFromEnv(DEFAULT_QR_CODE_FORMAT),
|
||||
'error_correction' => EnvVars::DEFAULT_QR_CODE_ERROR_CORRECTION->loadFromEnv(
|
||||
DEFAULT_QR_CODE_ERROR_CORRECTION,
|
||||
),
|
||||
'round_block_size' => (bool) EnvVars::DEFAULT_QR_CODE_ROUND_BLOCK_SIZE->loadFromEnv(
|
||||
DEFAULT_QR_CODE_ROUND_BLOCK_SIZE,
|
||||
),
|
||||
],
|
||||
|
||||
];
|
||||
|
||||
@@ -2,47 +2,20 @@
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Laminas\ServiceManager\AbstractFactory\ConfigAbstractFactory;
|
||||
use Laminas\ServiceManager\Proxy\LazyServiceFactory;
|
||||
use PhpAmqpLib\Connection\AMQPStreamConnection;
|
||||
|
||||
use function Shlinkio\Shlink\Common\env;
|
||||
use Shlinkio\Shlink\Core\Config\EnvVars;
|
||||
|
||||
return [
|
||||
|
||||
'rabbitmq' => [
|
||||
'enabled' => (bool) env('RABBITMQ_ENABLED', false),
|
||||
'host' => env('RABBITMQ_HOST'),
|
||||
'port' => (int) env('RABBITMQ_PORT', '5672'),
|
||||
'user' => env('RABBITMQ_USER'),
|
||||
'password' => env('RABBITMQ_PASSWORD'),
|
||||
'vhost' => env('RABBITMQ_VHOST', '/'),
|
||||
],
|
||||
'enabled' => (bool) EnvVars::RABBITMQ_ENABLED->loadFromEnv(false),
|
||||
'host' => EnvVars::RABBITMQ_HOST->loadFromEnv(),
|
||||
'port' => (int) EnvVars::RABBITMQ_PORT->loadFromEnv('5672'),
|
||||
'user' => EnvVars::RABBITMQ_USER->loadFromEnv(),
|
||||
'password' => EnvVars::RABBITMQ_PASSWORD->loadFromEnv(),
|
||||
'vhost' => EnvVars::RABBITMQ_VHOST->loadFromEnv('/'),
|
||||
|
||||
'dependencies' => [
|
||||
'factories' => [
|
||||
AMQPStreamConnection::class => ConfigAbstractFactory::class,
|
||||
],
|
||||
'delegators' => [
|
||||
AMQPStreamConnection::class => [
|
||||
LazyServiceFactory::class,
|
||||
],
|
||||
],
|
||||
'lazy_services' => [
|
||||
'class_map' => [
|
||||
AMQPStreamConnection::class => AMQPStreamConnection::class,
|
||||
],
|
||||
],
|
||||
],
|
||||
|
||||
ConfigAbstractFactory::class => [
|
||||
AMQPStreamConnection::class => [
|
||||
'config.rabbitmq.host',
|
||||
'config.rabbitmq.port',
|
||||
'config.rabbitmq.user',
|
||||
'config.rabbitmq.password',
|
||||
'config.rabbitmq.vhost',
|
||||
],
|
||||
// Deprecated
|
||||
'legacy_visits_publishing' => (bool) EnvVars::RABBITMQ_LEGACY_VISITS_PUBLISHING->loadFromEnv(false),
|
||||
],
|
||||
|
||||
];
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use function Shlinkio\Shlink\Common\env;
|
||||
use Shlinkio\Shlink\Core\Config\EnvVars;
|
||||
|
||||
use const Shlinkio\Shlink\DEFAULT_REDIRECT_CACHE_LIFETIME;
|
||||
use const Shlinkio\Shlink\DEFAULT_REDIRECT_STATUS_CODE;
|
||||
@@ -10,16 +10,16 @@ use const Shlinkio\Shlink\DEFAULT_REDIRECT_STATUS_CODE;
|
||||
return [
|
||||
|
||||
'not_found_redirects' => [
|
||||
// Deprecated env vars
|
||||
'invalid_short_url' => env('DEFAULT_INVALID_SHORT_URL_REDIRECT', env('INVALID_SHORT_URL_REDIRECT_TO')),
|
||||
'regular_404' => env('DEFAULT_REGULAR_404_REDIRECT', env('REGULAR_404_REDIRECT_TO')),
|
||||
'base_url' => env('DEFAULT_BASE_URL_REDIRECT', env('BASE_URL_REDIRECT_TO')),
|
||||
'invalid_short_url' => EnvVars::DEFAULT_INVALID_SHORT_URL_REDIRECT->loadFromEnv(),
|
||||
'regular_404' => EnvVars::DEFAULT_REGULAR_404_REDIRECT->loadFromEnv(),
|
||||
'base_url' => EnvVars::DEFAULT_BASE_URL_REDIRECT->loadFromEnv(),
|
||||
],
|
||||
|
||||
'url_shortener' => [
|
||||
// TODO Move these options to their own config namespace. Maybe "redirects".
|
||||
'redirect_status_code' => (int) env('REDIRECT_STATUS_CODE', DEFAULT_REDIRECT_STATUS_CODE),
|
||||
'redirect_cache_lifetime' => (int) env('REDIRECT_CACHE_LIFETIME', DEFAULT_REDIRECT_CACHE_LIFETIME),
|
||||
'redirects' => [
|
||||
'redirect_status_code' => (int) EnvVars::REDIRECT_STATUS_CODE->loadFromEnv(DEFAULT_REDIRECT_STATUS_CODE),
|
||||
'redirect_cache_lifetime' => (int) EnvVars::REDIRECT_CACHE_LIFETIME->loadFromEnv(
|
||||
DEFAULT_REDIRECT_CACHE_LIFETIME,
|
||||
),
|
||||
],
|
||||
|
||||
];
|
||||
|
||||
@@ -2,20 +2,26 @@
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use function Shlinkio\Shlink\Common\env;
|
||||
use Shlinkio\Shlink\Core\Config\EnvVars;
|
||||
|
||||
return (static function (): array {
|
||||
$redisServers = env('REDIS_SERVERS');
|
||||
$redisServers = EnvVars::REDIS_SERVERS->loadFromEnv();
|
||||
$pubSub = [
|
||||
'redis' => [
|
||||
'pub_sub_enabled' => $redisServers !== null && EnvVars::REDIS_PUB_SUB_ENABLED->loadFromEnv(false),
|
||||
],
|
||||
];
|
||||
|
||||
return match (true) {
|
||||
$redisServers === null => [],
|
||||
return match ($redisServers) {
|
||||
null => $pubSub,
|
||||
default => [
|
||||
'cache' => [
|
||||
'redis' => [
|
||||
'servers' => $redisServers,
|
||||
'sentinel_service' => env('REDIS_SENTINEL_SERVICE'),
|
||||
'sentinel_service' => EnvVars::REDIS_SENTINEL_SERVICE->loadFromEnv(),
|
||||
],
|
||||
],
|
||||
...$pubSub,
|
||||
],
|
||||
};
|
||||
})();
|
||||
|
||||
@@ -7,12 +7,13 @@ return [
|
||||
'cache' => [
|
||||
'redis' => [
|
||||
'servers' => 'tcp://shlink_redis:6379',
|
||||
// 'servers' => [
|
||||
// 'tcp://shlink_redis:6379',
|
||||
// ],
|
||||
],
|
||||
],
|
||||
|
||||
'redis' => [
|
||||
'pub_sub_enabled' => true,
|
||||
],
|
||||
|
||||
'dependencies' => [
|
||||
'aliases' => [
|
||||
// With this config, a user could alias 'lock_store' => 'redis_lock_store' to override the default
|
||||
|
||||
@@ -5,6 +5,7 @@ declare(strict_types=1);
|
||||
use Laminas\ServiceManager\AbstractFactory\ConfigAbstractFactory;
|
||||
use Laminas\ServiceManager\Factory\InvokableFactory;
|
||||
use PhpMiddleware\RequestId;
|
||||
use Shlinkio\Shlink\Common\Logger\Processor\BackwardsCompatibleMonologProcessorDelegator;
|
||||
|
||||
return [
|
||||
|
||||
@@ -20,6 +21,11 @@ return [
|
||||
RequestId\RequestIdMiddleware::class => ConfigAbstractFactory::class,
|
||||
RequestId\MonologProcessor::class => ConfigAbstractFactory::class,
|
||||
],
|
||||
'delegators' => [
|
||||
RequestId\MonologProcessor::class => [
|
||||
BackwardsCompatibleMonologProcessorDelegator::class,
|
||||
],
|
||||
],
|
||||
],
|
||||
|
||||
ConfigAbstractFactory::class => [
|
||||
|
||||
@@ -3,13 +3,12 @@
|
||||
declare(strict_types=1);
|
||||
|
||||
use Mezzio\Router\FastRouteRouter;
|
||||
|
||||
use function Shlinkio\Shlink\Common\env;
|
||||
use Shlinkio\Shlink\Core\Config\EnvVars;
|
||||
|
||||
return [
|
||||
|
||||
'router' => [
|
||||
'base_path' => env('BASE_PATH', ''),
|
||||
'base_path' => EnvVars::BASE_PATH->loadFromEnv(''),
|
||||
|
||||
'fastroute' => [
|
||||
FastRouteRouter::CONFIG_CACHE_ENABLED => true,
|
||||
|
||||
107
config/autoload/routes.config.php
Normal file
107
config/autoload/routes.config.php
Normal file
@@ -0,0 +1,107 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Shlinkio\Shlink;
|
||||
|
||||
use Fig\Http\Message\RequestMethodInterface;
|
||||
use RKA\Middleware\IpAddress;
|
||||
use Shlinkio\Shlink\Core\Action as CoreAction;
|
||||
use Shlinkio\Shlink\Core\Config\EnvVars;
|
||||
use Shlinkio\Shlink\Rest\Action;
|
||||
use Shlinkio\Shlink\Rest\ConfigProvider;
|
||||
use Shlinkio\Shlink\Rest\Middleware;
|
||||
use Shlinkio\Shlink\Rest\Middleware\Mercure\NotConfiguredMercureErrorHandler;
|
||||
|
||||
use function sprintf;
|
||||
|
||||
// The order of the routes defined here matters. Changing it might cause path conflicts
|
||||
return (static function (): array {
|
||||
$contentNegotiationMiddleware = Middleware\ShortUrl\CreateShortUrlContentNegotiationMiddleware::class;
|
||||
$dropDomainMiddleware = Middleware\ShortUrl\DropDefaultDomainFromRequestMiddleware::class;
|
||||
$overrideDomainMiddleware = Middleware\ShortUrl\OverrideDomainMiddleware::class;
|
||||
$multiSegment = (bool) EnvVars::MULTI_SEGMENT_SLUGS_ENABLED->loadFromEnv(false);
|
||||
|
||||
return [
|
||||
|
||||
'routes' => [
|
||||
// Rest
|
||||
...ConfigProvider::applyRoutesPrefix([
|
||||
Action\HealthAction::getRouteDef(),
|
||||
|
||||
// Visits
|
||||
Action\Visit\ShortUrlVisitsAction::getRouteDef([$dropDomainMiddleware]),
|
||||
Action\Visit\TagVisitsAction::getRouteDef(),
|
||||
Action\Visit\DomainVisitsAction::getRouteDef(),
|
||||
Action\Visit\GlobalVisitsAction::getRouteDef(),
|
||||
Action\Visit\OrphanVisitsAction::getRouteDef(),
|
||||
Action\Visit\NonOrphanVisitsAction::getRouteDef(),
|
||||
|
||||
// Short URLs
|
||||
Action\ShortUrl\CreateShortUrlAction::getRouteDef([
|
||||
$contentNegotiationMiddleware,
|
||||
$dropDomainMiddleware,
|
||||
$overrideDomainMiddleware,
|
||||
Middleware\ShortUrl\DefaultShortCodesLengthMiddleware::class,
|
||||
]),
|
||||
Action\ShortUrl\SingleStepCreateShortUrlAction::getRouteDef([
|
||||
$contentNegotiationMiddleware,
|
||||
$overrideDomainMiddleware,
|
||||
]),
|
||||
Action\ShortUrl\EditShortUrlAction::getRouteDef([$dropDomainMiddleware]),
|
||||
Action\ShortUrl\DeleteShortUrlAction::getRouteDef([$dropDomainMiddleware]),
|
||||
Action\ShortUrl\ResolveShortUrlAction::getRouteDef([$dropDomainMiddleware]),
|
||||
Action\ShortUrl\ListShortUrlsAction::getRouteDef(),
|
||||
|
||||
// Tags
|
||||
Action\Tag\ListTagsAction::getRouteDef(),
|
||||
Action\Tag\TagsStatsAction::getRouteDef(),
|
||||
Action\Tag\DeleteTagsAction::getRouteDef(),
|
||||
Action\Tag\UpdateTagAction::getRouteDef(),
|
||||
|
||||
// Domains
|
||||
Action\Domain\ListDomainsAction::getRouteDef(),
|
||||
Action\Domain\DomainRedirectsAction::getRouteDef(),
|
||||
|
||||
Action\MercureInfoAction::getRouteDef([NotConfiguredMercureErrorHandler::class]),
|
||||
], $multiSegment),
|
||||
|
||||
// Non-rest
|
||||
[
|
||||
'name' => CoreAction\RobotsAction::class,
|
||||
'path' => '/robots.txt',
|
||||
'middleware' => [
|
||||
CoreAction\RobotsAction::class,
|
||||
],
|
||||
'allowed_methods' => [RequestMethodInterface::METHOD_GET],
|
||||
],
|
||||
[
|
||||
'name' => CoreAction\PixelAction::class,
|
||||
'path' => sprintf('/{shortCode%s}/track', $multiSegment ? ':.+' : ''),
|
||||
'middleware' => [
|
||||
IpAddress::class,
|
||||
CoreAction\PixelAction::class,
|
||||
],
|
||||
'allowed_methods' => [RequestMethodInterface::METHOD_GET],
|
||||
],
|
||||
[
|
||||
'name' => CoreAction\QrCodeAction::class,
|
||||
'path' => sprintf('/{shortCode%s}/qr-code', $multiSegment ? ':.+' : ''),
|
||||
'middleware' => [
|
||||
CoreAction\QrCodeAction::class,
|
||||
],
|
||||
'allowed_methods' => [RequestMethodInterface::METHOD_GET],
|
||||
],
|
||||
[
|
||||
'name' => CoreAction\RedirectAction::class,
|
||||
'path' => sprintf('/{shortCode%s}', $multiSegment ? ':.+' : ''),
|
||||
'middleware' => [
|
||||
IpAddress::class,
|
||||
CoreAction\RedirectAction::class,
|
||||
],
|
||||
'allowed_methods' => [RequestMethodInterface::METHOD_GET],
|
||||
],
|
||||
],
|
||||
|
||||
];
|
||||
})();
|
||||
@@ -2,12 +2,12 @@
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use function Shlinkio\Shlink\Common\env;
|
||||
use Shlinkio\Shlink\Core\Config\EnvVars;
|
||||
|
||||
use const Shlinkio\Shlink\MIN_TASK_WORKERS;
|
||||
|
||||
return (static function () {
|
||||
$taskWorkers = (int) env('TASK_WORKER_NUM', 16);
|
||||
return (static function (): array {
|
||||
$taskWorkers = (int) EnvVars::TASK_WORKER_NUM->loadFromEnv(16);
|
||||
|
||||
return [
|
||||
|
||||
@@ -17,11 +17,11 @@ return (static function () {
|
||||
|
||||
'swoole-http-server' => [
|
||||
'host' => '0.0.0.0',
|
||||
'port' => (int) env('PORT', 8080),
|
||||
'port' => (int) EnvVars::PORT->loadFromEnv(8080),
|
||||
'process-name' => 'shlink',
|
||||
|
||||
'options' => [
|
||||
'worker_num' => (int) env('WEB_WORKER_NUM', 16),
|
||||
'worker_num' => (int) EnvVars::WEB_WORKER_NUM->loadFromEnv(16),
|
||||
'task_worker_num' => max($taskWorkers, MIN_TASK_WORKERS),
|
||||
],
|
||||
],
|
||||
|
||||
@@ -2,35 +2,35 @@
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use function Shlinkio\Shlink\Common\env;
|
||||
use Shlinkio\Shlink\Core\Config\EnvVars;
|
||||
|
||||
return [
|
||||
|
||||
'tracking' => [
|
||||
// Tells if IP addresses should be anonymized before persisting, to fulfil data protection regulations
|
||||
// This applies only if IP address tracking is enabled
|
||||
'anonymize_remote_addr' => (bool) env('ANONYMIZE_REMOTE_ADDR', true),
|
||||
'anonymize_remote_addr' => (bool) EnvVars::ANONYMIZE_REMOTE_ADDR->loadFromEnv(true),
|
||||
|
||||
// Tells if visits to not-found URLs should be tracked. The disable_tracking option takes precedence
|
||||
'track_orphan_visits' => (bool) env('TRACK_ORPHAN_VISITS', true),
|
||||
'track_orphan_visits' => (bool) EnvVars::TRACK_ORPHAN_VISITS->loadFromEnv(true),
|
||||
|
||||
// A query param that, if provided, will disable tracking of one particular visit. Always takes precedence
|
||||
'disable_track_param' => env('DISABLE_TRACK_PARAM'),
|
||||
'disable_track_param' => EnvVars::DISABLE_TRACK_PARAM->loadFromEnv(),
|
||||
|
||||
// If true, visits will not be tracked at all
|
||||
'disable_tracking' => (bool) env('DISABLE_TRACKING', false),
|
||||
'disable_tracking' => (bool) EnvVars::DISABLE_TRACKING->loadFromEnv(false),
|
||||
|
||||
// If true, visits will be tracked, but neither the IP address, nor the location will be resolved
|
||||
'disable_ip_tracking' => (bool) env('DISABLE_IP_TRACKING', false),
|
||||
'disable_ip_tracking' => (bool) EnvVars::DISABLE_IP_TRACKING->loadFromEnv(false),
|
||||
|
||||
// If true, the referrer will not be tracked
|
||||
'disable_referrer_tracking' => (bool) env('DISABLE_REFERRER_TRACKING', false),
|
||||
'disable_referrer_tracking' => (bool) EnvVars::DISABLE_REFERRER_TRACKING->loadFromEnv(false),
|
||||
|
||||
// If true, the user agent will not be tracked
|
||||
'disable_ua_tracking' => (bool) env('DISABLE_UA_TRACKING', false),
|
||||
'disable_ua_tracking' => (bool) EnvVars::DISABLE_UA_TRACKING->loadFromEnv(false),
|
||||
|
||||
// A list of IP addresses, patterns or CIDR blocks from which tracking is disabled by default
|
||||
'disable_tracking_from' => env('DISABLE_TRACKING_FROM'),
|
||||
'disable_tracking_from' => EnvVars::DISABLE_TRACKING_FROM->loadFromEnv(),
|
||||
],
|
||||
|
||||
];
|
||||
|
||||
@@ -2,40 +2,28 @@
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use function Shlinkio\Shlink\Common\env;
|
||||
use Shlinkio\Shlink\Core\Config\EnvVars;
|
||||
|
||||
use const Shlinkio\Shlink\DEFAULT_SHORT_CODES_LENGTH;
|
||||
use const Shlinkio\Shlink\MIN_SHORT_CODES_LENGTH;
|
||||
|
||||
return (static function (): array {
|
||||
$shortCodesLength = max(
|
||||
(int) env('DEFAULT_SHORT_CODES_LENGTH', DEFAULT_SHORT_CODES_LENGTH),
|
||||
(int) EnvVars::DEFAULT_SHORT_CODES_LENGTH->loadFromEnv(DEFAULT_SHORT_CODES_LENGTH),
|
||||
MIN_SHORT_CODES_LENGTH,
|
||||
);
|
||||
$resolveSchema = static function (): string {
|
||||
// Deprecated. For v3, IS_HTTPS_ENABLED should be true by default, instead of null
|
||||
// return ((bool) env('IS_HTTPS_ENABLED', true)) ? 'https' : 'http';
|
||||
$isHttpsEnabled = env('IS_HTTPS_ENABLED', env('USE_HTTPS'));
|
||||
if ($isHttpsEnabled !== null) {
|
||||
$boolIsHttpsEnabled = (bool) $isHttpsEnabled;
|
||||
return $boolIsHttpsEnabled ? 'https' : 'http';
|
||||
}
|
||||
|
||||
return env('SHORT_DOMAIN_SCHEMA', 'http');
|
||||
};
|
||||
|
||||
return [
|
||||
|
||||
'url_shortener' => [
|
||||
'domain' => [
|
||||
// Deprecated SHORT_DOMAIN_* env vars
|
||||
'schema' => $resolveSchema(),
|
||||
'hostname' => env('DEFAULT_DOMAIN', env('SHORT_DOMAIN_HOST', '')),
|
||||
'domain' => [ // TODO Refactor this structure to url_shortener.schema and url_shortener.default_domain
|
||||
'schema' => ((bool) EnvVars::IS_HTTPS_ENABLED->loadFromEnv(true)) ? 'https' : 'http',
|
||||
'hostname' => EnvVars::DEFAULT_DOMAIN->loadFromEnv(''),
|
||||
],
|
||||
'validate_url' => (bool) env('VALIDATE_URLS', false), // Deprecated
|
||||
'default_short_codes_length' => $shortCodesLength,
|
||||
'auto_resolve_titles' => (bool) env('AUTO_RESOLVE_TITLES', false),
|
||||
'append_extra_path' => (bool) env('REDIRECT_APPEND_EXTRA_PATH', false),
|
||||
'auto_resolve_titles' => (bool) EnvVars::AUTO_RESOLVE_TITLES->loadFromEnv(false),
|
||||
'append_extra_path' => (bool) EnvVars::REDIRECT_APPEND_EXTRA_PATH->loadFromEnv(false),
|
||||
'multi_segment_slugs_enabled' => (bool) EnvVars::MULTI_SEGMENT_SLUGS_ENABLED->loadFromEnv(false),
|
||||
],
|
||||
|
||||
];
|
||||
|
||||
@@ -2,17 +2,18 @@
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use function Shlinkio\Shlink\Common\env;
|
||||
use Shlinkio\Shlink\Core\Config\EnvVars;
|
||||
|
||||
// Deprecated. Webhooks are no longer supported. To be removed in Shlink 4.0.0
|
||||
return (static function (): array {
|
||||
$webhooks = env('VISITS_WEBHOOKS');
|
||||
$webhooks = EnvVars::VISITS_WEBHOOKS->loadFromEnv();
|
||||
|
||||
return [
|
||||
|
||||
'url_shortener' => [
|
||||
// TODO Move these options to their own config namespace
|
||||
'visits_webhooks' => $webhooks === null ? [] : explode(',', $webhooks),
|
||||
'notify_orphan_visits_to_webhooks' => (bool) env('NOTIFY_ORPHAN_VISITS_TO_WEBHOOKS', false),
|
||||
'visits_webhooks' => [
|
||||
'webhooks' => $webhooks === null ? [] : explode(',', $webhooks),
|
||||
'notify_orphan_visits_to_webhooks' =>
|
||||
(bool) EnvVars::NOTIFY_ORPHAN_VISITS_TO_WEBHOOKS->loadFromEnv(false),
|
||||
],
|
||||
|
||||
];
|
||||
|
||||
@@ -5,7 +5,7 @@ declare(strict_types=1);
|
||||
use Psr\Container\ContainerInterface;
|
||||
use Symfony\Component\Console\Application as CliApp;
|
||||
|
||||
return (static function () {
|
||||
return (static function (): CliApp {
|
||||
/** @var ContainerInterface $container */
|
||||
$container = include __DIR__ . '/container.php';
|
||||
return $container->get(CliApp::class);
|
||||
|
||||
@@ -9,15 +9,20 @@ use Laminas\Diactoros;
|
||||
use Mezzio;
|
||||
use Mezzio\ProblemDetails;
|
||||
use Mezzio\Swoole;
|
||||
use Shlinkio\Shlink\Config\ConfigAggregator\EnvVarLoaderProvider;
|
||||
|
||||
use function class_exists;
|
||||
use function Shlinkio\Shlink\Common\env;
|
||||
use function Shlinkio\Shlink\Config\env;
|
||||
|
||||
use const PHP_SAPI;
|
||||
|
||||
$isCli = PHP_SAPI === 'cli';
|
||||
$isTestEnv = env('APP_ENV') === 'test';
|
||||
|
||||
return (new ConfigAggregator\ConfigAggregator([
|
||||
! $isTestEnv
|
||||
? new EnvVarLoaderProvider('config/params/generated_config.php', Core\Config\EnvVars::cases())
|
||||
: new ConfigAggregator\ArrayProvider([]),
|
||||
Mezzio\ConfigProvider::class,
|
||||
Mezzio\Router\ConfigProvider::class,
|
||||
Mezzio\Router\FastRouteRouter\ConfigProvider::class,
|
||||
@@ -35,12 +40,11 @@ return (new ConfigAggregator\ConfigAggregator([
|
||||
CLI\ConfigProvider::class,
|
||||
Rest\ConfigProvider::class,
|
||||
new ConfigAggregator\PhpFileProvider('config/autoload/{{,*.}global,{,*.}local}.php'),
|
||||
env('APP_ENV') === 'test'
|
||||
$isTestEnv
|
||||
? new ConfigAggregator\PhpFileProvider('config/test/*.global.php')
|
||||
// Deprecated. When the SimplifiedConfigParser is removed, load only generated_config.php here
|
||||
: new ConfigAggregator\LaminasConfigProvider('config/params/{generated_config.php,*.config.{php,json}}'),
|
||||
: new ConfigAggregator\ArrayProvider([]),
|
||||
// Routes have to be loaded last
|
||||
new ConfigAggregator\PhpFileProvider('config/autoload/routes.config.php'),
|
||||
], 'data/cache/app_config.php', [
|
||||
Core\Config\SimplifiedConfigParser::class,
|
||||
Core\Config\BasePathPrefixer::class,
|
||||
Core\Config\DeprecatedConfigParser::class,
|
||||
]))->getMergedConfig();
|
||||
|
||||
@@ -12,11 +12,11 @@ const MIN_SHORT_CODES_LENGTH = 4;
|
||||
const DEFAULT_REDIRECT_STATUS_CODE = StatusCodeInterface::STATUS_FOUND;
|
||||
const DEFAULT_REDIRECT_CACHE_LIFETIME = 30;
|
||||
const LOCAL_LOCK_FACTORY = 'Shlinkio\Shlink\LocalLockFactory';
|
||||
const CUSTOM_SLUGS_REGEXP = '/[^\pL\pN._~]/u'; // Any unicode letter or number, plus ".", "_" and "~" chars
|
||||
const TITLE_TAG_VALUE = '/<title[^>]*>(.*?)<\/title>/i'; // Matches the value inside an html title tag
|
||||
const TITLE_TAG_VALUE = '/<title[^>]*>(.*?)<\/title>/i'; // Matches the value inside a html title tag
|
||||
const DEFAULT_QR_CODE_SIZE = 300;
|
||||
const DEFAULT_QR_CODE_MARGIN = 0;
|
||||
const DEFAULT_QR_CODE_FORMAT = 'png';
|
||||
const DEFAULT_QR_CODE_ERROR_CORRECTION = 'l';
|
||||
const DEFAULT_QR_CODE_ROUND_BLOCK_SIZE = true;
|
||||
const MIN_TASK_WORKERS = 4;
|
||||
const MIGRATIONS_TABLE = 'migrations';
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
declare(strict_types=1);
|
||||
|
||||
use Laminas\ServiceManager\ServiceManager;
|
||||
use Shlinkio\Shlink\Core\Config\EnvVars;
|
||||
use Symfony\Component\Lock;
|
||||
|
||||
use const Shlinkio\Shlink\LOCAL_LOCK_FACTORY;
|
||||
@@ -11,6 +12,9 @@ chdir(dirname(__DIR__));
|
||||
|
||||
require 'vendor/autoload.php';
|
||||
|
||||
// This is one of the first files loaded. Configure the timezone here
|
||||
date_default_timezone_set(EnvVars::TIMEZONE->loadFromEnv(date_default_timezone_get()));
|
||||
|
||||
// This class alias tricks the ConfigAbstractFactory to return Lock\Factory instances even with a different service name
|
||||
// It needs to be placed here as individual config files will not be loaded once config is cached
|
||||
if (! class_exists(LOCAL_LOCK_FACTORY)) {
|
||||
@@ -18,7 +22,7 @@ if (! class_exists(LOCAL_LOCK_FACTORY)) {
|
||||
}
|
||||
|
||||
// Build container
|
||||
return (function () {
|
||||
return (static function (): ServiceManager {
|
||||
$config = require __DIR__ . '/config.php';
|
||||
$container = new ServiceManager($config['dependencies']);
|
||||
$container->setService('config', $config);
|
||||
|
||||
@@ -3,9 +3,10 @@
|
||||
declare(strict_types=1);
|
||||
|
||||
use Doctrine\ORM\EntityManager;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Psr\Container\ContainerInterface;
|
||||
|
||||
return (static function () {
|
||||
return (static function (): EntityManagerInterface {
|
||||
/** @var ContainerInterface $container */
|
||||
$container = include __DIR__ . '/container.php';
|
||||
return $container->get(EntityManager::class);
|
||||
|
||||
@@ -8,5 +8,5 @@ use Psr\Container\ContainerInterface;
|
||||
|
||||
/** @var ContainerInterface $container */
|
||||
$container = require __DIR__ . '/../container.php';
|
||||
$container->get(Helper\TestHelper::class)->createTestDb();
|
||||
$container->get(Helper\TestHelper::class)->createTestDb(['bin/cli', 'db:create'], ['bin/cli', 'db:migrate']);
|
||||
DbTest\DatabaseTestCase::setEntityManager($container->get('em'));
|
||||
|
||||
@@ -8,8 +8,7 @@ use GuzzleHttp\Client;
|
||||
use Laminas\ConfigAggregator\ConfigAggregator;
|
||||
use Laminas\Diactoros\Response\EmptyResponse;
|
||||
use Laminas\ServiceManager\Factory\InvokableFactory;
|
||||
use Monolog\Handler\StreamHandler;
|
||||
use Monolog\Logger;
|
||||
use Monolog\Level;
|
||||
use PHPUnit\Runner\Version;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
@@ -20,9 +19,10 @@ use SebastianBergmann\CodeCoverage\Filter;
|
||||
use SebastianBergmann\CodeCoverage\Report\Html\Facade as Html;
|
||||
use SebastianBergmann\CodeCoverage\Report\PHP;
|
||||
use SebastianBergmann\CodeCoverage\Report\Xml\Facade as Xml;
|
||||
use Shlinkio\Shlink\Common\Logger\LoggerType;
|
||||
|
||||
use function Laminas\Stratigility\middleware;
|
||||
use function Shlinkio\Shlink\Common\env;
|
||||
use function Shlinkio\Shlink\Config\env;
|
||||
use function sprintf;
|
||||
use function sys_get_temp_dir;
|
||||
|
||||
@@ -55,6 +55,7 @@ $buildDbConnection = static function (): array {
|
||||
'user' => 'postgres',
|
||||
'password' => 'root',
|
||||
'dbname' => 'shlink_test',
|
||||
'charset' => 'utf8',
|
||||
],
|
||||
'mssql' => [
|
||||
'driver' => 'pdo_sqlsrv',
|
||||
@@ -70,20 +71,15 @@ $buildDbConnection = static function (): array {
|
||||
'user' => 'root',
|
||||
'password' => 'root',
|
||||
'dbname' => 'shlink_test',
|
||||
'charset' => 'utf8mb4',
|
||||
],
|
||||
};
|
||||
};
|
||||
|
||||
$buildTestLoggerConfig = fn (string $handlerName, string $filename) => [
|
||||
'handlers' => [
|
||||
$handlerName => [
|
||||
'name' => StreamHandler::class,
|
||||
'params' => [
|
||||
'level' => Logger::DEBUG,
|
||||
'stream' => sprintf('data/log/api-tests/%s', $filename),
|
||||
],
|
||||
],
|
||||
],
|
||||
$buildTestLoggerConfig = static fn (string $filename) => [
|
||||
'level' => Level::Debug->value,
|
||||
'type' => LoggerType::STREAM->value,
|
||||
'destination' => sprintf('data/log/api-tests/%s', $filename),
|
||||
];
|
||||
|
||||
return [
|
||||
@@ -107,6 +103,7 @@ return [
|
||||
'process-name' => 'shlink_test',
|
||||
'options' => [
|
||||
'pid_file' => sys_get_temp_dir() . '/shlink-test-swoole.pid',
|
||||
'log_file' => __DIR__ . '/../../data/log/api-tests/output.log',
|
||||
'enable_coroutine' => false,
|
||||
],
|
||||
],
|
||||
@@ -180,8 +177,8 @@ return [
|
||||
],
|
||||
|
||||
'logger' => [
|
||||
'Shlink' => $buildTestLoggerConfig('shlink_handler', 'shlink.log'),
|
||||
'Access' => $buildTestLoggerConfig('access_handler', 'access.log'),
|
||||
'Shlink' => $buildTestLoggerConfig('shlink.log'),
|
||||
'Access' => $buildTestLoggerConfig('access.log'),
|
||||
],
|
||||
|
||||
];
|
||||
|
||||
@@ -11,7 +11,7 @@ server {
|
||||
|
||||
location ~ \.php$ {
|
||||
fastcgi_split_path_info ^(.+\.php)(/.+)$;
|
||||
fastcgi_pass unix:/var/run/php/php8.0-fpm.sock;
|
||||
fastcgi_pass unix:/var/run/php/php8.1-fpm.sock;
|
||||
fastcgi_index index.php;
|
||||
include fastcgi.conf;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/var/log/shlink/shlink_swoole.log {
|
||||
/var/log/shlink/shlink_openswoole.log {
|
||||
su root root
|
||||
daily
|
||||
missingok
|
||||
@@ -8,6 +8,6 @@
|
||||
notifempty
|
||||
create 0640 root root
|
||||
postrotate
|
||||
/etc/init.d/shlink_swoole restart
|
||||
/etc/init.d/shlink_openswoole restart
|
||||
endscript
|
||||
}
|
||||
|
||||
@@ -1,26 +1,26 @@
|
||||
#!/bin/bash
|
||||
### BEGIN INIT INFO
|
||||
# Provides: shlink_swoole
|
||||
# Provides: shlink_openswoole
|
||||
# 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
|
||||
# Description: Shlink non-blocking server with openswoole
|
||||
### END INIT INFO
|
||||
|
||||
SCRIPT=/path/to/shlink/vendor/bin/laminas\ mezzio:swoole:start
|
||||
RUNAS=root
|
||||
|
||||
PIDFILE=/var/run/shlink_swoole.pid
|
||||
PIDFILE=/var/run/shlink_openswoole.pid
|
||||
LOGDIR=/var/log/shlink
|
||||
LOGFILE=${LOGDIR}/shlink_swoole.log
|
||||
LOGFILE=${LOGDIR}/shlink_openswoole.log
|
||||
|
||||
start() {
|
||||
if [[ -f "$PIDFILE" ]] && kill -0 $(cat "$PIDFILE"); then
|
||||
echo 'Shlink with swoole already running' >&2
|
||||
echo 'Shlink with openswoole already running' >&2
|
||||
return 1
|
||||
fi
|
||||
echo 'Starting shlink with swoole' >&2
|
||||
echo 'Starting shlink with openswoole' >&2
|
||||
mkdir -p "$LOGDIR"
|
||||
touch "$LOGFILE"
|
||||
local CMD="$SCRIPT &> \"$LOGFILE\" & echo \$!"
|
||||
@@ -30,10 +30,10 @@ start() {
|
||||
|
||||
stop() {
|
||||
if [[ ! -f "$PIDFILE" ]] || ! kill -0 $(cat "$PIDFILE"); then
|
||||
echo 'Shlink with swoole not running' >&2
|
||||
echo 'Shlink with openswoole not running' >&2
|
||||
return 1
|
||||
fi
|
||||
echo 'Stopping shlink with swoole' >&2
|
||||
echo 'Stopping shlink with openswoole' >&2
|
||||
kill -15 $(cat "$PIDFILE") && rm -f "$PIDFILE"
|
||||
echo 'Shlink stopped' >&2
|
||||
}
|
||||
|
||||
@@ -1,15 +1,14 @@
|
||||
FROM php:8.1.0-fpm-alpine3.15
|
||||
FROM php:8.1.9-fpm-alpine3.16
|
||||
MAINTAINER Alejandro Celaya <alejandro@alejandrocelaya.com>
|
||||
|
||||
ENV APCU_VERSION 5.1.21
|
||||
ENV PDO_SQLSRV_VERSION 5.10.0beta2
|
||||
ENV PDO_SQLSRV_VERSION 5.10.1
|
||||
ENV MS_ODBC_SQL_VERSION 17.5.2.2
|
||||
|
||||
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 calendar
|
||||
|
||||
RUN apk add --no-cache oniguruma-dev
|
||||
@@ -31,9 +30,6 @@ 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
|
||||
|
||||
RUN docker-php-ext-install sockets
|
||||
RUN docker-php-ext-install bcmath
|
||||
|
||||
|
||||
@@ -1,17 +1,16 @@
|
||||
FROM php:8.1.0-alpine3.15
|
||||
FROM php:8.1.9-alpine3.16
|
||||
MAINTAINER Alejandro Celaya <alejandro@alejandrocelaya.com>
|
||||
|
||||
ENV APCU_VERSION 5.1.21
|
||||
ENV INOTIFY_VERSION 3.0.0
|
||||
ENV OPENSWOOLE_VERSION 4.8.1
|
||||
ENV PDO_SQLSRV_VERSION 5.10.0beta2
|
||||
ENV OPENSWOOLE_VERSION 4.11.1
|
||||
ENV PDO_SQLSRV_VERSION 5.10.1
|
||||
ENV MS_ODBC_SQL_VERSION 17.5.2.2
|
||||
|
||||
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 calendar
|
||||
|
||||
RUN apk add --no-cache oniguruma-dev
|
||||
@@ -33,9 +32,6 @@ 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
|
||||
|
||||
RUN docker-php-ext-install sockets
|
||||
RUN docker-php-ext-install bcmath
|
||||
|
||||
|
||||
@@ -5,45 +5,45 @@ declare(strict_types=1);
|
||||
namespace ShlinkMigrations;
|
||||
|
||||
use Doctrine\DBAL\Exception;
|
||||
use Doctrine\DBAL\Platforms\MySQLPlatform;
|
||||
use Doctrine\DBAL\Platforms\SqlitePlatform;
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\DBAL\Schema\SchemaException;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
|
||||
use function is_subclass_of;
|
||||
|
||||
/**
|
||||
* Auto-generated Migration: Please modify to your needs!
|
||||
*/
|
||||
class Version20160819142757 extends AbstractMigration
|
||||
{
|
||||
private const MYSQL = 'mysql';
|
||||
private const SQLITE = 'sqlite';
|
||||
|
||||
/**
|
||||
* @throws Exception
|
||||
* @throws SchemaException
|
||||
*/
|
||||
public function up(Schema $schema): void
|
||||
{
|
||||
$db = $this->connection->getDatabasePlatform()->getName();
|
||||
$platformClass = $this->connection->getDatabasePlatform();
|
||||
$table = $schema->getTable('short_urls');
|
||||
$column = $table->getColumn('short_code');
|
||||
|
||||
if ($db === self::MYSQL) {
|
||||
$column->setPlatformOption('collation', 'utf8_bin');
|
||||
} elseif ($db === self::SQLITE) {
|
||||
$column->setPlatformOption('collate', 'BINARY');
|
||||
}
|
||||
match (true) {
|
||||
is_subclass_of($platformClass, MySQLPlatform::class) => $column
|
||||
->setPlatformOption('charset', 'utf8mb4')
|
||||
->setPlatformOption('collation', 'utf8mb4_bin'),
|
||||
is_subclass_of($platformClass, SqlitePlatform::class) => $column->setPlatformOption('collate', 'BINARY'),
|
||||
default => null,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws Exception
|
||||
*/
|
||||
public function down(Schema $schema): void
|
||||
{
|
||||
$this->connection->getDatabasePlatform()->getName();
|
||||
// Nothing to roll back
|
||||
}
|
||||
|
||||
public function isTransactional(): bool
|
||||
{
|
||||
return $this->connection->getDatabasePlatform()->getName() !== 'mysql';
|
||||
return ! ($this->connection->getDatabasePlatform() instanceof MySQLPlatform);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace ShlinkMigrations;
|
||||
|
||||
use Doctrine\DBAL\Platforms\MySQLPlatform;
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\DBAL\Types\Types;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
@@ -76,6 +77,6 @@ class Version20160820191203 extends AbstractMigration
|
||||
|
||||
public function isTransactional(): bool
|
||||
{
|
||||
return $this->connection->getDatabasePlatform()->getName() !== 'mysql';
|
||||
return ! ($this->connection->getDatabasePlatform() instanceof MySQLPlatform);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace ShlinkMigrations;
|
||||
|
||||
use Doctrine\DBAL\Platforms\MySQLPlatform;
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\DBAL\Schema\SchemaException;
|
||||
use Doctrine\DBAL\Types\Types;
|
||||
@@ -48,6 +49,6 @@ class Version20171021093246 extends AbstractMigration
|
||||
|
||||
public function isTransactional(): bool
|
||||
{
|
||||
return $this->connection->getDatabasePlatform()->getName() !== 'mysql';
|
||||
return ! ($this->connection->getDatabasePlatform() instanceof MySQLPlatform);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace ShlinkMigrations;
|
||||
|
||||
use Doctrine\DBAL\Platforms\MySQLPlatform;
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\DBAL\Schema\SchemaException;
|
||||
use Doctrine\DBAL\Types\Types;
|
||||
@@ -45,6 +46,6 @@ class Version20171022064541 extends AbstractMigration
|
||||
|
||||
public function isTransactional(): bool
|
||||
{
|
||||
return $this->connection->getDatabasePlatform()->getName() !== 'mysql';
|
||||
return ! ($this->connection->getDatabasePlatform() instanceof MySQLPlatform);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace ShlinkMigrations;
|
||||
|
||||
use Doctrine\DBAL\Platforms\MySQLPlatform;
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\DBAL\Schema\SchemaException;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
@@ -42,6 +43,6 @@ final class Version20180801183328 extends AbstractMigration
|
||||
|
||||
public function isTransactional(): bool
|
||||
{
|
||||
return $this->connection->getDatabasePlatform()->getName() !== 'mysql';
|
||||
return ! ($this->connection->getDatabasePlatform() instanceof MySQLPlatform);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ declare(strict_types=1);
|
||||
namespace ShlinkMigrations;
|
||||
|
||||
use Doctrine\DBAL\Exception;
|
||||
use Doctrine\DBAL\Platforms\MySQLPlatform;
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
use PDO;
|
||||
@@ -69,6 +70,6 @@ final class Version20180913205455 extends AbstractMigration
|
||||
|
||||
public function isTransactional(): bool
|
||||
{
|
||||
return $this->connection->getDatabasePlatform()->getName() !== 'mysql';
|
||||
return ! ($this->connection->getDatabasePlatform() instanceof MySQLPlatform);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace ShlinkMigrations;
|
||||
|
||||
use Doctrine\DBAL\Platforms\MySQLPlatform;
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\DBAL\Schema\SchemaException;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
@@ -50,6 +51,6 @@ final class Version20180915110857 extends AbstractMigration
|
||||
|
||||
public function isTransactional(): bool
|
||||
{
|
||||
return $this->connection->getDatabasePlatform()->getName() !== 'mysql';
|
||||
return ! ($this->connection->getDatabasePlatform() instanceof MySQLPlatform);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ declare(strict_types=1);
|
||||
namespace ShlinkMigrations;
|
||||
|
||||
use Doctrine\DBAL\Exception;
|
||||
use Doctrine\DBAL\Platforms\MySQLPlatform;
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\DBAL\Schema\SchemaException;
|
||||
use Doctrine\DBAL\Schema\Table;
|
||||
@@ -58,7 +59,7 @@ final class Version20181020060559 extends AbstractMigration
|
||||
foreach (self::COLUMNS as $camelCaseName => $snakeCaseName) {
|
||||
$qb->set($snakeCaseName, $camelCaseName);
|
||||
}
|
||||
$qb->execute();
|
||||
$qb->executeStatement();
|
||||
}
|
||||
|
||||
public function down(Schema $schema): void
|
||||
@@ -68,6 +69,6 @@ final class Version20181020060559 extends AbstractMigration
|
||||
|
||||
public function isTransactional(): bool
|
||||
{
|
||||
return $this->connection->getDatabasePlatform()->getName() !== 'mysql';
|
||||
return ! ($this->connection->getDatabasePlatform() instanceof MySQLPlatform);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace ShlinkMigrations;
|
||||
|
||||
use Doctrine\DBAL\Platforms\MySQLPlatform;
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\DBAL\Schema\SchemaException;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
@@ -41,6 +42,6 @@ final class Version20181020065148 extends AbstractMigration
|
||||
|
||||
public function isTransactional(): bool
|
||||
{
|
||||
return $this->connection->getDatabasePlatform()->getName() !== 'mysql';
|
||||
return ! ($this->connection->getDatabasePlatform() instanceof MySQLPlatform);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace ShlinkMigrations;
|
||||
|
||||
use Doctrine\DBAL\Platforms\MySQLPlatform;
|
||||
use Doctrine\DBAL\Schema\Column;
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\DBAL\Schema\SchemaException;
|
||||
@@ -37,6 +38,6 @@ final class Version20181110175521 extends AbstractMigration
|
||||
|
||||
public function isTransactional(): bool
|
||||
{
|
||||
return $this->connection->getDatabasePlatform()->getName() !== 'mysql';
|
||||
return ! ($this->connection->getDatabasePlatform() instanceof MySQLPlatform);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace ShlinkMigrations;
|
||||
|
||||
use Doctrine\DBAL\Platforms\MySQLPlatform;
|
||||
use Doctrine\DBAL\Schema\Column;
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\DBAL\Schema\SchemaException;
|
||||
@@ -37,6 +38,6 @@ final class Version20190824075137 extends AbstractMigration
|
||||
|
||||
public function isTransactional(): bool
|
||||
{
|
||||
return $this->connection->getDatabasePlatform()->getName() !== 'mysql';
|
||||
return ! ($this->connection->getDatabasePlatform() instanceof MySQLPlatform);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace ShlinkMigrations;
|
||||
|
||||
use Doctrine\DBAL\Platforms\MySQLPlatform;
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\DBAL\Schema\SchemaException;
|
||||
use Doctrine\DBAL\Types\Types;
|
||||
@@ -55,6 +56,6 @@ final class Version20190930165521 extends AbstractMigration
|
||||
|
||||
public function isTransactional(): bool
|
||||
{
|
||||
return $this->connection->getDatabasePlatform()->getName() !== 'mysql';
|
||||
return ! ($this->connection->getDatabasePlatform() instanceof MySQLPlatform);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace ShlinkMigrations;
|
||||
|
||||
use Doctrine\DBAL\Platforms\MySQLPlatform;
|
||||
use Doctrine\DBAL\Schema\Index;
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\DBAL\Schema\SchemaException;
|
||||
@@ -49,6 +50,6 @@ final class Version20191001201532 extends AbstractMigration
|
||||
|
||||
public function isTransactional(): bool
|
||||
{
|
||||
return $this->connection->getDatabasePlatform()->getName() !== 'mysql';
|
||||
return ! ($this->connection->getDatabasePlatform() instanceof MySQLPlatform);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace ShlinkMigrations;
|
||||
|
||||
use Doctrine\DBAL\Platforms\MySQLPlatform;
|
||||
use Doctrine\DBAL\Schema\Column;
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\DBAL\Schema\SchemaException;
|
||||
@@ -37,6 +38,6 @@ final class Version20191020074522 extends AbstractMigration
|
||||
|
||||
public function isTransactional(): bool
|
||||
{
|
||||
return $this->connection->getDatabasePlatform()->getName() !== 'mysql';
|
||||
return ! ($this->connection->getDatabasePlatform() instanceof MySQLPlatform);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,8 @@ declare(strict_types=1);
|
||||
namespace ShlinkMigrations;
|
||||
|
||||
use Doctrine\DBAL\Exception;
|
||||
use Doctrine\DBAL\Platforms\MySQLPlatform;
|
||||
use Doctrine\DBAL\Platforms\PostgreSQLPlatform;
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\DBAL\Types\Types;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
@@ -38,7 +40,7 @@ final class Version20200105165647 extends AbstractMigration
|
||||
'zeroValue' => '0',
|
||||
'emptyString' => '',
|
||||
])
|
||||
->execute();
|
||||
->executeStatement();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,14 +63,14 @@ final class Version20200105165647 extends AbstractMigration
|
||||
*/
|
||||
public function postUp(Schema $schema): void
|
||||
{
|
||||
$platformName = $this->connection->getDatabasePlatform()->getName();
|
||||
$castType = $platformName === 'postgres' ? 'DOUBLE PRECISION' : 'DECIMAL(9,2)';
|
||||
$isPostgres = $this->connection->getDatabasePlatform() instanceof PostgreSQLPlatform;
|
||||
$castType = $isPostgres ? '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();
|
||||
->executeStatement();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,7 +80,7 @@ final class Version20200105165647 extends AbstractMigration
|
||||
$qb = $this->connection->createQueryBuilder();
|
||||
$qb->update('visit_locations')
|
||||
->set($oldName, $newName)
|
||||
->execute();
|
||||
->executeStatement();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,6 +98,6 @@ final class Version20200105165647 extends AbstractMigration
|
||||
|
||||
public function isTransactional(): bool
|
||||
{
|
||||
return $this->connection->getDatabasePlatform()->getName() !== 'mysql';
|
||||
return ! ($this->connection->getDatabasePlatform() instanceof MySQLPlatform);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ declare(strict_types=1);
|
||||
namespace ShlinkMigrations;
|
||||
|
||||
use Doctrine\DBAL\Exception;
|
||||
use Doctrine\DBAL\Platforms\MySQLPlatform;
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\DBAL\Types\Types;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
@@ -47,6 +48,6 @@ final class Version20200106215144 extends AbstractMigration
|
||||
|
||||
public function isTransactional(): bool
|
||||
{
|
||||
return $this->connection->getDatabasePlatform()->getName() !== 'mysql';
|
||||
return ! ($this->connection->getDatabasePlatform() instanceof MySQLPlatform);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,8 @@ declare(strict_types=1);
|
||||
|
||||
namespace ShlinkMigrations;
|
||||
|
||||
use Doctrine\DBAL\Exception;
|
||||
use Doctrine\DBAL\Platforms\MySQLPlatform;
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
|
||||
@@ -36,6 +38,9 @@ final class Version20200110182849 extends AbstractMigration
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws Exception
|
||||
*/
|
||||
public function setDefaultValueForColumnInTable(string $tableName, string $columnName): void
|
||||
{
|
||||
$qb = $this->connection->createQueryBuilder();
|
||||
@@ -43,7 +48,7 @@ final class Version20200110182849 extends AbstractMigration
|
||||
->set($columnName, ':emptyValue')
|
||||
->setParameter('emptyValue', self::DEFAULT_EMPTY_VALUE)
|
||||
->where($qb->expr()->isNull($columnName))
|
||||
->execute();
|
||||
->executeStatement();
|
||||
}
|
||||
|
||||
public function down(Schema $schema): void
|
||||
@@ -53,6 +58,6 @@ final class Version20200110182849 extends AbstractMigration
|
||||
|
||||
public function isTransactional(): bool
|
||||
{
|
||||
return $this->connection->getDatabasePlatform()->getName() !== 'mysql';
|
||||
return ! ($this->connection->getDatabasePlatform() instanceof MySQLPlatform);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace ShlinkMigrations;
|
||||
|
||||
use Doctrine\DBAL\Platforms\MySQLPlatform;
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\DBAL\Types\Types;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
@@ -32,7 +33,7 @@ final class Version20200323190014 extends AbstractMigration
|
||||
->andWhere($qb->expr()->eq('lon', 0))
|
||||
->setParameter('isEmpty', true)
|
||||
->setParameter('emptyString', '')
|
||||
->execute();
|
||||
->executeStatement();
|
||||
}
|
||||
|
||||
public function down(Schema $schema): void
|
||||
@@ -45,6 +46,6 @@ final class Version20200323190014 extends AbstractMigration
|
||||
|
||||
public function isTransactional(): bool
|
||||
{
|
||||
return $this->connection->getDatabasePlatform()->getName() !== 'mysql';
|
||||
return ! ($this->connection->getDatabasePlatform() instanceof MySQLPlatform);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace ShlinkMigrations;
|
||||
|
||||
use Doctrine\DBAL\Platforms\MySQLPlatform;
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
|
||||
@@ -27,6 +28,6 @@ final class Version20200503170404 extends AbstractMigration
|
||||
|
||||
public function isTransactional(): bool
|
||||
{
|
||||
return $this->connection->getDatabasePlatform()->getName() !== 'mysql';
|
||||
return ! ($this->connection->getDatabasePlatform() instanceof MySQLPlatform);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace ShlinkMigrations;
|
||||
|
||||
use Doctrine\DBAL\Platforms\MySQLPlatform;
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\DBAL\Types\Types;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
@@ -44,6 +45,6 @@ final class Version20201023090929 extends AbstractMigration
|
||||
|
||||
public function isTransactional(): bool
|
||||
{
|
||||
return $this->connection->getDatabasePlatform()->getName() !== 'mysql';
|
||||
return ! ($this->connection->getDatabasePlatform() instanceof MySQLPlatform);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ namespace ShlinkMigrations;
|
||||
|
||||
use Cake\Chronos\Chronos;
|
||||
use Doctrine\DBAL\Driver\Result;
|
||||
use Doctrine\DBAL\Platforms\MySQLPlatform;
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\DBAL\Types\Types;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
@@ -86,6 +87,6 @@ final class Version20201102113208 extends AbstractMigration
|
||||
|
||||
public function isTransactional(): bool
|
||||
{
|
||||
return $this->connection->getDatabasePlatform()->getName() !== 'mysql';
|
||||
return ! ($this->connection->getDatabasePlatform() instanceof MySQLPlatform);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace ShlinkMigrations;
|
||||
|
||||
use Doctrine\DBAL\Platforms\MySQLPlatform;
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\DBAL\Types\Types;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
@@ -52,6 +53,6 @@ final class Version20210102174433 extends AbstractMigration
|
||||
|
||||
public function isTransactional(): bool
|
||||
{
|
||||
return $this->connection->getDatabasePlatform()->getName() !== 'mysql';
|
||||
return ! ($this->connection->getDatabasePlatform() instanceof MySQLPlatform);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace ShlinkMigrations;
|
||||
|
||||
use Doctrine\DBAL\Platforms\MySQLPlatform;
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
|
||||
@@ -26,6 +27,6 @@ final class Version20210118153932 extends AbstractMigration
|
||||
|
||||
public function isTransactional(): bool
|
||||
{
|
||||
return $this->connection->getDatabasePlatform()->getName() !== 'mysql';
|
||||
return ! ($this->connection->getDatabasePlatform() instanceof MySQLPlatform);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace ShlinkMigrations;
|
||||
|
||||
use Doctrine\DBAL\Platforms\MySQLPlatform;
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\DBAL\Types\Types;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
@@ -36,6 +37,6 @@ final class Version20210202181026 extends AbstractMigration
|
||||
|
||||
public function isTransactional(): bool
|
||||
{
|
||||
return $this->connection->getDatabasePlatform()->getName() !== 'mysql';
|
||||
return ! ($this->connection->getDatabasePlatform() instanceof MySQLPlatform);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,11 +4,12 @@ declare(strict_types=1);
|
||||
|
||||
namespace ShlinkMigrations;
|
||||
|
||||
use Doctrine\DBAL\Platforms\MySQLPlatform;
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\DBAL\Types\Types;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
use Shlinkio\Shlink\Core\Entity\Visit;
|
||||
use Shlinkio\Shlink\Core\Model\Visitor;
|
||||
use Shlinkio\Shlink\Core\Visit\Model\VisitType;
|
||||
|
||||
final class Version20210207100807 extends AbstractMigration
|
||||
{
|
||||
@@ -26,7 +27,7 @@ final class Version20210207100807 extends AbstractMigration
|
||||
]);
|
||||
$visits->addColumn('type', Types::STRING, [
|
||||
'length' => 255,
|
||||
'default' => Visit::TYPE_VALID_SHORT_URL,
|
||||
'default' => VisitType::VALID_SHORT_URL->value,
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -43,6 +44,6 @@ final class Version20210207100807 extends AbstractMigration
|
||||
|
||||
public function isTransactional(): bool
|
||||
{
|
||||
return $this->connection->getDatabasePlatform()->getName() !== 'mysql';
|
||||
return ! ($this->connection->getDatabasePlatform() instanceof MySQLPlatform);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace ShlinkMigrations;
|
||||
|
||||
use Doctrine\DBAL\Platforms\MySQLPlatform;
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\DBAL\Types\Types;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
@@ -37,6 +38,6 @@ final class Version20210306165711 extends AbstractMigration
|
||||
|
||||
public function isTransactional(): bool
|
||||
{
|
||||
return $this->connection->getDatabasePlatform()->getName() !== 'mysql';
|
||||
return ! ($this->connection->getDatabasePlatform() instanceof MySQLPlatform);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace ShlinkMigrations;
|
||||
|
||||
use Doctrine\DBAL\Platforms\MySQLPlatform;
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\DBAL\Types\Types;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
@@ -26,6 +27,6 @@ final class Version20210522051601 extends AbstractMigration
|
||||
|
||||
public function isTransactional(): bool
|
||||
{
|
||||
return $this->connection->getDatabasePlatform()->getName() !== 'mysql';
|
||||
return ! ($this->connection->getDatabasePlatform() instanceof MySQLPlatform);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace ShlinkMigrations;
|
||||
|
||||
use Doctrine\DBAL\Platforms\MySQLPlatform;
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\DBAL\Types\Types;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
@@ -28,6 +29,6 @@ final class Version20210522124633 extends AbstractMigration
|
||||
|
||||
public function isTransactional(): bool
|
||||
{
|
||||
return $this->connection->getDatabasePlatform()->getName() !== 'mysql';
|
||||
return ! ($this->connection->getDatabasePlatform() instanceof MySQLPlatform);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace ShlinkMigrations;
|
||||
|
||||
use Doctrine\DBAL\Platforms\MySQLPlatform;
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\DBAL\Schema\Table;
|
||||
use Doctrine\DBAL\Types\Types;
|
||||
@@ -41,6 +42,6 @@ final class Version20210720143824 extends AbstractMigration
|
||||
|
||||
public function isTransactional(): bool
|
||||
{
|
||||
return $this->connection->getDatabasePlatform()->getName() !== 'mysql';
|
||||
return ! ($this->connection->getDatabasePlatform() instanceof MySQLPlatform);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace ShlinkMigrations;
|
||||
|
||||
use Doctrine\DBAL\Platforms\MySQLPlatform;
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\DBAL\Types\Types;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
@@ -26,6 +27,6 @@ final class Version20211002072605 extends AbstractMigration
|
||||
|
||||
public function isTransactional(): bool
|
||||
{
|
||||
return $this->connection->getDatabasePlatform()->getName() !== 'mysql';
|
||||
return ! ($this->connection->getDatabasePlatform() instanceof MySQLPlatform);
|
||||
}
|
||||
}
|
||||
|
||||
73
data/migrations/Version20220110113313.php
Normal file
73
data/migrations/Version20220110113313.php
Normal file
@@ -0,0 +1,73 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace ShlinkMigrations;
|
||||
|
||||
use Doctrine\DBAL\Platforms\MySQLPlatform;
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
|
||||
final class Version20220110113313 extends AbstractMigration
|
||||
{
|
||||
private const CHARSET = 'utf8mb4';
|
||||
private const COLLATIONS = [
|
||||
'short_urls' => [
|
||||
'original_url' => 'unicode_ci',
|
||||
'short_code' => 'bin',
|
||||
'import_original_short_code' => 'unicode_ci',
|
||||
'title' => 'unicode_ci',
|
||||
],
|
||||
'domains' => [
|
||||
'authority' => 'unicode_ci',
|
||||
'base_url_redirect' => 'unicode_ci',
|
||||
'regular_not_found_redirect' => 'unicode_ci',
|
||||
'invalid_short_url_redirect' => 'unicode_ci',
|
||||
],
|
||||
'tags' => [
|
||||
'name' => 'unicode_ci',
|
||||
],
|
||||
'visits' => [
|
||||
'referer' => 'unicode_ci',
|
||||
'user_agent' => 'unicode_ci',
|
||||
'visited_url' => 'unicode_ci',
|
||||
],
|
||||
'visit_locations' => [
|
||||
'country_code' => 'unicode_ci',
|
||||
'country_name' => 'unicode_ci',
|
||||
'region_name' => 'unicode_ci',
|
||||
'city_name' => 'unicode_ci',
|
||||
'timezone' => 'unicode_ci',
|
||||
],
|
||||
];
|
||||
|
||||
public function up(Schema $schema): void
|
||||
{
|
||||
$this->skipIf(! $this->isMySql(), 'This only sets MySQL-specific database options');
|
||||
|
||||
foreach (self::COLLATIONS as $tableName => $columns) {
|
||||
$table = $schema->getTable($tableName);
|
||||
|
||||
foreach ($columns as $columnName => $collation) {
|
||||
$table->getColumn($columnName)
|
||||
->setPlatformOption('charset', self::CHARSET)
|
||||
->setPlatformOption('collation', self::CHARSET . '_' . $collation);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function down(Schema $schema): void
|
||||
{
|
||||
// No down
|
||||
}
|
||||
|
||||
public function isTransactional(): bool
|
||||
{
|
||||
return ! $this->isMySql();
|
||||
}
|
||||
|
||||
private function isMySql(): bool
|
||||
{
|
||||
return $this->connection->getDatabasePlatform() instanceof MySQLPlatform;
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace <namespace>;
|
||||
|
||||
use Doctrine\DBAL\Platforms\MySQLPlatform;
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
|
||||
@@ -21,6 +22,6 @@ final class <className> extends AbstractMigration
|
||||
|
||||
public function isTransactional(): bool
|
||||
{
|
||||
return $this->connection->getDatabasePlatform()->getName() !== 'mysql';
|
||||
return ! ($this->connection->getDatabasePlatform() instanceof MySQLPlatform);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,8 @@ services:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: ./data/infra/php.Dockerfile
|
||||
ports:
|
||||
- '8888:8888'
|
||||
volumes:
|
||||
- ./:/home/shlink/www
|
||||
- ./data/infra/php.ini:/usr/local/etc/php/php.ini
|
||||
@@ -98,7 +100,7 @@ services:
|
||||
|
||||
shlink_db_maria:
|
||||
container_name: shlink_db_maria
|
||||
image: mariadb:10.5
|
||||
image: mariadb:10.7
|
||||
ports:
|
||||
- "3308:3306"
|
||||
volumes:
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
This image provides an easy way to set up [shlink](https://shlink.io) on a container-based runtime.
|
||||
|
||||
It exposes a shlink instance served with [openswoole](https://www.swoole.co.uk/), which can be linked to external databases to persist data.
|
||||
It exposes a shlink instance served with [openswoole](https://openswoole.com/), which can be linked to external databases to persist data.
|
||||
|
||||
## Usage
|
||||
|
||||
|
||||
@@ -4,22 +4,13 @@ declare(strict_types=1);
|
||||
|
||||
namespace Shlinkio\Shlink;
|
||||
|
||||
use Monolog\Handler\StreamHandler;
|
||||
use Monolog\Logger;
|
||||
use Shlinkio\Shlink\Common\Logger\LoggerType;
|
||||
|
||||
return [
|
||||
|
||||
'logger' => [
|
||||
'Shlink' => [
|
||||
'handlers' => [
|
||||
'shlink_handler' => [
|
||||
'name' => StreamHandler::class,
|
||||
'params' => [
|
||||
'level' => Logger::INFO,
|
||||
'stream' => 'php://stdout',
|
||||
],
|
||||
],
|
||||
],
|
||||
'type' => LoggerType::STREAM->value,
|
||||
],
|
||||
],
|
||||
|
||||
|
||||
@@ -24,11 +24,9 @@ if [ ! -z "${GEOLITE_LICENSE_KEY}" ]; then
|
||||
php bin/cli visit:download-db -n ${flags}
|
||||
fi
|
||||
|
||||
# Periodicaly run visit:locate every hour
|
||||
# https://shlink.io/documentation/long-running-tasks/#locate-visits
|
||||
# set env var "ENABLE_PERIODIC_VISIT_LOCATE=true" to enable
|
||||
# Periodically run visit:locate every hour, if ENABLE_PERIODIC_VISIT_LOCATE=true was provided
|
||||
if [ $ENABLE_PERIODIC_VISIT_LOCATE ]; then
|
||||
echo "Configuring periodic visit locate..."
|
||||
echo "Configuring periodic visit location..."
|
||||
echo "0 * * * * php /etc/shlink/bin/cli visit:locate -q" > /etc/crontabs/root
|
||||
/usr/sbin/crond &
|
||||
fi
|
||||
|
||||
@@ -16,7 +16,7 @@ The intention is to implement a system that allows adding to API keys as many of
|
||||
|
||||
Supporting more restrictions in the future is also desirable.
|
||||
|
||||
## Considered option
|
||||
## Considered options
|
||||
|
||||
* Using an ACL/RBAC library, and checking roles in a middleware.
|
||||
* Using a service that, provided an API key, tells if certain resource is reachable while it also allows building queries dynamically.
|
||||
|
||||
@@ -11,7 +11,7 @@ However, it does not track visits to any of those, just to valid short URLs.
|
||||
|
||||
The intention is to change that, and allow users to track the cases mentioned above.
|
||||
|
||||
## Considered option
|
||||
## Considered options
|
||||
|
||||
* Create a new table to track visits o this kind.
|
||||
* Reuse the existing `visits` table, by making `short_url_id` nullable and adding a couple of other fields.
|
||||
|
||||
@@ -13,7 +13,7 @@ However, after the creation of the caching PSRs ([PSR-6 - Cache](https://www.php
|
||||
|
||||
Also, Shlink needs support for Redis clusters and Redis sentinels, which is not supported by `doctrine/cache` Redis adapters.
|
||||
|
||||
## Considered option
|
||||
## Considered options
|
||||
|
||||
After some research, the only packages that seem to support the capabilities required by Shlink and also seem healthy, are these:
|
||||
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
# Update env vars behavior to have precedence over installer options
|
||||
|
||||
* Status: Accepted
|
||||
* Date: 2022-01-15
|
||||
|
||||
## Context and problem statement
|
||||
|
||||
Shlink supports providing configuration via the installer tool that generates a config file that gets merged with the rest of the config, or via environment variables.
|
||||
|
||||
It is potentially possible to combine both, but if you do so, you will find out the installer tool config has precedence over env vars, which is not very intuitive.
|
||||
|
||||
A [Twitter survey](https://twitter.com/shlinkio/status/1480614855006732289) has also showed up all participants also found the behavior should be the opposite.
|
||||
|
||||
## Considered options
|
||||
|
||||
* Move the logic to read env vars to another config file which always overrides installer options.
|
||||
* Move the logic to read env vars to a config post-processor which overrides config dynamically, only if the appropriate env var had been defined.
|
||||
* Make the installer generate a config file which also includes the logic to load env vars on it.
|
||||
* Make the installer no longer generate the config structure, and instead generate a map with env vars and their values. Then Shlink would define those env vars if not defined already.
|
||||
|
||||
## Decision outcome
|
||||
|
||||
The most viable option was finally to re-think the installer tool, and make it generate a map of env vars and their values.
|
||||
|
||||
Then Shlink reads this as the first config file, which sets the values as env vars if not yet defined, and later on, the values are read as usual wherever needed.
|
||||
|
||||
## Pros and Cons of the Options
|
||||
|
||||
### Read all env vars in a single config file
|
||||
|
||||
* Bad: This option had to be discarded, as it would always override the installer config no matter what.
|
||||
|
||||
### Read all env vars in a config post-processor
|
||||
|
||||
* Good because it would not require any change in the installer.
|
||||
* Bad because it requires moving all env var reading logic somewhere else, while having it together with their contextual config is quite convenient.
|
||||
* Bad because it requires defining a map between the config path from the installer and the env var to set.
|
||||
|
||||
### Make the installer generate a config file which also reads env vars
|
||||
|
||||
* Good because it would not require changing Shlink.
|
||||
* Bad because it requires looking for a new way to generate the installer config.
|
||||
* Bad because it would mean reading the env vars in multiple places.
|
||||
|
||||
### Re-think the installer to no longer generate internal config, and instead, just define values for regular env vars
|
||||
|
||||
* Bad because it requires changes both in Shlink and the installer.
|
||||
* Bad because it's more error-prone, and the option with higher chances to introduce a regression.
|
||||
* Good because it finally decouples Shlink internal config (which is an implementation detail) from any external tool, including the installer, allowing to change it at will.
|
||||
* Good because it opens the door to eventually simplify the installer. For the moment, it requires a bit of extra logic to support importing the old config.
|
||||
* Good because it allows keeping the logic to read env vars next to the config where it applies.
|
||||
42
docs/adr/2022-08-05-support-multi-segment-custom-slugs.md
Normal file
42
docs/adr/2022-08-05-support-multi-segment-custom-slugs.md
Normal file
@@ -0,0 +1,42 @@
|
||||
# Support multi-segment custom slugs
|
||||
|
||||
* Status: Accepted
|
||||
* Date: 2022-08-05
|
||||
|
||||
## Context and problem statement
|
||||
|
||||
There's a new requirement to support multi-segment custom slugs (as in `https://exam.ple/foo/bar/baz`).
|
||||
|
||||
The internal router does not support this at the moment, as it only matches the shortCode in one of the segments.
|
||||
|
||||
## Considered options
|
||||
|
||||
* Tweak the internal router, so that it is capable of matching multiple segments for the slug, in every route that requires it.
|
||||
* Define a new set of routes with a short prefix that allows configuring multi-segment in those, without touching the existing routes.
|
||||
* Let the router fail, and use a middleware to fall back to the proper route (similar to what was done for the extra path forwarding feature).
|
||||
|
||||
## Decision outcome
|
||||
|
||||
Even though I was initially inclined to use a fallback middleware, that has turned out to be harder than anticipated, because there are several possible routes where the slug is used, and we would still need some kind of router to determine which one matches.
|
||||
|
||||
Because of that, the selected approach has been to tweak the existing router, so that it can match multiple segments, and moving the configuration of routes to a common place so that they can be defined in the proper order that prevents conflicts.
|
||||
|
||||
## Pros and Cons of the Options
|
||||
|
||||
### Tweaking the router
|
||||
|
||||
* Bad: It requires routes to be defined in a specific order, and remember it in the future if more routes are added.
|
||||
* Good: It initially requires fewer changes.
|
||||
* Good: Once routes are defined in the proper order, all the internal logic works out of the box.
|
||||
|
||||
### Defining new routes
|
||||
|
||||
* Bad: The end-user experience gets affected.
|
||||
* Bad: Probably a lot of side effects would happen when it comes to assembling short URLs.
|
||||
* Bad: Routing needs to be configured twice, resolving the same logic.
|
||||
* Bad: It turns out to still conflict with some routes, even with the prefix, which defeats what looked like its main benefit.
|
||||
|
||||
### Let routing fail and fall back in middleware
|
||||
|
||||
* Good: Does not require changing routes configuration, which means less side effects.
|
||||
* Bad: Since many routes can potentially end up in the middleware, there's still the need to have some kind of routing logic.
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
Here listed you will find the different architectural decisions taken in the project, including all the reasoning behind it, options considered, and final outcome.
|
||||
|
||||
* [2022-08-05 Support multi-segment custom slugs](2022-08-05-support-multi-segment-custom-slugs.md)
|
||||
* [2022-01-15 Update env vars behavior to have precedence over installer options](2022-01-15-update-env-vars-behavior-to-have-precedence-over-installer-options.md)
|
||||
* [2021-08-05 Migrate to a new caching library](2021-08-05-migrate-to-a-new-caching-library.md)
|
||||
* [2021-02-07 Track visits to 'base_url', 'invalid_short_url' and 'regular_404'](2021-02-07-track-visits-to-base-url-invalid-short-url-and-regular-404.md)
|
||||
* [2021-01-17 Support restrictions and permissions in API keys](2021-01-17-support-restrictions-and-permissions-in-api-keys.md)
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"asyncapi": "2.0.0",
|
||||
"asyncapi": "2.4.0",
|
||||
"info": {
|
||||
"title": "Shlink",
|
||||
"version": "2.0.0",
|
||||
"version": "3.0.0",
|
||||
"description": "Shlink, the self-hosted URL shortener",
|
||||
"license": {
|
||||
"name": "MIT",
|
||||
@@ -75,6 +75,23 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"https://shlink.io/new-short-url": {
|
||||
"subscribe": {
|
||||
"summary": "Receive information about any new short URL.",
|
||||
"operationId": "newshortUrl",
|
||||
"message": {
|
||||
"payload": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"shortUrl": {
|
||||
"$ref": "#/components/schemas/ShortUrl"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"components": {
|
||||
@@ -101,7 +118,7 @@
|
||||
},
|
||||
"visitsCount": {
|
||||
"type": "integer",
|
||||
"description": "The number of visits that this short URL has recieved."
|
||||
"description": "The number of visits that this short URL has received."
|
||||
},
|
||||
"tags": {
|
||||
"type": "array",
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
},
|
||||
"visitsCount": {
|
||||
"type": "integer",
|
||||
"description": "The number of visits that this short URL has recieved."
|
||||
"description": "The number of visits that this short URL has received."
|
||||
},
|
||||
"tags": {
|
||||
"type": "array",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"value": {
|
||||
"detail":"No URL found with short code \"abc123\"",
|
||||
"title":"Short URL not found",
|
||||
"detail": "No URL found with short code \"abc123\"",
|
||||
"title": "Short URL not found",
|
||||
"type": "INVALID_SHORTCODE",
|
||||
"status": 404,
|
||||
"shortCode": "abc123"
|
||||
|
||||
@@ -49,10 +49,20 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "tagsMode",
|
||||
"in": "query",
|
||||
"description": "Tells how the filtering by tags should work, returning short URLs containing \"any\" of the tags, or \"all\" the tags. It's ignored if no tags are provided, and defaults to \"any\" if not provided.",
|
||||
"required": false,
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"enum": ["any", "all"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "orderBy",
|
||||
"in": "query",
|
||||
"description": "The field from which you want to order the result. (Since v1.3.0)",
|
||||
"description": "The field from which you want to order the result.",
|
||||
"required": false,
|
||||
"schema": {
|
||||
"type": "string",
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user