mirror of
https://github.com/shlinkio/shlink.git
synced 2026-02-28 12:13:13 +08:00
Compare commits
355 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
02e48ae665 | ||
|
|
0d627ce808 | ||
|
|
99639b9844 | ||
|
|
0c3c7ff3b2 | ||
|
|
d7423585ff | ||
|
|
7de07a9cd4 | ||
|
|
2a734b5d89 | ||
|
|
4520afb271 | ||
|
|
e7a9ad1db0 | ||
|
|
84860539ce | ||
|
|
2901fe8b7b | ||
|
|
f9694333c5 | ||
|
|
fc1f35ad59 | ||
|
|
9a58748581 | ||
|
|
45e108d21e | ||
|
|
f4da9c1fcc | ||
|
|
a3ea8f56dd | ||
|
|
f3244b35e3 | ||
|
|
442eea0ea7 | ||
|
|
46601443f5 | ||
|
|
c0200317dd | ||
|
|
c8e5196aab | ||
|
|
b991b1699e | ||
|
|
582033ceb3 | ||
|
|
549a8d8837 | ||
|
|
5fb6c8708c | ||
|
|
7ee757243a | ||
|
|
044efe6ee4 | ||
|
|
9b16749737 | ||
|
|
6d51ff831f | ||
|
|
0635615149 | ||
|
|
51de4b17c0 | ||
|
|
615b443652 | ||
|
|
4b7b530f49 | ||
|
|
fa7969c746 | ||
|
|
aef04af4f0 | ||
|
|
f118ea252c | ||
|
|
d514f39a82 | ||
|
|
e17556a7ae | ||
|
|
d79f11eeb8 | ||
|
|
1ec950ee1e | ||
|
|
14ba9fd6a4 | ||
|
|
83e8801827 | ||
|
|
be822646e4 | ||
|
|
3a4a27a60c | ||
|
|
1773e6ecae | ||
|
|
a8e4b2fceb | ||
|
|
15b53ef43c | ||
|
|
11a4702b10 | ||
|
|
6b15cd6d51 | ||
|
|
00169a5729 | ||
|
|
94702791d9 | ||
|
|
447cccacdf | ||
|
|
0413399102 | ||
|
|
9afc7876c4 | ||
|
|
187c17319a | ||
|
|
7310ecd886 | ||
|
|
620cd92d11 | ||
|
|
f9658c8da1 | ||
|
|
613b1d3045 | ||
|
|
d39711ec51 | ||
|
|
69dcab96f8 | ||
|
|
d76c96ad41 | ||
|
|
133efff2cd | ||
|
|
c10f0db170 | ||
|
|
037cd8a389 | ||
|
|
1d24750f43 | ||
|
|
b52ceaff9a | ||
|
|
6b0b52853c | ||
|
|
64d7ac7093 | ||
|
|
b9ba1246d4 | ||
|
|
7f9dc10f6a | ||
|
|
a1afc90150 | ||
|
|
df94c68e2e | ||
|
|
65ea1e00a6 | ||
|
|
5bccdded8a | ||
|
|
8917ed5c2e | ||
|
|
fabc752398 | ||
|
|
38d8086516 | ||
|
|
ae0ff5f23c | ||
|
|
7c659699f3 | ||
|
|
9e6cdcb838 | ||
|
|
7e2f755dfd | ||
|
|
ce2ed237c7 | ||
|
|
626caa4afa | ||
|
|
f4a7712ded | ||
|
|
bab6a3951e | ||
|
|
f49d98f2ea | ||
|
|
1312ea61f4 | ||
|
|
8d90661d0a | ||
|
|
b6b2530cb6 | ||
|
|
e4f66b7ce6 | ||
|
|
4b52c92e97 | ||
|
|
76c42bc17c | ||
|
|
c4f8da5f02 | ||
|
|
80bdeb280a | ||
|
|
99010b6eae | ||
|
|
b2dabf06bf | ||
|
|
67ae05f473 | ||
|
|
fb4fecf411 | ||
|
|
c855f011d1 | ||
|
|
02717eb2fb | ||
|
|
de70ebe769 | ||
|
|
c6109fd396 | ||
|
|
83584a3175 | ||
|
|
f5dcc52b3b | ||
|
|
1901964de1 | ||
|
|
80e9c2452b | ||
|
|
5ad4b39160 | ||
|
|
89b73a9cfa | ||
|
|
e2d8334d69 | ||
|
|
9b16d7acc0 | ||
|
|
6836840746 | ||
|
|
4084d301ca | ||
|
|
added21b18 | ||
|
|
8cd77391cc | ||
|
|
05ebfccc63 | ||
|
|
cb3a690294 | ||
|
|
194a7b0e57 | ||
|
|
98e4d01feb | ||
|
|
c22e3895b5 | ||
|
|
9a76c19615 | ||
|
|
59fa088975 | ||
|
|
163244f40f | ||
|
|
a89b53af4f | ||
|
|
35508e253d | ||
|
|
e586fec338 | ||
|
|
93fa27bdba | ||
|
|
048856c333 | ||
|
|
986f1162dd | ||
|
|
dc8dfa9f0c | ||
|
|
82e7094f3a | ||
|
|
f0e62004d5 | ||
|
|
bbdbafd8db | ||
|
|
6121efec59 | ||
|
|
4fdbcc25a0 | ||
|
|
ca42425b33 | ||
|
|
ce0f61b66d | ||
|
|
13ee71f351 | ||
|
|
c57494d7cd | ||
|
|
d2e74ab330 | ||
|
|
850dde1a06 | ||
|
|
5e83f301ff | ||
|
|
5e74dd7a6d | ||
|
|
8a273e01e9 | ||
|
|
75f6f8dd18 | ||
|
|
e1cf0c4ea7 | ||
|
|
cc134abd12 | ||
|
|
b7db676cba | ||
|
|
3881996560 | ||
|
|
527d28ad81 | ||
|
|
f2371e8a80 | ||
|
|
fd882834d3 | ||
|
|
f92a720d63 | ||
|
|
d6f58698b7 | ||
|
|
d090260b17 | ||
|
|
cd43c1c01f | ||
|
|
284b28e8d9 | ||
|
|
b50547d868 | ||
|
|
401046fbe5 | ||
|
|
6e82509964 | ||
|
|
ab6fa490e5 | ||
|
|
55e2780f50 | ||
|
|
f4803c675c | ||
|
|
90514c603f | ||
|
|
7f4137e7cc | ||
|
|
071cb9af2b | ||
|
|
6ce1550457 | ||
|
|
8cb5d44dc9 | ||
|
|
1331b3f87c | ||
|
|
ab96297e58 | ||
|
|
c4fd3a74c5 | ||
|
|
da922fb2a7 | ||
|
|
4a05c4be40 | ||
|
|
cef30c8e2d | ||
|
|
8417498f08 | ||
|
|
10e941cea6 | ||
|
|
3d7b1ca799 | ||
|
|
b236354fc7 | ||
|
|
6fbb5a380d | ||
|
|
054eb42613 | ||
|
|
6074f4475d | ||
|
|
7afd3fd6a2 | ||
|
|
7d415e40b2 | ||
|
|
3c89d252d2 | ||
|
|
f678873e9f | ||
|
|
17d37a062a | ||
|
|
14702063f2 | ||
|
|
c599d8a0ed | ||
|
|
207d5adceb | ||
|
|
b4c46ce222 | ||
|
|
6fe269193a | ||
|
|
d948543d5c | ||
|
|
a327e6c0a7 | ||
|
|
fbd35b7974 | ||
|
|
b94a22e6a7 | ||
|
|
63ea9e4a21 | ||
|
|
e028d8ea31 | ||
|
|
457a7a14e5 | ||
|
|
cd387328be | ||
|
|
5524476787 | ||
|
|
78526fb405 | ||
|
|
b2dee43bb0 | ||
|
|
60e9443b12 | ||
|
|
ab8fa52ca4 | ||
|
|
16f64f6247 | ||
|
|
98992c656f | ||
|
|
053e026982 | ||
|
|
74180a4381 | ||
|
|
293725f933 | ||
|
|
c33f8d0ea2 | ||
|
|
0f2cd3cb7f | ||
|
|
2441ac5e77 | ||
|
|
f248001460 | ||
|
|
1fe2c93946 | ||
|
|
a3d50605c1 | ||
|
|
5427152f15 | ||
|
|
a4e9c2fdde | ||
|
|
e244b2dc51 | ||
|
|
31dea8fa99 | ||
|
|
be8cf56240 | ||
|
|
0bc7412430 | ||
|
|
6d56e92306 | ||
|
|
97c94f8fcc | ||
|
|
92b5a5296d | ||
|
|
febca6d441 | ||
|
|
bf29abc468 | ||
|
|
97cb30565c | ||
|
|
9809f050ef | ||
|
|
7ecfb24584 | ||
|
|
4aa65f750e | ||
|
|
63c533fa62 | ||
|
|
8751d6c315 | ||
|
|
eb40dc2d5d | ||
|
|
c9d1a955b9 | ||
|
|
c346fd0602 | ||
|
|
a45550b0c6 | ||
|
|
a843c59d77 | ||
|
|
3bfb29a51c | ||
|
|
d8ede3263f | ||
|
|
c36e43e249 | ||
|
|
52150b3067 | ||
|
|
e7796cc917 | ||
|
|
7f560e6a65 | ||
|
|
8f233221e5 | ||
|
|
f700abd65d | ||
|
|
f9e4d6d617 | ||
|
|
d9286765e1 | ||
|
|
a7cde9364a | ||
|
|
070d74830b | ||
|
|
23c07c4e82 | ||
|
|
ab7824aa85 | ||
|
|
67bafbe44e | ||
|
|
c4805b8152 | ||
|
|
33729289c7 | ||
|
|
721e3d9ef9 | ||
|
|
a72e22e046 | ||
|
|
36749658da | ||
|
|
4ad3dc0bc7 | ||
|
|
73864b923d | ||
|
|
71277e979a | ||
|
|
60fef3de74 | ||
|
|
0fe503fa0e | ||
|
|
db02d9f1ba | ||
|
|
89a987d03a | ||
|
|
3284cea6f2 | ||
|
|
df5ad554c1 | ||
|
|
07ae92943d | ||
|
|
175712d4a9 | ||
|
|
3f1b253c31 | ||
|
|
202d0b86b3 | ||
|
|
4e87affb0b | ||
|
|
7f83d37b3c | ||
|
|
09e81b00c5 | ||
|
|
68b77e22c5 | ||
|
|
c5ddd8302a | ||
|
|
1a0fe0429a | ||
|
|
6646232311 | ||
|
|
c1e88c3e83 | ||
|
|
c91a534d1a | ||
|
|
752100f1ce | ||
|
|
dae083c540 | ||
|
|
857c3a4f8d | ||
|
|
acc4c4756e | ||
|
|
0bacb215c5 | ||
|
|
d1a6e60b01 | ||
|
|
8f954151ca | ||
|
|
145d4eaaed | ||
|
|
7673232793 | ||
|
|
f08951a9b9 | ||
|
|
ff963a9df4 | ||
|
|
f30c74b987 | ||
|
|
467dbdd183 | ||
|
|
0e78deb8f2 | ||
|
|
50cc7ae632 | ||
|
|
512d765d60 | ||
|
|
7b9331bd14 | ||
|
|
4f5ce9fb43 | ||
|
|
83f73eb631 | ||
|
|
3f1b89d665 | ||
|
|
8f6fc97fc8 | ||
|
|
a463e6f9d7 | ||
|
|
2a0364ca8f | ||
|
|
23e9ed93bb | ||
|
|
689343d1c9 | ||
|
|
d01dc334d7 | ||
|
|
58a3791a5c | ||
|
|
1a133af141 | ||
|
|
938fb6509e | ||
|
|
d3bfd99210 | ||
|
|
3a1740fdca | ||
|
|
e3de403c6c | ||
|
|
5c1ab02753 | ||
|
|
e5713df008 | ||
|
|
95ea64980b | ||
|
|
c0a77b790d | ||
|
|
e073b4331a | ||
|
|
e919901487 | ||
|
|
13f9f106be | ||
|
|
e9c7053ef5 | ||
|
|
62051c8809 | ||
|
|
0a6a794e23 | ||
|
|
01846657d1 | ||
|
|
dd7545afdf | ||
|
|
9296013596 | ||
|
|
8015c6cc88 | ||
|
|
8c93444286 | ||
|
|
96ed7cae0d | ||
|
|
72c4628b79 | ||
|
|
1117631717 | ||
|
|
60176060cb | ||
|
|
d949b54ef4 | ||
|
|
720db64a03 | ||
|
|
37e0978bfc | ||
|
|
cf355b0b69 | ||
|
|
f2edb54b8b | ||
|
|
13ec27039d | ||
|
|
ad3805a560 | ||
|
|
cc4afa7b62 | ||
|
|
7a6bfed445 | ||
|
|
f2a7b687a9 | ||
|
|
522d021264 | ||
|
|
14a0db1f34 | ||
|
|
430883987a | ||
|
|
f17b641d46 | ||
|
|
48a8290e92 | ||
|
|
46acf4de1c | ||
|
|
17792a1603 | ||
|
|
a8611f5d80 | ||
|
|
deef938e97 | ||
|
|
e014cfa72a | ||
|
|
aa242eba25 | ||
|
|
0ac5569d60 | ||
|
|
7c3e3442c2 | ||
|
|
0f894dcdfe |
@@ -1,5 +1,6 @@
|
||||
bin/rr
|
||||
config/autoload/*local*
|
||||
config/params/shlink_dev_env.*
|
||||
data/infra
|
||||
data/cache/*
|
||||
data/log/*
|
||||
@@ -19,7 +20,6 @@ indocker
|
||||
docker-*
|
||||
phpstan.neon
|
||||
php*xml*
|
||||
infection*
|
||||
**/test*
|
||||
build*
|
||||
**/.*
|
||||
|
||||
1
.gitattributes
vendored
1
.gitattributes
vendored
@@ -13,7 +13,6 @@
|
||||
.travis.yml export-ignore
|
||||
build.sh export-ignore
|
||||
CHANGELOG.md export-ignore
|
||||
docker-compose.override.yml.dist export-ignore
|
||||
docker-compose.yml export-ignore
|
||||
indocker export-ignore
|
||||
phpcs.xml export-ignore
|
||||
|
||||
4
.github/DISCUSSION_TEMPLATE/help-wanted.yml
vendored
4
.github/DISCUSSION_TEMPLATE/help-wanted.yml
vendored
@@ -20,10 +20,8 @@ body:
|
||||
options:
|
||||
- Self-hosted Apache
|
||||
- Self-hosted nginx
|
||||
- Self-hosted openswoole
|
||||
- Self-hosted RoadRunner
|
||||
- Openswoole Docker image
|
||||
- RoadRunner Docker image
|
||||
- Docker image
|
||||
- Other (explain in summary)
|
||||
- type: dropdown
|
||||
validations:
|
||||
|
||||
2
.github/FUNDING.yml
vendored
2
.github/FUNDING.yml
vendored
@@ -1,2 +1,2 @@
|
||||
github: ['acelaya']
|
||||
custom: ['https://acel.me/donate']
|
||||
custom: ['https://slnk.to/donate']
|
||||
|
||||
17
.github/ISSUE_TEMPLATE/Bug.yml
vendored
17
.github/ISSUE_TEMPLATE/Bug.yml
vendored
@@ -22,10 +22,8 @@ body:
|
||||
options:
|
||||
- Self-hosted Apache
|
||||
- Self-hosted nginx
|
||||
- Self-hosted openswoole
|
||||
- Self-hosted RoadRunner
|
||||
- Openswoole Docker image
|
||||
- RoadRunner Docker image
|
||||
- Docker image
|
||||
- Other (explain in summary)
|
||||
- type: dropdown
|
||||
validations:
|
||||
@@ -60,5 +58,14 @@ body:
|
||||
validations:
|
||||
required: true
|
||||
attributes:
|
||||
label: How to reproduce
|
||||
value: '<!-- Provide steps to reproduce the bug. -->'
|
||||
label: Minimum steps to reproduce
|
||||
value: |
|
||||
<!--
|
||||
Simple but detailed way to reproduce the bug:
|
||||
|
||||
* Avoid things like "create a kubernetes cluster", or anything related with cloud providers, as that is rarely the root cause.
|
||||
* Avoid too vague steps or one-liners like "Update from v1 to v2".
|
||||
* Providing the reproduction in the form of a self-contained docker-composer is desirable.
|
||||
|
||||
Failing in any of these will cause the issue to be closed as "not reproducible".
|
||||
-->
|
||||
|
||||
10
.github/actions/ci-setup/action.yml
vendored
10
.github/actions/ci-setup/action.yml
vendored
@@ -12,7 +12,6 @@ inputs:
|
||||
php-extensions:
|
||||
description: 'The PHP extensions to install'
|
||||
required: false
|
||||
default: ''
|
||||
extensions-cache-key:
|
||||
description: 'The key used to cache PHP extensions. If empty value is provided, extension caching is disabled'
|
||||
required: true
|
||||
@@ -21,6 +20,7 @@ runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Setup cache environment
|
||||
if: ${{ inputs.php-extensions }}
|
||||
id: extcache
|
||||
uses: shivammathur/cache-extensions@v1
|
||||
with:
|
||||
@@ -28,7 +28,8 @@ runs:
|
||||
extensions: ${{ inputs.php-extensions }}
|
||||
key: ${{ inputs.extensions-cache-key }}
|
||||
- name: Cache extensions
|
||||
uses: actions/cache@v3
|
||||
if: ${{ inputs.php-extensions }}
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ${{ steps.extcache.outputs.dir }}
|
||||
key: ${{ steps.extcache.outputs.key }}
|
||||
@@ -39,9 +40,8 @@ runs:
|
||||
php-version: ${{ inputs.php-version }}
|
||||
tools: composer
|
||||
extensions: ${{ inputs.php-extensions }}
|
||||
coverage: pcov
|
||||
ini-values: pcov.directory=module
|
||||
coverage: xdebug
|
||||
- name: Install dependencies
|
||||
if: ${{ inputs.install-deps == 'yes' }}
|
||||
run: composer install --no-interaction --prefer-dist ${{ inputs.php-version == '8.3' && '--ignore-platform-reqs' || '' }}
|
||||
run: composer install --no-interaction --prefer-dist ${{ inputs.php-version == '8.4' && '--ignore-platform-req=php' || '' }}
|
||||
shell: bash
|
||||
|
||||
14
.github/workflows/ci-db-tests.yml
vendored
14
.github/workflows/ci-db-tests.yml
vendored
@@ -10,11 +10,11 @@ on:
|
||||
|
||||
jobs:
|
||||
db-tests:
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-24.04
|
||||
strategy:
|
||||
matrix:
|
||||
php-version: ['8.2', '8.3']
|
||||
continue-on-error: ${{ matrix.php-version == '8.3' }}
|
||||
php-version: ['8.2', '8.3', '8.4']
|
||||
continue-on-error: ${{ matrix.php-version == '8.4' }}
|
||||
env:
|
||||
LC_ALL: C
|
||||
steps:
|
||||
@@ -24,20 +24,20 @@ jobs:
|
||||
run: sudo ./data/infra/ci/install-ms-odbc.sh
|
||||
- name: Start database server
|
||||
if: ${{ inputs.platform != 'sqlite:ci' }}
|
||||
run: docker-compose -f docker-compose.yml -f docker-compose.ci.yml up -d shlink_db_${{ inputs.platform }}
|
||||
run: docker compose -f docker-compose.yml -f docker-compose.ci.yml up -d shlink_db_${{ inputs.platform }}
|
||||
- uses: './.github/actions/ci-setup'
|
||||
with:
|
||||
php-version: ${{ matrix.php-version }}
|
||||
php-extensions: openswoole-22.1.0, pdo_sqlsrv-5.11.1
|
||||
php-extensions: pdo_sqlsrv-5.12.0
|
||||
extensions-cache-key: db-tests-extensions-${{ matrix.php-version }}-${{ inputs.platform }}
|
||||
- name: Create test database
|
||||
if: ${{ inputs.platform == 'ms' }}
|
||||
run: docker-compose exec -T shlink_db_ms /opt/mssql-tools/bin/sqlcmd -S localhost -U sa -P 'Passw0rd!' -Q "CREATE DATABASE shlink_test;"
|
||||
run: docker compose exec -T shlink_db_ms /opt/mssql-tools18/bin/sqlcmd -C -S localhost -U sa -P 'Passw0rd!' -Q "CREATE DATABASE shlink_test;"
|
||||
- name: Run tests
|
||||
run: composer test:db:${{ inputs.platform }}
|
||||
- name: Upload code coverage
|
||||
uses: actions/upload-artifact@v4
|
||||
if: ${{ matrix.php-version == '8.2' && inputs.platform == 'sqlite:ci' }}
|
||||
if: ${{ matrix.php-version == '8.3' && inputs.platform == 'sqlite:ci' }}
|
||||
with:
|
||||
name: coverage-db
|
||||
path: |
|
||||
|
||||
8
.github/workflows/ci-docker-image-build.yml
vendored
8
.github/workflows/ci-docker-image-build.yml
vendored
@@ -1,4 +1,4 @@
|
||||
name: Build docker image
|
||||
name: Test docker image build
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
@@ -7,8 +7,4 @@ on:
|
||||
|
||||
jobs:
|
||||
build-docker-image:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
- run: docker build -t shlink-docker-image:temp .
|
||||
uses: shlinkio/github-actions/.github/workflows/docker-image-build-ci.yml@main
|
||||
|
||||
46
.github/workflows/ci-mutation-tests.yml
vendored
46
.github/workflows/ci-mutation-tests.yml
vendored
@@ -1,46 +0,0 @@
|
||||
name: Mutation tests
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
test-group:
|
||||
type: string
|
||||
required: true
|
||||
description: One of unit, db, api or cli
|
||||
|
||||
jobs:
|
||||
mutation-tests:
|
||||
runs-on: ubuntu-22.04
|
||||
strategy:
|
||||
matrix:
|
||||
php-version: ['8.2', '8.3']
|
||||
continue-on-error: ${{ matrix.php-version == '8.3' }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: './.github/actions/ci-setup'
|
||||
with:
|
||||
php-version: ${{ matrix.php-version }}
|
||||
php-extensions: openswoole-22.1.0
|
||||
extensions-cache-key: mutation-tests-extensions-${{ matrix.php-version }}-${{ inputs.test-group }}
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: coverage-${{ inputs.test-group }}
|
||||
path: build
|
||||
- name: Resolve infection args
|
||||
id: infection_args
|
||||
run: echo "args=--logger-github=false" >> $GITHUB_OUTPUT
|
||||
# TODO Try to filter mutation tests to improve execution times. Investigate why --git-diff-lines --git-diff-base=develop does not work
|
||||
# run: |
|
||||
# BRANCH="${GITHUB_REF#refs/heads/}" |
|
||||
# if [[ $BRANCH == 'main' || $BRANCH == 'develop' ]]; then
|
||||
# echo "args=--logger-github=false" >> $GITHUB_OUTPUT
|
||||
# else
|
||||
# echo "args=--logger-github=false --git-diff-lines --git-diff-base=develop" >> $GITHUB_OUTPUT
|
||||
# fi;
|
||||
shell: bash
|
||||
- if: ${{ inputs.test-group == 'unit' }}
|
||||
run: composer infect:ci:unit -- ${{ steps.infection_args.outputs.args }}
|
||||
env:
|
||||
INFECTION_BADGE_API_KEY: ${{ secrets.INFECTION_BADGE_API_KEY }}
|
||||
- if: ${{ inputs.test-group != 'unit' }}
|
||||
run: composer infect:ci:${{ inputs.test-group }} -- ${{ steps.infection_args.outputs.args }}
|
||||
18
.github/workflows/ci-tests.yml
vendored
18
.github/workflows/ci-tests.yml
vendored
@@ -10,27 +10,31 @@ on:
|
||||
|
||||
jobs:
|
||||
tests:
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-24.04
|
||||
strategy:
|
||||
matrix:
|
||||
php-version: ['8.2', '8.3']
|
||||
continue-on-error: ${{ matrix.php-version == '8.3' }}
|
||||
php-version: ['8.2', '8.3', '8.4']
|
||||
continue-on-error: ${{ matrix.php-version == '8.4' }}
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # rr get-binary picks this env automatically
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Start postgres database server
|
||||
if: ${{ inputs.test-group == 'api' }}
|
||||
run: docker-compose -f docker-compose.yml -f docker-compose.ci.yml up -d shlink_db_postgres
|
||||
run: docker compose -f docker-compose.yml -f docker-compose.ci.yml up -d shlink_db_postgres
|
||||
- name: Start maria database server
|
||||
if: ${{ inputs.test-group == 'cli' }}
|
||||
run: docker-compose -f docker-compose.yml -f docker-compose.ci.yml up -d shlink_db_maria
|
||||
run: docker compose -f docker-compose.yml -f docker-compose.ci.yml up -d shlink_db_maria
|
||||
- uses: './.github/actions/ci-setup'
|
||||
with:
|
||||
php-version: ${{ matrix.php-version }}
|
||||
php-extensions: openswoole-22.1.0
|
||||
extensions-cache-key: tests-extensions-${{ matrix.php-version }}-${{ inputs.test-group }}
|
||||
- name: Download RoadRunner binary
|
||||
if: ${{ inputs.test-group == 'api' }}
|
||||
run: ./vendor/bin/rr get --no-interaction --no-config --location bin/ && chmod +x bin/rr
|
||||
- run: composer test:${{ inputs.test-group }}:ci
|
||||
- uses: actions/upload-artifact@v4
|
||||
if: ${{ matrix.php-version == '8.2' }}
|
||||
if: ${{ matrix.php-version == '8.3' }}
|
||||
with:
|
||||
name: coverage-${{ inputs.test-group }}
|
||||
path: |
|
||||
|
||||
103
.github/workflows/ci.yml
vendored
103
.github/workflows/ci.yml
vendored
@@ -8,7 +8,6 @@ on:
|
||||
- '*.md'
|
||||
- '*.xml'
|
||||
- '*.yml*'
|
||||
- '*.json5'
|
||||
- '*.neon'
|
||||
push:
|
||||
branches:
|
||||
@@ -21,22 +20,20 @@ on:
|
||||
- '*.md'
|
||||
- '*.xml'
|
||||
- '*.yml*'
|
||||
- '*.json5'
|
||||
- '*.neon'
|
||||
|
||||
jobs:
|
||||
static-analysis:
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-24.04
|
||||
strategy:
|
||||
matrix:
|
||||
php-version: ['8.2']
|
||||
php-version: ['8.3']
|
||||
command: ['cs', 'stan', 'swagger:validate']
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: './.github/actions/ci-setup'
|
||||
with:
|
||||
php-version: ${{ matrix.php-version }}
|
||||
php-extensions: openswoole-22.1.0
|
||||
extensions-cache-key: tests-extensions-${{ matrix.php-version }}-${{ matrix.command }}
|
||||
- run: composer ${{ matrix.command }}
|
||||
|
||||
@@ -50,102 +47,37 @@ jobs:
|
||||
with:
|
||||
test-group: cli
|
||||
|
||||
openswoole-api-tests:
|
||||
api-tests:
|
||||
uses: './.github/workflows/ci-tests.yml'
|
||||
with:
|
||||
test-group: api
|
||||
|
||||
roadrunner-api-tests:
|
||||
runs-on: ubuntu-22.04
|
||||
db-tests:
|
||||
strategy:
|
||||
matrix:
|
||||
php-version: ['8.2', '8.3']
|
||||
continue-on-error: ${{ matrix.php-version == '8.3' }}
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # rr get-binary picks this env automatically
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- run: docker-compose -f docker-compose.yml -f docker-compose.ci.yml up -d shlink_db_postgres
|
||||
- uses: shivammathur/setup-php@v2
|
||||
with:
|
||||
php-version: ${{ matrix.php-version }}
|
||||
tools: composer
|
||||
- run: composer install --no-interaction --prefer-dist --ignore-platform-req=ext-openswoole ${{ matrix.php-version == '8.3' && '--ignore-platform-reqs' || '' }}
|
||||
- run: ./vendor/bin/rr get --no-interaction --no-config --location bin/ && chmod +x bin/rr
|
||||
- run: composer test:api:rr
|
||||
|
||||
sqlite-db-tests:
|
||||
platform: ['sqlite:ci', 'mysql', 'maria', 'postgres', 'ms']
|
||||
uses: './.github/workflows/ci-db-tests.yml'
|
||||
with:
|
||||
platform: 'sqlite:ci'
|
||||
|
||||
mysql-db-tests:
|
||||
uses: './.github/workflows/ci-db-tests.yml'
|
||||
with:
|
||||
platform: 'mysql'
|
||||
|
||||
maria-db-tests:
|
||||
uses: './.github/workflows/ci-db-tests.yml'
|
||||
with:
|
||||
platform: 'maria'
|
||||
|
||||
postgres-db-tests:
|
||||
uses: './.github/workflows/ci-db-tests.yml'
|
||||
with:
|
||||
platform: 'postgres'
|
||||
|
||||
ms-db-tests:
|
||||
uses: './.github/workflows/ci-db-tests.yml'
|
||||
with:
|
||||
platform: 'ms'
|
||||
|
||||
unit-mutation-tests:
|
||||
needs:
|
||||
- unit-tests
|
||||
uses: './.github/workflows/ci-mutation-tests.yml'
|
||||
with:
|
||||
test-group: unit
|
||||
|
||||
db-mutation-tests:
|
||||
needs:
|
||||
- sqlite-db-tests
|
||||
uses: './.github/workflows/ci-mutation-tests.yml'
|
||||
with:
|
||||
test-group: db
|
||||
|
||||
api-mutation-tests:
|
||||
needs:
|
||||
- openswoole-api-tests
|
||||
uses: './.github/workflows/ci-mutation-tests.yml'
|
||||
with:
|
||||
test-group: api
|
||||
|
||||
cli-mutation-tests:
|
||||
needs:
|
||||
- cli-tests
|
||||
uses: './.github/workflows/ci-mutation-tests.yml'
|
||||
with:
|
||||
test-group: cli
|
||||
platform: ${{ matrix.platform }}
|
||||
|
||||
upload-coverage:
|
||||
needs:
|
||||
- unit-tests
|
||||
- openswoole-api-tests
|
||||
- api-tests
|
||||
- cli-tests
|
||||
- sqlite-db-tests
|
||||
runs-on: ubuntu-22.04
|
||||
- db-tests
|
||||
runs-on: ubuntu-24.04
|
||||
strategy:
|
||||
matrix:
|
||||
php-version: ['8.2']
|
||||
php-version: ['8.3']
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
- name: Use PHP
|
||||
uses: shivammathur/setup-php@v2
|
||||
uses: './.github/actions/ci-setup'
|
||||
with:
|
||||
php-version: ${{ matrix.php-version }}
|
||||
coverage: pcov
|
||||
ini-values: pcov.directory=module
|
||||
extensions-cache-key: tests-extensions-${{ matrix.php-version }}
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
path: build
|
||||
@@ -153,21 +85,16 @@ jobs:
|
||||
- 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: mv build/coverage-cli/coverage-cli.cov build/coverage-cli.cov
|
||||
- run: wget https://phar.phpunit.de/phpcov-9.0.0.phar
|
||||
- run: php phpcov-9.0.0.phar merge build --clover build/clover.xml
|
||||
- run: vendor/bin/phpcov merge build --clover build/clover.xml
|
||||
- name: Publish coverage
|
||||
uses: codecov/codecov-action@v1
|
||||
uses: codecov/codecov-action@v4
|
||||
with:
|
||||
file: ./build/clover.xml
|
||||
|
||||
delete-artifacts:
|
||||
needs:
|
||||
- unit-mutation-tests
|
||||
- db-mutation-tests
|
||||
- api-mutation-tests
|
||||
- cli-mutation-tests
|
||||
- upload-coverage
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: geekyeggo/delete-artifact@v2
|
||||
with:
|
||||
|
||||
10
.github/workflows/publish-docker-image.yml
vendored
10
.github/workflows/publish-docker-image.yml
vendored
@@ -15,14 +15,7 @@ jobs:
|
||||
- runtime: 'rr'
|
||||
tag-suffix: 'roadrunner'
|
||||
platforms: 'linux/arm64/v8,linux/amd64'
|
||||
- runtime: 'openswoole'
|
||||
tag-suffix: 'openswoole'
|
||||
platforms: 'linux/arm/v7,linux/arm64/v8,linux/amd64'
|
||||
- runtime: 'rr'
|
||||
tag-suffix: 'non-root'
|
||||
platforms: 'linux/arm64/v8,linux/amd64'
|
||||
user-id: '1001'
|
||||
uses: shlinkio/github-actions/.github/workflows/docker-build-and-publish.yml@main
|
||||
uses: shlinkio/github-actions/.github/workflows/docker-publish-image.yml@main
|
||||
secrets: inherit
|
||||
with:
|
||||
image-name: shlinkio/shlink
|
||||
@@ -31,4 +24,3 @@ jobs:
|
||||
tags-suffix: ${{ matrix.tag-suffix }}
|
||||
extra-build-args: |
|
||||
SHLINK_RUNTIME=${{ matrix.runtime }}
|
||||
SHLINK_USER_ID=${{ matrix.user-id && matrix.user-id || 'root' }}
|
||||
|
||||
17
.github/workflows/publish-release.yml
vendored
17
.github/workflows/publish-release.yml
vendored
@@ -7,31 +7,26 @@ on:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-24.04
|
||||
strategy:
|
||||
matrix:
|
||||
php-version: ['8.2', '8.3']
|
||||
swoole: ['yes', 'no']
|
||||
php-version: ['8.2', '8.3'] # TODO 8.4
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: './.github/actions/ci-setup'
|
||||
with:
|
||||
php-version: ${{ matrix.php-version }}
|
||||
php-extensions: openswoole-22.1.0
|
||||
extensions-cache-key: publish-swagger-spec-extensions-${{ matrix.php-version }}
|
||||
install-deps: 'no'
|
||||
- if: ${{ matrix.swoole == 'yes' }}
|
||||
run: ./build.sh ${GITHUB_REF#refs/tags/v}
|
||||
- if: ${{ matrix.swoole == 'no' }}
|
||||
run: ./build.sh ${GITHUB_REF#refs/tags/v} --no-swoole
|
||||
- run: ./build.sh ${GITHUB_REF#refs/tags/v}
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: dist-files-${{ matrix.php-version }}-${{ matrix.swoole }}
|
||||
name: dist-files-${{ matrix.php-version }}
|
||||
path: build
|
||||
|
||||
publish:
|
||||
needs: ['build']
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/download-artifact@v4
|
||||
@@ -48,7 +43,7 @@ jobs:
|
||||
|
||||
delete-artifacts:
|
||||
needs: ['publish']
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: geekyeggo/delete-artifact@v2
|
||||
with:
|
||||
|
||||
3
.github/workflows/publish-swagger-spec.yml
vendored
3
.github/workflows/publish-swagger-spec.yml
vendored
@@ -7,7 +7,7 @@ on:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-24.04
|
||||
strategy:
|
||||
matrix:
|
||||
php-version: ['8.2']
|
||||
@@ -20,7 +20,6 @@ jobs:
|
||||
- uses: './.github/actions/ci-setup'
|
||||
with:
|
||||
php-version: ${{ matrix.php-version }}
|
||||
php-extensions: openswoole-22.1.0
|
||||
extensions-cache-key: publish-swagger-spec-extensions-${{ matrix.php-version }}
|
||||
- run: composer swagger:inline
|
||||
- run: mkdir ${{ steps.determine_version.outputs.version }}
|
||||
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -1,6 +1,6 @@
|
||||
.idea
|
||||
bin/rr
|
||||
config/roadrunner/.pid
|
||||
.pid
|
||||
build
|
||||
!docker/build
|
||||
composer.lock
|
||||
@@ -12,6 +12,5 @@ data/GeoLite2-City.*
|
||||
data/infra/matomo
|
||||
docs/swagger-ui*
|
||||
docs/mercure.html
|
||||
docker-compose.override.yml
|
||||
.phpunit.result.cache
|
||||
docs/swagger/swagger-inlined.json
|
||||
|
||||
2330
CHANGELOG.md
2330
CHANGELOG.md
File diff suppressed because it is too large
Load Diff
@@ -16,11 +16,14 @@ The first thing you need to do is fork the repository, and clone it in your loca
|
||||
|
||||
Then you will have to follow these steps:
|
||||
|
||||
* Copy all files with `.local.php.dist` extension from `config/autoload` by removing the dist extension.
|
||||
* Copy the `config/params/shlink_dev_env.php.dist` in the same directory, but removing the `.dist` extension:
|
||||
|
||||
For example the `common.local.php.dist` file should be copied as `common.local.php`.
|
||||
```
|
||||
cp config/params/shlink_dev_env.php.dist config/params/shlink_dev_env.php
|
||||
```
|
||||
|
||||
The `shlink_dev_env.php` file is gitignored, so you can customize it as you want. For example, by adding your own GeoLite license key.
|
||||
|
||||
* Copy the file `docker-compose.override.yml.dist` by also removing the `dist` extension.
|
||||
* Start-up the project by running `docker compose up`.
|
||||
|
||||
The first time this command is run, it will create several containers that are used during development, so it may take some time.
|
||||
@@ -31,7 +34,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 `8800` through RoadRunner, `8080` through openswoole and `8000` through nginx+php-fpm.
|
||||
Once you finish this, you will have the project exposed in ports `8800` through RoadRunner and `8000` through nginx+php-fpm.
|
||||
|
||||
> Note: The `indocker` shell script is a helper tool used to run commands inside the main docker container.
|
||||
|
||||
@@ -80,7 +83,7 @@ The purposes of every folder are:
|
||||
* `data`: Common git-ignored assets, like logs, caches, lock files, GeoLite DB files, etc. It's the only location where Shlink may need to write at runtime.
|
||||
* `docs`: Any project documentation is stored here, like API spec definitions or architectural decision records.
|
||||
* `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 RoadRunner or openswoole.
|
||||
* `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 RoadRunner.
|
||||
|
||||
## Project tests
|
||||
|
||||
@@ -96,7 +99,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 RoadRunner or openswoole, 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 RoadRunner, 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.
|
||||
|
||||
@@ -124,7 +127,6 @@ Depending on the kind of contribution, maybe not all kinds of tests are needed,
|
||||
|
||||
* Run `./indocker composer test:api` to run API E2E tests. For these, the Postgres database engine is used.
|
||||
* Run `./indocker composer test:cli` to run CLI E2E tests. For these, the Maria DB database engine is used.
|
||||
* Run `./indocker composer infect:test` to run both unit and database tests (over sqlite) and then apply mutations to them with [infection](https://infection.github.io/).
|
||||
* Run `./indocker composer ci` to run all previous commands together, parallelizing non-conflicting tasks as much as possible.
|
||||
|
||||
## Testing endpoints
|
||||
|
||||
31
Dockerfile
31
Dockerfile
@@ -1,14 +1,12 @@
|
||||
FROM php:8.2-alpine3.17 as base
|
||||
FROM php:8.3-alpine3.19 as base
|
||||
|
||||
ARG SHLINK_VERSION=latest
|
||||
ENV SHLINK_VERSION ${SHLINK_VERSION}
|
||||
ARG SHLINK_RUNTIME=rr
|
||||
ENV SHLINK_RUNTIME ${SHLINK_RUNTIME}
|
||||
ARG SHLINK_USER_ID='root'
|
||||
ENV SHLINK_USER_ID ${SHLINK_USER_ID}
|
||||
|
||||
ENV OPENSWOOLE_VERSION 22.1.0
|
||||
ENV PDO_SQLSRV_VERSION 5.11.1
|
||||
ENV USER_ID '1001'
|
||||
ENV PDO_SQLSRV_VERSION 5.12.0
|
||||
ENV MS_ODBC_DOWNLOAD 'b/9/f/b9f3cce4-3925-46d4-9f46-da08869c6486'
|
||||
ENV MS_ODBC_SQL_VERSION 18_18.1.1.1
|
||||
ENV LC_ALL 'C'
|
||||
@@ -26,13 +24,8 @@ RUN \
|
||||
apk del .dev-deps && \
|
||||
apk add --no-cache postgresql icu libzip libpng
|
||||
|
||||
# Install openswoole and sqlsrv driver for x86_64 builds
|
||||
# Install sqlsrv driver for x86_64 builds
|
||||
RUN apk add --no-cache --virtual .phpize-deps ${PHPIZE_DEPS} unixodbc-dev && \
|
||||
if [ "$SHLINK_RUNTIME" == 'openswoole' ]; then \
|
||||
# Openswoole is deprecated. Remove in v4.0.0
|
||||
pecl install openswoole-${OPENSWOOLE_VERSION} && \
|
||||
docker-php-ext-enable openswoole ; \
|
||||
fi; \
|
||||
if [ $(uname -m) == "x86_64" ]; then \
|
||||
wget https://download.microsoft.com/download/${MS_ODBC_DOWNLOAD}/msodbcsql${MS_ODBC_SQL_VERSION}-1_amd64.apk && \
|
||||
apk add --allow-untrusted msodbcsql${MS_ODBC_SQL_VERSION}-1_amd64.apk && \
|
||||
@@ -47,24 +40,17 @@ FROM base as builder
|
||||
COPY . .
|
||||
COPY --from=composer:2 /usr/bin/composer ./composer.phar
|
||||
RUN apk add --no-cache git && \
|
||||
# FIXME Ignoring ext-openswoole platform req, as it makes install fail with roadrunner, even though it's a dev dependency and we are passing --no-dev
|
||||
php composer.phar install --no-dev --prefer-dist --optimize-autoloader --no-progress --no-interaction --ignore-platform-req=ext-openswoole && \
|
||||
if [ "$SHLINK_RUNTIME" == 'openswoole' ]; then \
|
||||
# Openswoole is deprecated. Remove in v4.0.0
|
||||
php composer.phar remove spiral/roadrunner spiral/roadrunner-jobs spiral/roadrunner-cli spiral/roadrunner-http --with-all-dependencies --update-no-dev --optimize-autoloader --no-progress --no-interaction ; \
|
||||
elif [ "$SHLINK_RUNTIME" == 'rr' ]; then \
|
||||
php composer.phar remove mezzio/mezzio-swoole --with-all-dependencies --update-no-dev --optimize-autoloader --no-progress --no-interaction --ignore-platform-req=ext-openswoole ; \
|
||||
fi; \
|
||||
php composer.phar install --no-dev --prefer-dist --optimize-autoloader --no-progress --no-interaction && \
|
||||
php composer.phar clear-cache && \
|
||||
rm -r docker composer.* && \
|
||||
sed -i "s/%SHLINK_VERSION%/${SHLINK_VERSION}/g" config/autoload/app_options.global.php
|
||||
sed -i "s/%SHLINK_VERSION%/${SHLINK_VERSION}/g" module/Core/src/Config/Options/AppOptions.php
|
||||
|
||||
|
||||
# Prepare final image
|
||||
FROM base
|
||||
LABEL maintainer="Alejandro Celaya <alejandro@alejandrocelaya.com>"
|
||||
|
||||
COPY --from=builder --chown=${SHLINK_USER_ID} /etc/shlink .
|
||||
COPY --from=builder --chown=${USER_ID} /etc/shlink .
|
||||
RUN ln -s /etc/shlink/bin/cli /usr/local/bin/shlink && \
|
||||
if [ "$SHLINK_RUNTIME" == 'rr' ]; then \
|
||||
php ./vendor/bin/rr get --no-interaction --no-config --location bin/ && chmod +x bin/rr ; \
|
||||
@@ -75,9 +61,8 @@ EXPOSE 8080
|
||||
|
||||
# Copy config specific for the image
|
||||
COPY docker/docker-entrypoint.sh docker-entrypoint.sh
|
||||
COPY docker/config/shlink_in_docker.local.php config/autoload/shlink_in_docker.local.php
|
||||
COPY docker/config/php.ini ${PHP_INI_DIR}/conf.d/
|
||||
|
||||
USER ${SHLINK_USER_ID}
|
||||
USER ${USER_ID}
|
||||
|
||||
ENTRYPOINT ["/bin/sh", "./docker-entrypoint.sh"]
|
||||
|
||||
2
LICENSE
2
LICENSE
@@ -1,6 +1,6 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016-2023 Alejandro Celaya
|
||||
Copyright (c) 2016-2024 Alejandro Celaya
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
@@ -2,12 +2,12 @@
|
||||
|
||||
[](https://github.com/shlinkio/shlink/actions/workflows/ci.yml?query=workflow%3A%22Continuous+integration%22)
|
||||
[](https://app.codecov.io/gh/shlinkio/shlink)
|
||||
[](https://dashboard.stryker-mutator.io/reports/github.com/shlinkio/shlink/develop)
|
||||
[](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://fosstodon.org/@shlinkio)
|
||||
[](https://bsky.app/profile/shlinkio.bsky.social)
|
||||
[](https://slnk.to/donate)
|
||||
|
||||
A PHP-based self-hosted URL shortener that can be used to serve shortened URLs under your own domain.
|
||||
@@ -38,12 +38,11 @@ First, make sure the host where you are going to run shlink fulfills these requi
|
||||
|
||||
* PHP 8.2 or 8.3
|
||||
* The next PHP extensions: json, curl, pdo, intl, gd and gmp/bcmath.
|
||||
* apcu extension is recommended if you don't plan to use openswoole.
|
||||
* apcu extension is recommended if you don't plan to use RoadRunner.
|
||||
* xml extension is required if you want to generate QR codes in svg format.
|
||||
* sockets and bcmath extensions are required if you want to integrate with a RabbitMQ instance.
|
||||
* MySQL, MariaDB, PostgreSQL, MicrosoftSQL or SQLite.
|
||||
* You will also need the corresponding pdo variation for the database you are planning to use: `pdo_mysql`, `pdo_pgsql`, `pdo_sqlsrv` or `pdo_sqlite`.
|
||||
* 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
|
||||
|
||||
@@ -53,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 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.
|
||||
|
||||
Finally, decompress the file in the location of your choice.
|
||||
|
||||
|
||||
50
UPGRADE.md
50
UPGRADE.md
@@ -1,5 +1,55 @@
|
||||
# Upgrading
|
||||
|
||||
## From v3.x to v4.x
|
||||
|
||||
### General
|
||||
|
||||
* Swoole and Openswoole are no longer officially supported runtimes. The recommended alternative is RoadRunner.
|
||||
* Dist files for swoole/openswoole are no longer published.
|
||||
* Webhooks are no longer supported. Migrate to one of the other [real-time updates](https://shlink.io/documentation/advanced/real-time-updates/) mechanisms.
|
||||
* When using RoadRunner, the amount of web workers, task workers and the port number can no longer be provided via config options. Use `WEB_WORKER_NUM`, `TASK_WORKER_NUM` and `PORT` env vars instead.
|
||||
|
||||
### Changes in URL shortener
|
||||
|
||||
* The short URLs `loosely` mode is no longer supported, as it was a typo. Use `loose` mode instead.
|
||||
* QR codes URLs now work by default, even for short URLs that cannot be visited due to max visits or date range limitations.
|
||||
If you want to keep previous behavior, pass `QR_CODE_FOR_DISABLED_SHORT_URLS=false` or the equivalent configuration option.
|
||||
* Long URL title resolution is now enabled by default. You can still disable it by passing `AUTO_RESOLVE_TITLES=false` or the equivalent configuration option.
|
||||
* Shlink no longer allows to opt-in for long URL verification. Long URLs are unconditionally considered correct during short URL creation/edition.
|
||||
* Device long URLs have been migrated to the new Dynamic rule-based redirects system and will continue to work as expected, but the API surface has changed.
|
||||
If you use shlink-web-client and rely on this feature when creating/updating short URLs, **DO NOT UPDATE YET**. Support for dynamic rule-based redirects will be added to shlink-web-client soon, in v4.1.0
|
||||
|
||||
### Changes in REST API
|
||||
|
||||
* REST API v1/v2 now behave like v3. This only affects error codes, which are now proper URIs.
|
||||
* `INVALID_ARGUMENT` -> `https://shlink.io/api/error/invalid-data`
|
||||
* `INVALID_SHORT_URL_DELETION` -> `https://shlink.io/api/error/invalid-short-url-deletion`
|
||||
* `DOMAIN_NOT_FOUND` -> `https://shlink.io/api/error/domain-not-found`
|
||||
* `FORBIDDEN_OPERATION` -> `https://shlink.io/api/error/forbidden-tag-operation`
|
||||
* `INVALID_SLUG` -> `https://shlink.io/api/error/non-unique-slug`
|
||||
* `INVALID_SHORTCODE` -> `https://shlink.io/api/error/short-url-not-found`
|
||||
* `TAG_CONFLICT` -> `https://shlink.io/api/error/tag-conflict`
|
||||
* `TAG_NOT_FOUND` -> `https://shlink.io/api/error/tag-not-found`
|
||||
* `MERCURE_NOT_CONFIGURED` -> `https://shlink.io/api/error/mercure-not-configured`
|
||||
* `INVALID_AUTHORIZATION` -> `https://shlink.io/api/error/missing-authentication`
|
||||
* `INVALID_API_KEY` -> `https://shlink.io/api/error/invalid-api-key`
|
||||
* Endpoints previously returning props like `"visitsCount": {number}` no longer do it. There should be an alternative `"visitsSummary": {}` object with the amount nested on it.
|
||||
* It is no longer possible to order the short URLs list with `orderBy=visitsCount-ASC`/`orderBy=visitsCount-DESC`. Use `orderBy=visits-ASC`/`orderBy=visits-DESC` instead.
|
||||
* It is no longer possible to get tags with stats using `GET /tags?withStats=true`. Use `GET /tags/stats` endpoint instead.
|
||||
* The `deviceLongUrls` are ignored when calling `POST /short-urls` or `PATCH /short-urls/{shortCode}`. These should now be configured as dynamic rule-based redirects via `POST /short-urls/{shortCode}/redirect-rules`.
|
||||
|
||||
### Changes in Docker image
|
||||
|
||||
* Since openswoole is no longer supported, there are no longer image tags suffixed with `openswoole`. You should migrate to the default or `roadrunner` ones.
|
||||
* The `non-root` docker tag is no longer published, as all docker images are now running without super-user permissions.
|
||||
* Due to previous point, it is no longer possible to pass `ENABLE_PERIODIC_VISIT_LOCATE=true` in order to configure a cron job that locates visits periodically.
|
||||
This was not really needed in the docker image, as visits are located on the fly.
|
||||
|
||||
### Changes in integrations
|
||||
|
||||
* Credentials in redis URLs should now be URL-encoded, as they are unconditionally url-decoded before being used. Previously, it was possible to customize this behavior via `REDIS_DECODE_CREDENTIALS=true|false`.
|
||||
* Providing redis URIs in the form of `tcp://password@6.6.6.6:6379` is no longer supported. If you want to provide password with no username, do `tcp://:password@6.6.6.6:6379` instead.
|
||||
|
||||
## From v2.x to v3.x
|
||||
|
||||
### Changes in REST API
|
||||
|
||||
@@ -4,6 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
use Mezzio\Application;
|
||||
use Psr\Container\ContainerInterface;
|
||||
use Shlinkio\Shlink\Common\Middleware\RequestIdMiddleware;
|
||||
use Shlinkio\Shlink\EventDispatcher\RoadRunner\RoadRunnerTaskConsumerToListener;
|
||||
use Spiral\RoadRunner\Http\PSR7Worker;
|
||||
|
||||
@@ -27,6 +28,9 @@ use function Shlinkio\Shlink\Config\env;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$container->get(RoadRunnerTaskConsumerToListener::class)->listenForTasks();
|
||||
$requestIdMiddleware = $container->get(RequestIdMiddleware::class);
|
||||
$container->get(RoadRunnerTaskConsumerToListener::class)->listenForTasks(
|
||||
fn (string $requestId) => $requestIdMiddleware->setCurrentRequestId($requestId),
|
||||
);
|
||||
}
|
||||
})();
|
||||
|
||||
@@ -2,10 +2,12 @@
|
||||
|
||||
export APP_ENV=test
|
||||
export TEST_ENV=api
|
||||
export TEST_RUNTIME="${TEST_RUNTIME:-"openswoole"}" # Openswoole is deprecated. Remove in v4.0.0
|
||||
export TEST_RUNTIME="${TEST_RUNTIME:-"rr"}" # rr is the only runtime currently supported
|
||||
export DB_DRIVER="${DB_DRIVER:-"postgres"}"
|
||||
export GENERATE_COVERAGE="${GENERATE_COVERAGE:-"no"}"
|
||||
|
||||
[ "$GENERATE_COVERAGE" != 'no' ] && export XDEBUG_MODE=coverage
|
||||
|
||||
# Reset logs
|
||||
OUTPUT_LOGS=data/log/api-tests/output.log
|
||||
rm -rf data/log/api-tests
|
||||
@@ -13,26 +15,19 @@ mkdir data/log/api-tests
|
||||
touch $OUTPUT_LOGS
|
||||
|
||||
# Try to stop server just in case it hanged in last execution
|
||||
[ "$TEST_RUNTIME" = 'openswoole' ] && vendor/bin/laminas mezzio:swoole:stop
|
||||
[ "$TEST_RUNTIME" = 'rr' ] && bin/rr stop -f
|
||||
[ "$TEST_RUNTIME" = 'rr' ] && bin/rr stop -f -w .
|
||||
|
||||
echo 'Starting server...'
|
||||
[ "$TEST_RUNTIME" = 'openswoole' ] && vendor/bin/laminas mezzio:swoole:start -d
|
||||
[ "$TEST_RUNTIME" = 'rr' ] && bin/rr serve -p -c=config/roadrunner/.rr.dev.yml \
|
||||
-o=http.address=0.0.0.0:9999 \
|
||||
-o=logs.encoding=json \
|
||||
-o=logs.channels.http.encoding=json \
|
||||
-o=logs.channels.server.encoding=json \
|
||||
[ "$TEST_RUNTIME" = 'rr' ] && bin/rr serve -p -w . -c=config/roadrunner/.rr.test.yml \
|
||||
-o=logs.output="${PWD}/${OUTPUT_LOGS}" \
|
||||
-o=logs.channels.http.output="${PWD}/${OUTPUT_LOGS}" \
|
||||
-o=logs.channels.server.output="${PWD}/${OUTPUT_LOGS}" &
|
||||
sleep 2 # Let's give the server a couple of seconds to start
|
||||
|
||||
vendor/bin/phpunit --order-by=random -c phpunit-api.xml --testdox --colors=always --log-junit=build/coverage-api/junit.xml $*
|
||||
testsExitCode=$?
|
||||
vendor/bin/phpunit --order-by=random -c phpunit-api.xml --testdox --testdox-summary $*
|
||||
TESTS_EXIT_CODE=$?
|
||||
|
||||
[ "$TEST_RUNTIME" = 'openswoole' ] && vendor/bin/laminas mezzio:swoole:stop
|
||||
[ "$TEST_RUNTIME" = 'rr' ] && bin/rr stop -c config/roadrunner/.rr.dev.yml -o=http.address=0.0.0.0:9999
|
||||
[ "$TEST_RUNTIME" = 'rr' ] && bin/rr stop -w .
|
||||
|
||||
# Exit this script with the same code as the tests. If tests failed, this script has to fail
|
||||
exit $testsExitCode
|
||||
exit $TESTS_EXIT_CODE
|
||||
|
||||
14
bin/test/run-cli-tests.sh
Executable file
14
bin/test/run-cli-tests.sh
Executable file
@@ -0,0 +1,14 @@
|
||||
#!/usr/bin/env sh
|
||||
|
||||
export APP_ENV=test
|
||||
export TEST_ENV=cli
|
||||
export DB_DRIVER="${DB_DRIVER:-"maria"}"
|
||||
export GENERATE_COVERAGE="${GENERATE_COVERAGE:-"no"}"
|
||||
|
||||
[ "$GENERATE_COVERAGE" != 'no' ] && export XDEBUG_MODE=coverage
|
||||
|
||||
vendor/bin/phpunit --order-by=random --testdox --testdox-summary -c phpunit-cli.xml $*
|
||||
TESTS_EXIT_CODE=$?
|
||||
|
||||
# Exit this script with the same code as the tests. If tests failed, this script has to fail
|
||||
exit $TESTS_EXIT_CODE
|
||||
26
build.sh
26
build.sh
@@ -1,18 +1,15 @@
|
||||
#!/usr/bin/env bash
|
||||
set -e
|
||||
|
||||
if [ "$#" -lt 1 ] || [ "$#" -gt 2 ] || ([ "$#" == 2 ] && [ "$2" != "--no-swoole" ]); then
|
||||
if [ "$#" -lt 1 ]; then
|
||||
echo "Usage:" >&2
|
||||
echo " $0 {version} [--no-swoole]" >&2
|
||||
echo " $0 {version}" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
version=$1
|
||||
noSwoole=$2
|
||||
phpVersion=$(php -r 'echo PHP_MAJOR_VERSION . "." . PHP_MINOR_VERSION;')
|
||||
# Openswoole is deprecated. Remove in v4.0.0
|
||||
[[ $noSwoole ]] && swooleSuffix="" || swooleSuffix="_openswoole"
|
||||
distId="shlink${version}_php${phpVersion}${swooleSuffix}_dist"
|
||||
distId="shlink${version}_php${phpVersion}_dist"
|
||||
builtContent="./build/${distId}"
|
||||
projectdir=$(pwd)
|
||||
[[ -f ./composer.phar ]] && composerBin='./composer.phar' || composerBin='composer'
|
||||
@@ -31,26 +28,15 @@ cd "${builtContent}"
|
||||
|
||||
# Install dependencies
|
||||
echo "Installing dependencies with $composerBin..."
|
||||
# Deprecated. Do not ignore PHP platform req for Shlink v4.0.0
|
||||
composerFlags="--optimize-autoloader --no-progress --no-interaction --ignore-platform-req=php+"
|
||||
${composerBin} self-update
|
||||
${composerBin} install --no-dev --prefer-dist $composerFlags
|
||||
|
||||
if [[ $noSwoole ]]; then
|
||||
# If generating a dist not for openswoole, uninstall mezzio-swoole
|
||||
${composerBin} remove mezzio/mezzio-swoole --with-all-dependencies --update-no-dev $composerFlags
|
||||
else
|
||||
# Deprecated. Remove in Shlink v4.0.0
|
||||
# If generating a dist for openswoole, uninstall RoadRunner
|
||||
${composerBin} remove spiral/roadrunner spiral/roadrunner-jobs spiral/roadrunner-cli spiral/roadrunner-http --with-all-dependencies --update-no-dev $composerFlags
|
||||
fi
|
||||
${composerBin} install --no-dev --prefer-dist --optimize-autoloader --no-progress --no-interaction
|
||||
|
||||
# Delete development files
|
||||
echo 'Deleting dev files...'
|
||||
rm composer.*
|
||||
|
||||
# Update Shlink version in config
|
||||
sed -i "s/%SHLINK_VERSION%/${version}/g" config/autoload/app_options.global.php
|
||||
# Update Shlink version
|
||||
sed -i "s/%SHLINK_VERSION%/${version}/g" module/Core/src/Config/Options/AppOptions.php
|
||||
|
||||
# Compressing file
|
||||
echo 'Compressing files...'
|
||||
|
||||
172
composer.json
172
composer.json
@@ -16,68 +16,66 @@
|
||||
"ext-curl": "*",
|
||||
"ext-gd": "*",
|
||||
"ext-json": "*",
|
||||
"ext-mbstring": "*",
|
||||
"ext-pdo": "*",
|
||||
"akrabat/ip-address-middleware": "^2.1",
|
||||
"cakephp/chronos": "^3.0.2",
|
||||
"doctrine/migrations": "^3.6",
|
||||
"doctrine/orm": "^2.16",
|
||||
"endroid/qr-code": "^4.8",
|
||||
"akrabat/ip-address-middleware": "^2.3",
|
||||
"cakephp/chronos": "^3.1",
|
||||
"doctrine/dbal": "^4.2",
|
||||
"doctrine/migrations": "^3.8",
|
||||
"doctrine/orm": "^3.3",
|
||||
"endroid/qr-code": "^6.0",
|
||||
"friendsofphp/proxy-manager-lts": "^1.0",
|
||||
"geoip2/geoip2": "^3.0",
|
||||
"guzzlehttp/guzzle": "^7.5",
|
||||
"happyr/doctrine-specification": "^2.0",
|
||||
"guzzlehttp/guzzle": "^7.9",
|
||||
"hidehalo/nanoid-php": "^1.1",
|
||||
"jaybizzle/crawler-detect": "^1.2.116",
|
||||
"laminas/laminas-config": "^3.8",
|
||||
"laminas/laminas-config-aggregator": "^1.13",
|
||||
"laminas/laminas-diactoros": "^3.3",
|
||||
"laminas/laminas-inputfilter": "^2.27",
|
||||
"laminas/laminas-servicemanager": "^3.21",
|
||||
"laminas/laminas-stdlib": "^3.17",
|
||||
"league/uri": "^6.8",
|
||||
"matomo/matomo-php-tracker": "^3.2",
|
||||
"mezzio/mezzio": "^3.17",
|
||||
"mezzio/mezzio-fastroute": "^3.10",
|
||||
"mezzio/mezzio-problem-details": "^1.13",
|
||||
"mezzio/mezzio-swoole": "^4.7",
|
||||
"laminas/laminas-config": "^3.9",
|
||||
"laminas/laminas-config-aggregator": "^1.15",
|
||||
"laminas/laminas-diactoros": "^3.5",
|
||||
"laminas/laminas-inputfilter": "^2.30",
|
||||
"laminas/laminas-servicemanager": "^3.22",
|
||||
"laminas/laminas-stdlib": "^3.19",
|
||||
"matomo/matomo-php-tracker": "^3.3",
|
||||
"mezzio/mezzio": "^3.20",
|
||||
"mezzio/mezzio-fastroute": "^3.12",
|
||||
"mezzio/mezzio-problem-details": "^1.15",
|
||||
"mlocati/ip-lib": "^1.18",
|
||||
"mobiledetect/mobiledetectlib": "^4.8",
|
||||
"pagerfanta/core": "^3.8",
|
||||
"php-middleware/request-id": "^4.1",
|
||||
"pugx/shortid-php": "^1.1",
|
||||
"ramsey/uuid": "^4.7",
|
||||
"shlinkio/shlink-common": "^5.7.1",
|
||||
"shlinkio/shlink-config": "^2.5",
|
||||
"shlinkio/shlink-event-dispatcher": "^3.1",
|
||||
"shlinkio/shlink-importer": "^5.2.1",
|
||||
"shlinkio/shlink-installer": "^8.7",
|
||||
"shlinkio/shlink-ip-geolocation": "^3.4",
|
||||
"shlinkio/doctrine-specification": "^2.1.1",
|
||||
"shlinkio/shlink-common": "^6.4",
|
||||
"shlinkio/shlink-config": "^3.3",
|
||||
"shlinkio/shlink-event-dispatcher": "^4.1",
|
||||
"shlinkio/shlink-importer": "^5.3.2",
|
||||
"shlinkio/shlink-installer": "^9.2",
|
||||
"shlinkio/shlink-ip-geolocation": "^4.1",
|
||||
"shlinkio/shlink-json": "^1.1",
|
||||
"spiral/roadrunner": "^2023.2",
|
||||
"spiral/roadrunner-cli": "^2.5",
|
||||
"spiral/roadrunner-http": "^3.1",
|
||||
"spiral/roadrunner-jobs": "^4.0",
|
||||
"symfony/console": "^6.3",
|
||||
"symfony/filesystem": "^6.3",
|
||||
"symfony/lock": "^6.3",
|
||||
"symfony/process": "^6.3",
|
||||
"symfony/string": "^6.3"
|
||||
"spiral/roadrunner": "^2024.1",
|
||||
"spiral/roadrunner-cli": "^2.6",
|
||||
"spiral/roadrunner-http": "^3.5",
|
||||
"spiral/roadrunner-jobs": "^4.5",
|
||||
"symfony/console": "^7.1",
|
||||
"symfony/filesystem": "^7.1",
|
||||
"symfony/lock": "^7.1",
|
||||
"symfony/process": "^7.1",
|
||||
"symfony/string": "^7.1"
|
||||
},
|
||||
"require-dev": {
|
||||
"devizzent/cebe-php-openapi": "^1.0.1",
|
||||
"devster/ubench": "^2.1",
|
||||
"infection/infection": "^0.27",
|
||||
"openswoole/ide-helper": "~22.0.0",
|
||||
"phpstan/phpstan": "^1.10",
|
||||
"phpstan/phpstan-doctrine": "^1.3",
|
||||
"phpstan/phpstan-phpunit": "^1.3",
|
||||
"phpstan/phpstan-symfony": "^1.3",
|
||||
"phpunit/php-code-coverage": "^10.1",
|
||||
"phpunit/phpunit": "^10.4",
|
||||
"phpstan/phpstan": "^1.12",
|
||||
"phpstan/phpstan-doctrine": "^1.5",
|
||||
"phpstan/phpstan-phpunit": "^1.4",
|
||||
"phpstan/phpstan-symfony": "^1.4",
|
||||
"phpunit/php-code-coverage": "^11.0",
|
||||
"phpunit/phpcov": "^10.0",
|
||||
"phpunit/phpunit": "^11.4",
|
||||
"roave/security-advisories": "dev-master",
|
||||
"shlinkio/php-coding-standard": "~2.3.0",
|
||||
"shlinkio/shlink-test-utils": "^3.8.1",
|
||||
"symfony/var-dumper": "^6.3",
|
||||
"veewee/composer-run-parallel": "^1.3"
|
||||
"shlinkio/shlink-test-utils": "^4.1.1",
|
||||
"symfony/var-dumper": "^7.1",
|
||||
"veewee/composer-run-parallel": "^1.4"
|
||||
},
|
||||
"conflict": {
|
||||
"symfony/var-exporter": ">=6.3.9,<=6.4.0"
|
||||
@@ -111,65 +109,58 @@
|
||||
},
|
||||
"scripts": {
|
||||
"ci": [
|
||||
"@parallel cs stan swagger:validate test:unit:ci test:db:sqlite:ci test:db:mysql test:db:maria test:db:postgres test:db:ms",
|
||||
"@parallel infect:test:api infect:test:cli infect:ci:unit infect:ci:db"
|
||||
"@parallel cs stan swagger:validate test:unit:ci test:db:sqlite:ci test:db:postgres test:db:mysql test:db:maria test:db:ms",
|
||||
"@parallel test:api:ci test:cli:ci"
|
||||
],
|
||||
"cs": "phpcs -s",
|
||||
"cs:fix": "phpcbf",
|
||||
"stan": "APP_ENV=test php vendor/bin/phpstan analyse module/*/src module/*/test* module/*/config module/*/migrations config docker/config --level=8",
|
||||
"stan": ["@putenv APP_ENV=test", "phpstan analyse"],
|
||||
"test": [
|
||||
"@parallel test:unit test:db",
|
||||
"@parallel test:api test:cli"
|
||||
],
|
||||
"test:unit": "@php vendor/bin/phpunit --order-by=random --colors=always --testdox",
|
||||
"test:unit:ci": "@test:unit --coverage-php=build/coverage-unit.cov --coverage-xml=build/coverage-unit/coverage-xml --log-junit=build/coverage-unit/junit.xml",
|
||||
"test:unit:pretty": "@test:unit --coverage-html build/coverage-unit/coverage-html",
|
||||
"test:unit": ["@putenv COLUMNS=120", "phpunit --order-by=random --testdox --testdox-summary"],
|
||||
"test:unit:ci": ["@putenv XDEBUG_MODE=coverage", "@test:unit --coverage-php=build/coverage-unit.cov"],
|
||||
"test:unit:pretty": ["@putenv XDEBUG_MODE=coverage", "@test:unit --coverage-html build/coverage-unit/coverage-html"],
|
||||
"test:db": "@parallel test:db:sqlite:ci test:db:mysql test:db:maria test:db:postgres test:db:ms",
|
||||
"test:db:sqlite": "APP_ENV=test php vendor/bin/phpunit --order-by=random --colors=always --testdox -c phpunit-db.xml",
|
||||
"test:db:sqlite:ci": "@test:db:sqlite --coverage-php build/coverage-db.cov --coverage-xml=build/coverage-db/coverage-xml --log-junit=build/coverage-db/junit.xml",
|
||||
"test:db:mysql": "DB_DRIVER=mysql composer test:db:sqlite",
|
||||
"test:db:maria": "DB_DRIVER=maria composer test:db:sqlite",
|
||||
"test:db:postgres": "DB_DRIVER=postgres composer test:db:sqlite",
|
||||
"test:db:ms": "DB_DRIVER=mssql composer test:db:sqlite",
|
||||
"test:db:sqlite": ["@putenv APP_ENV=test", "phpunit --order-by=random --testdox --testdox-summary -c phpunit-db.xml"],
|
||||
"test:db:sqlite:ci": ["@putenv XDEBUG_MODE=coverage", "@test:db:sqlite --coverage-php build/coverage-db.cov"],
|
||||
"test:db:mysql": ["@putenv DB_DRIVER=mysql", "@test:db:sqlite"],
|
||||
"test:db:maria": ["@putenv DB_DRIVER=maria", "@test:db:sqlite"],
|
||||
"test:db:postgres": ["@putenv DB_DRIVER=postgres", "@test:db:sqlite"],
|
||||
"test:db:ms": ["@putenv DB_DRIVER=mssql", "@test:db:sqlite"],
|
||||
"test:api": "bin/test/run-api-tests.sh",
|
||||
"test:api:rr": "TEST_RUNTIME=rr bin/test/run-api-tests.sh",
|
||||
"test:api:ci": "GENERATE_COVERAGE=yes composer test:api",
|
||||
"test:api:pretty": "GENERATE_COVERAGE=pretty composer test:api",
|
||||
"test:cli": "APP_ENV=test DB_DRIVER=maria TEST_ENV=cli php vendor/bin/phpunit --order-by=random --colors=always --testdox -c phpunit-cli.xml --log-junit=build/coverage-cli/junit.xml",
|
||||
"test:cli:ci": "GENERATE_COVERAGE=yes composer test:cli",
|
||||
"test:cli:pretty": "GENERATE_COVERAGE=pretty composer test:cli",
|
||||
"infect:ci:base": "infection --threads=max --only-covered --skip-initial-tests",
|
||||
"infect:ci:unit": "@infect:ci:base --coverage=build/coverage-unit --min-msi=80",
|
||||
"infect:ci:db": "@infect:ci:base --coverage=build/coverage-db --min-msi=95 --configuration=infection-db.json5",
|
||||
"infect:ci:api": "@infect:ci:base --coverage=build/coverage-api --min-msi=95 --configuration=infection-api.json5",
|
||||
"infect:ci:cli": "@infect:ci:base --coverage=build/coverage-cli --min-msi=90 --configuration=infection-cli.json5",
|
||||
"infect:ci": "@parallel infect:ci:unit infect:ci:db infect:ci:api infect:ci:cli",
|
||||
"infect:test": [
|
||||
"@parallel test:unit:ci test:db:sqlite:ci test:api:ci",
|
||||
"@infect:ci"
|
||||
"test:api:sqlite": ["@putenv DB_DRIVER=sqlite", "@test:api"],
|
||||
"test:api:mysql": ["@putenv DB_DRIVER=mysql", "@test:api"],
|
||||
"test:api:maria": ["@putenv DB_DRIVER=maria", "@test:api"],
|
||||
"test:api:mssql": ["@putenv DB_DRIVER=mssql", "@test:api"],
|
||||
"test:api:ci": [
|
||||
"@putenv GENERATE_COVERAGE=yes",
|
||||
"@test:api",
|
||||
"phpcov merge build/coverage-api --php build/coverage-api.cov && rm build/coverage-api/*.cov"
|
||||
],
|
||||
"infect:test:unit": [
|
||||
"@test:unit:ci",
|
||||
"@infect:ci:unit"
|
||||
"test:api:pretty": [
|
||||
"@putenv GENERATE_COVERAGE=yes",
|
||||
"@test:api",
|
||||
"phpcov merge build/coverage-api --html build/coverage-api/coverage-html && rm build/coverage-api/*.cov"
|
||||
],
|
||||
"infect:test:db": [
|
||||
"@test:db:sqlite:ci",
|
||||
"@infect:ci:db"
|
||||
"test:cli": "bin/test/run-cli-tests.sh",
|
||||
"test:cli:ci": [
|
||||
"@putenv GENERATE_COVERAGE=yes",
|
||||
"@test:cli",
|
||||
"vendor/bin/phpcov merge build/coverage-cli --php build/coverage-cli.cov && rm build/coverage-cli/*.cov"
|
||||
],
|
||||
"infect:test:api": [
|
||||
"@test:api:ci",
|
||||
"@infect:ci:api"
|
||||
],
|
||||
"infect:test:cli": [
|
||||
"@test:cli:ci",
|
||||
"@infect:ci:cli"
|
||||
"test:cli:pretty": [
|
||||
"@putenv GENERATE_COVERAGE=yes",
|
||||
"@test:cli",
|
||||
"phpcov merge build/coverage-cli --html build/coverage-cli/coverage-html && rm build/coverage-cli/*.cov"
|
||||
],
|
||||
"swagger:validate": "php-openapi validate docs/swagger/swagger.json",
|
||||
"swagger:inline": "php-openapi inline docs/swagger/swagger.json docs/swagger/swagger-inlined.json",
|
||||
"clean:dev": "rm -f data/database.sqlite && rm -f config/params/generated_config.php"
|
||||
},
|
||||
"scripts-descriptions": {
|
||||
"ci": "<fg=blue;options=bold>Alias for \"cs\", \"stan\", \"swagger:validate\", \"test:ci\" and \"infect:ci\"</>",
|
||||
"ci": "<fg=blue;options=bold>Alias for \"cs\", \"stan\", \"swagger:validate\" and \"test:ci\"</>",
|
||||
"cs": "<fg=blue;options=bold>Checks coding styles</>",
|
||||
"cs:fix": "<fg=blue;options=bold>Fixes coding styles, when possible</>",
|
||||
"stan": "<fg=blue;options=bold>Inspects code with phpstan</>",
|
||||
@@ -190,10 +181,6 @@
|
||||
"test:cli": "<fg=blue;options=bold>Runs CLI test suites</>",
|
||||
"test:cli:ci": "<fg=blue;options=bold>Runs CLI test suites, and generates code coverage for CI</>",
|
||||
"test:cli:pretty": "<fg=blue;options=bold>Runs CLI test suites, and generates code coverage in HTML format</>",
|
||||
"infect:ci": "<fg=blue;options=bold>Checks unit and db tests quality applying mutation testing with existing reports and logs</>",
|
||||
"infect:ci:unit": "<fg=blue;options=bold>Checks unit tests quality applying mutation testing with existing reports and logs</>",
|
||||
"infect:ci:db": "<fg=blue;options=bold>Checks db tests quality applying mutation testing with existing reports and logs</>",
|
||||
"infect:test": "<fg=blue;options=bold>Runs unit and db tests, then checks tests quality applying mutation testing</>",
|
||||
"swagger:validate": "<fg=blue;options=bold>Validates the swagger docs, making sure they fulfil the spec</>",
|
||||
"swagger:inline": "<fg=blue;options=bold>Inlines swagger docs in a single file</>",
|
||||
"clean:dev": "<fg=blue;options=bold>Deletes artifacts which are gitignored and could affect dev env</>"
|
||||
@@ -204,7 +191,6 @@
|
||||
"allow-plugins": {
|
||||
"composer/package-versions-deprecated": true,
|
||||
"dealerdirect/phpcodesniffer-composer-installer": true,
|
||||
"infection/extension-installer": true,
|
||||
"veewee/composer-run-parallel": true
|
||||
}
|
||||
}
|
||||
|
||||
2
config/autoload/.gitignore
vendored
2
config/autoload/.gitignore
vendored
@@ -1,2 +0,0 @@
|
||||
local.php
|
||||
*.local.php
|
||||
@@ -1,12 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
return [
|
||||
|
||||
'app_options' => [
|
||||
'name' => 'Shlink',
|
||||
'version' => '%SHLINK_VERSION%',
|
||||
],
|
||||
|
||||
];
|
||||
@@ -1,11 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
return [
|
||||
|
||||
'app_options' => [
|
||||
'version' => 'latest',
|
||||
],
|
||||
|
||||
];
|
||||
@@ -6,18 +6,17 @@ use Shlinkio\Shlink\Core\Config\EnvVars;
|
||||
|
||||
return (static function (): array {
|
||||
$redisServers = EnvVars::REDIS_SERVERS->loadFromEnv();
|
||||
$redis = ['pub_sub_enabled' => $redisServers !== null && EnvVars::REDIS_PUB_SUB_ENABLED->loadFromEnv(false)];
|
||||
$redis = ['pub_sub_enabled' => $redisServers !== null && EnvVars::REDIS_PUB_SUB_ENABLED->loadFromEnv()];
|
||||
$cacheRedisBlock = $redisServers === null ? [] : [
|
||||
'redis' => [
|
||||
'servers' => $redisServers,
|
||||
'sentinel_service' => EnvVars::REDIS_SENTINEL_SERVICE->loadFromEnv(),
|
||||
'decode_credentials' => (bool) EnvVars::REDIS_DECODE_CREDENTIALS->loadFromEnv(false),
|
||||
],
|
||||
];
|
||||
|
||||
return [
|
||||
'cache' => [
|
||||
'namespace' => EnvVars::CACHE_NAMESPACE->loadFromEnv('Shlink'),
|
||||
'namespace' => EnvVars::CACHE_NAMESPACE->loadFromEnv(),
|
||||
...$cacheRedisBlock,
|
||||
],
|
||||
'redis' => $redis,
|
||||
|
||||
@@ -3,13 +3,18 @@
|
||||
declare(strict_types=1);
|
||||
|
||||
use Laminas\ConfigAggregator\ConfigAggregator;
|
||||
use Shlinkio\Shlink\Core\Config\EnvVars;
|
||||
|
||||
return [
|
||||
return (function () {
|
||||
$isDev = EnvVars::isDevEnv();
|
||||
|
||||
'debug' => false,
|
||||
return [
|
||||
|
||||
// Disabling config cache for cli, ensures it's never used for openswoole/RoadRunner, and also that console
|
||||
// commands don't generate a cache file that's then used by php-fpm web executions
|
||||
ConfigAggregator::ENABLE_CACHE => PHP_SAPI !== 'cli',
|
||||
'debug' => $isDev,
|
||||
|
||||
];
|
||||
// Disabling config cache for cli, ensures it's never used for RoadRunner, and also that console
|
||||
// commands don't generate a cache file that's then used by php-fpm web executions
|
||||
ConfigAggregator::ENABLE_CACHE => ! $isDev && PHP_SAPI !== 'cli',
|
||||
|
||||
];
|
||||
})();
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Laminas\ConfigAggregator\ConfigAggregator;
|
||||
|
||||
return [
|
||||
|
||||
'debug' => true,
|
||||
ConfigAggregator::ENABLE_CACHE => false,
|
||||
|
||||
];
|
||||
@@ -1,20 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Shlinkio\Shlink;
|
||||
|
||||
use Shlinkio\Shlink\Core\Config\EnvVars;
|
||||
|
||||
return (static function (): array {
|
||||
$threshold = EnvVars::DELETE_SHORT_URL_THRESHOLD->loadFromEnv();
|
||||
|
||||
return [
|
||||
|
||||
'delete_short_urls' => [
|
||||
'check_visits_threshold' => $threshold !== null,
|
||||
'visits_threshold' => (int) ($threshold ?? DEFAULT_DELETE_SHORT_URL_THRESHOLD),
|
||||
],
|
||||
|
||||
];
|
||||
})();
|
||||
@@ -4,20 +4,24 @@ declare(strict_types=1);
|
||||
|
||||
use GuzzleHttp\Client;
|
||||
use Laminas\ServiceManager\AbstractFactory\ConfigAbstractFactory;
|
||||
use Laminas\ServiceManager\Factory\InvokableFactory;
|
||||
use Mezzio\Application;
|
||||
use Mezzio\Container;
|
||||
use Psr\Http\Client\ClientInterface;
|
||||
use Psr\Http\Message\ServerRequestFactoryInterface;
|
||||
use Psr\Http\Message\StreamFactoryInterface;
|
||||
use Psr\Http\Message\UploadedFileFactoryInterface;
|
||||
use Shlinkio\Shlink\Core\Config\EnvVars;
|
||||
use Spiral\RoadRunner\Http\PSR7Worker;
|
||||
use Spiral\RoadRunner\WorkerInterface;
|
||||
use Symfony\Component\Filesystem\Filesystem;
|
||||
|
||||
return [
|
||||
|
||||
'dependencies' => [
|
||||
'factories' => [
|
||||
PSR7Worker::class => ConfigAbstractFactory::class,
|
||||
Filesystem::class => InvokableFactory::class,
|
||||
],
|
||||
|
||||
'delegators' => [
|
||||
@@ -33,7 +37,7 @@ return [
|
||||
'lazy_services' => [
|
||||
'proxies_target_dir' => 'data/proxies',
|
||||
'proxies_namespace' => 'ShlinkProxy',
|
||||
'write_proxy_files' => true,
|
||||
'write_proxy_files' => EnvVars::isProdEnv(),
|
||||
],
|
||||
],
|
||||
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Psr\Container\ContainerInterface;
|
||||
use Psr\Log;
|
||||
|
||||
return [
|
||||
|
||||
'dependencies' => [
|
||||
'lazy_services' => [
|
||||
'write_proxy_files' => false,
|
||||
],
|
||||
|
||||
'initializers' => [
|
||||
function (ContainerInterface $container, $instance): void {
|
||||
if ($instance instanceof Log\LoggerAwareInterface) {
|
||||
$instance->setLogger($container->get(Log\LoggerInterface::class));
|
||||
}
|
||||
},
|
||||
],
|
||||
],
|
||||
|
||||
];
|
||||
@@ -2,8 +2,11 @@
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Doctrine\ORM\Events;
|
||||
use Happyr\DoctrineSpecification\Repository\EntitySpecificationRepository;
|
||||
use Shlinkio\Shlink\Core\Config\EnvVars;
|
||||
use Shlinkio\Shlink\Core\Visit\Listener\OrphanVisitsCountTracker;
|
||||
use Shlinkio\Shlink\Core\Visit\Listener\ShortUrlVisitsCountTracker;
|
||||
|
||||
use function Shlinkio\Shlink\Core\ArrayUtils\contains;
|
||||
|
||||
@@ -16,10 +19,9 @@ return (static function (): array {
|
||||
'mssql' => 'pdo_sqlsrv',
|
||||
default => 'pdo_mysql',
|
||||
};
|
||||
$resolveDefaultPort = static fn () => match ($driver) {
|
||||
'postgres' => '5432',
|
||||
'mssql' => '1433',
|
||||
default => '3306',
|
||||
$readCredentialAsString = static function (EnvVars $envVar): string|null {
|
||||
$value = $envVar->loadFromEnv();
|
||||
return $value === null ? null : (string) $value;
|
||||
};
|
||||
$resolveCharset = static fn () => match ($driver) {
|
||||
// This does not determine charsets or collations in tables or columns, but the charset used in the data
|
||||
@@ -28,6 +30,7 @@ return (static function (): array {
|
||||
'postgres' => 'utf8',
|
||||
default => null,
|
||||
};
|
||||
|
||||
$resolveConnection = static fn () => match ($driver) {
|
||||
null, 'sqlite' => [
|
||||
'driver' => 'pdo_sqlite',
|
||||
@@ -35,11 +38,11 @@ return (static function (): array {
|
||||
],
|
||||
default => [
|
||||
'driver' => $resolveDriver(),
|
||||
'dbname' => EnvVars::DB_NAME->loadFromEnv('shlink'),
|
||||
'user' => EnvVars::DB_USER->loadFromEnv(),
|
||||
'password' => EnvVars::DB_PASSWORD->loadFromEnv(),
|
||||
'host' => EnvVars::DB_HOST->loadFromEnv(EnvVars::DB_UNIX_SOCKET->loadFromEnv()),
|
||||
'port' => EnvVars::DB_PORT->loadFromEnv($resolveDefaultPort()),
|
||||
'dbname' => EnvVars::DB_NAME->loadFromEnv(),
|
||||
'user' => $readCredentialAsString(EnvVars::DB_USER),
|
||||
'password' => $readCredentialAsString(EnvVars::DB_PASSWORD),
|
||||
'host' => EnvVars::DB_HOST->loadFromEnv(),
|
||||
'port' => EnvVars::DB_PORT->loadFromEnv(),
|
||||
'unix_socket' => $isMysqlCompatible ? EnvVars::DB_UNIX_SOCKET->loadFromEnv() : null,
|
||||
'charset' => $resolveCharset(),
|
||||
'driverOptions' => $driver !== 'mssql' ? [] : [
|
||||
@@ -55,6 +58,10 @@ return (static function (): array {
|
||||
'proxies_dir' => 'data/proxies',
|
||||
'load_mappings_using_functional_style' => true,
|
||||
'default_repository_classname' => EntitySpecificationRepository::class,
|
||||
'listeners' => [
|
||||
Events::onFlush => [ShortUrlVisitsCountTracker::class, OrphanVisitsCountTracker::class],
|
||||
Events::postFlush => [ShortUrlVisitsCountTracker::class, OrphanVisitsCountTracker::class],
|
||||
],
|
||||
],
|
||||
'connection' => $resolveConnection(),
|
||||
],
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
return [
|
||||
|
||||
'entity_manager' => [
|
||||
'connection' => [
|
||||
// MySQL
|
||||
'user' => 'root',
|
||||
'password' => 'root',
|
||||
'driver' => 'pdo_mysql',
|
||||
'host' => 'shlink_db_mysql',
|
||||
'dbname' => 'shlink',
|
||||
// 'dbname' => 'shlink_foo',
|
||||
'charset' => 'utf8mb4',
|
||||
|
||||
// MariaDB
|
||||
// 'user' => 'root',
|
||||
// 'password' => 'root',
|
||||
// 'driver' => 'pdo_mysql',
|
||||
// 'host' => 'shlink_db_maria',
|
||||
// 'dbname' => 'shlink_foo',
|
||||
// 'charset' => 'utf8mb4',
|
||||
|
||||
// Postgres
|
||||
// 'user' => 'postgres',
|
||||
// 'password' => 'root',
|
||||
// 'driver' => 'pdo_pgsql',
|
||||
// 'host' => 'shlink_db_postgres',
|
||||
// 'dbname' => 'shlink_foo',
|
||||
// 'charset' => 'utf8',
|
||||
|
||||
// MSSQL
|
||||
// 'user' => 'sa',
|
||||
// 'password' => 'Passw0rd!',
|
||||
// 'driver' => 'pdo_sqlsrv',
|
||||
// 'host' => 'shlink_db_ms',
|
||||
// 'dbname' => 'shlink_foo',
|
||||
// 'driverOptions' => [
|
||||
// 'TrustServerCertificate' => 'true',
|
||||
// ],
|
||||
],
|
||||
],
|
||||
|
||||
];
|
||||
@@ -12,6 +12,7 @@ return [
|
||||
'installer' => [
|
||||
'enabled_options' => [
|
||||
Option\Server\RuntimeConfigOption::class,
|
||||
Option\Server\MemoryLimitConfigOption::class,
|
||||
Option\Database\DatabaseDriverConfigOption::class,
|
||||
Option\Database\DatabaseNameConfigOption::class,
|
||||
Option\Database\DatabaseHostConfigOption::class,
|
||||
@@ -21,8 +22,6 @@ return [
|
||||
Option\Database\DatabaseUnixSocketConfigOption::class,
|
||||
Option\UrlShortener\ShortDomainHostConfigOption::class,
|
||||
Option\UrlShortener\ShortDomainSchemaConfigOption::class,
|
||||
Option\Visit\VisitsWebhooksConfigOption::class,
|
||||
Option\Visit\OrphanVisitsWebhooksConfigOption::class,
|
||||
Option\Redirect\BaseUrlRedirectConfigOption::class,
|
||||
Option\Redirect\InvalidShortUrlRedirectConfigOption::class,
|
||||
Option\Redirect\Regular404RedirectConfigOption::class,
|
||||
@@ -30,10 +29,7 @@ return [
|
||||
Option\BasePathConfigOption::class,
|
||||
Option\TimezoneConfigOption::class,
|
||||
Option\Cache\CacheNamespaceConfigOption::class,
|
||||
Option\Worker\TaskWorkerNumConfigOption::class,
|
||||
Option\Worker\WebWorkerNumConfigOption::class,
|
||||
Option\Redis\RedisServersConfigOption::class,
|
||||
Option\Redis\RedisDecodeCredentialsConfigOption::class,
|
||||
Option\Redis\RedisSentinelServiceConfigOption::class,
|
||||
Option\Redis\RedisPubSubConfigOption::class,
|
||||
Option\UrlShortener\ShortCodeLengthOption::class,
|
||||
@@ -49,6 +45,8 @@ return [
|
||||
Option\UrlShortener\EnableMultiSegmentSlugsConfigOption::class,
|
||||
Option\UrlShortener\EnableTrailingSlashConfigOption::class,
|
||||
Option\UrlShortener\ShortUrlModeConfigOption::class,
|
||||
Option\Robots\RobotsAllowAllShortUrlsConfigOption::class,
|
||||
Option\Robots\RobotsUserAgentsConfigOption::class,
|
||||
Option\Tracking\IpAnonymizationConfigOption::class,
|
||||
Option\Tracking\OrphanVisitsTrackingConfigOption::class,
|
||||
Option\Tracking\DisableTrackParamConfigOption::class,
|
||||
@@ -62,6 +60,9 @@ return [
|
||||
Option\QrCode\DefaultFormatConfigOption::class,
|
||||
Option\QrCode\DefaultErrorCorrectionConfigOption::class,
|
||||
Option\QrCode\DefaultRoundBlockSizeConfigOption::class,
|
||||
Option\QrCode\DefaultColorConfigOption::class,
|
||||
Option\QrCode\DefaultBgColorConfigOption::class,
|
||||
Option\QrCode\DefaultLogoUrlConfigOption::class,
|
||||
Option\QrCode\EnabledForDisabledShortUrlsConfigOption::class,
|
||||
Option\RabbitMq\RabbitMqEnabledConfigOption::class,
|
||||
Option\RabbitMq\RabbitMqHostConfigOption::class,
|
||||
|
||||
@@ -4,29 +4,43 @@ declare(strict_types=1);
|
||||
|
||||
namespace Shlinkio\Shlink;
|
||||
|
||||
use Laminas\ServiceManager\AbstractFactory\ConfigAbstractFactory;
|
||||
use Laminas\ServiceManager\Factory\InvokableFactory;
|
||||
use Monolog\Level;
|
||||
use Monolog\Logger;
|
||||
use PhpMiddleware\RequestId;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Psr\Log\NullLogger;
|
||||
use Shlinkio\Shlink\Common\Logger\LoggerFactory;
|
||||
use Shlinkio\Shlink\Common\Logger\LoggerType;
|
||||
use Shlinkio\Shlink\Common\Middleware\AccessLogMiddleware;
|
||||
use Shlinkio\Shlink\Common\Middleware\RequestIdMiddleware;
|
||||
use Shlinkio\Shlink\Core\Config\EnvVars;
|
||||
use Shlinkio\Shlink\Core\EventDispatcher\Helper\RequestIdProvider;
|
||||
use Shlinkio\Shlink\EventDispatcher\Util\RequestIdProviderInterface;
|
||||
|
||||
use function Shlinkio\Shlink\Config\env;
|
||||
use function Shlinkio\Shlink\Config\runningInRoadRunner;
|
||||
|
||||
return (static function (): array {
|
||||
$isDev = EnvVars::isDevEnv();
|
||||
$common = [
|
||||
'level' => Level::Info->value,
|
||||
'processors' => [RequestId\MonologProcessor::class],
|
||||
'line_format' => '[%datetime%] [%extra.request_id%] %channel%.%level_name% - %message%',
|
||||
'level' => $isDev ? Level::Debug->value : Level::Info->value,
|
||||
'processors' => [RequestIdMiddleware::class],
|
||||
'line_format' =>
|
||||
'[%datetime%] [%extra.' . RequestIdMiddleware::ATTRIBUTE . '%] %channel%.%level_name% - %message%',
|
||||
];
|
||||
|
||||
// In dev env or the docker container, stream Shlink logs to stderr, otherwise send them to a file
|
||||
$useStreamForShlinkLogger = $isDev || env('SHLINK_RUNTIME') !== null;
|
||||
|
||||
return [
|
||||
|
||||
'logger' => [
|
||||
'Shlink' => [
|
||||
'Shlink' => $useStreamForShlinkLogger ? [
|
||||
'type' => LoggerType::STREAM->value,
|
||||
'destination' => 'php://stderr',
|
||||
...$common,
|
||||
] : [
|
||||
'type' => LoggerType::FILE->value,
|
||||
...$common,
|
||||
],
|
||||
@@ -43,24 +57,19 @@ return (static function (): array {
|
||||
'Logger_Shlink' => [LoggerFactory::class, 'Shlink'],
|
||||
'Logger_Access' => [LoggerFactory::class, 'Access'],
|
||||
NullLogger::class => InvokableFactory::class,
|
||||
RequestIdProvider::class => ConfigAbstractFactory::class,
|
||||
],
|
||||
'aliases' => [
|
||||
'logger' => 'Logger_Shlink',
|
||||
Logger::class => 'Logger_Shlink',
|
||||
LoggerInterface::class => 'Logger_Shlink',
|
||||
AccessLogMiddleware::LOGGER_SERVICE_NAME => 'Logger_Access',
|
||||
RequestIdProviderInterface::class => RequestIdProvider::class,
|
||||
],
|
||||
],
|
||||
|
||||
// Deprecated. Remove in Shlink 4.0.0
|
||||
'mezzio-swoole' => [
|
||||
'swoole-http-server' => [
|
||||
'logger' => [
|
||||
// Let's disable mezio-swoole access logging, so that we can provide our own implementation,
|
||||
// consistent for roadrunner and openswoole
|
||||
'logger-name' => NullLogger::class,
|
||||
],
|
||||
],
|
||||
ConfigAbstractFactory::class => [
|
||||
RequestIdProvider::class => [RequestIdMiddleware::class],
|
||||
],
|
||||
|
||||
];
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Monolog\Level;
|
||||
use Shlinkio\Shlink\Common\Logger\LoggerType;
|
||||
|
||||
return [
|
||||
|
||||
'logger' => [
|
||||
'Shlink' => [
|
||||
'type' => LoggerType::STREAM->value,
|
||||
'destination' => 'php://stderr',
|
||||
'level' => Level::Debug->value,
|
||||
],
|
||||
],
|
||||
|
||||
];
|
||||
@@ -1,16 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Shlinkio\Shlink\Core\Config\EnvVars;
|
||||
|
||||
return [
|
||||
|
||||
'matomo' => [
|
||||
'enabled' => (bool) EnvVars::MATOMO_ENABLED->loadFromEnv(false),
|
||||
'base_url' => EnvVars::MATOMO_BASE_URL->loadFromEnv(),
|
||||
'site_id' => EnvVars::MATOMO_SITE_ID->loadFromEnv(),
|
||||
'api_token' => EnvVars::MATOMO_API_TOKEN->loadFromEnv(),
|
||||
],
|
||||
|
||||
];
|
||||
@@ -1,26 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* Dev matomo instance needs to be manually configured once before enabling the configuration below.
|
||||
*
|
||||
* 1. Go to http://localhost:8003 and follow the installation instructions.
|
||||
* 2. Open data/infra/matomo/config/config.ini.php and replace `trusted_hosts[] = "localhost"` with
|
||||
* `trusted_hosts[] = "localhost:8003"` (see https://github.com/matomo-org/matomo/issues/9549)
|
||||
* 3. Go to http://localhost:8003/index.php?module=SitesManager&action=index and paste the ID for the site you just
|
||||
* created into the `site_id` field below.
|
||||
* 4. Go to http://localhost:8003/index.php?module=UsersManager&action=userSecurity, scroll down, click
|
||||
* "Create new token" and once generated, paste the token into the `api_token` field below.
|
||||
*/
|
||||
|
||||
return [
|
||||
|
||||
'matomo' => [
|
||||
// 'enabled' => true,
|
||||
// 'base_url' => 'http://shlink_matomo',
|
||||
// 'site_id' => '...',
|
||||
// 'api_token' => '...',
|
||||
],
|
||||
|
||||
];
|
||||
@@ -8,34 +8,31 @@ use Shlinkio\Shlink\Core\Config\EnvVars;
|
||||
use Symfony\Component\Mercure\Hub;
|
||||
use Symfony\Component\Mercure\HubInterface;
|
||||
|
||||
return (static function (): array {
|
||||
$publicUrl = EnvVars::MERCURE_PUBLIC_HUB_URL->loadFromEnv();
|
||||
return [
|
||||
|
||||
return [
|
||||
// This config is used by shlink-common. Do not delete
|
||||
'mercure' => [
|
||||
'public_hub_url' => EnvVars::MERCURE_PUBLIC_HUB_URL->loadFromEnv(),
|
||||
'internal_hub_url' => EnvVars::MERCURE_INTERNAL_HUB_URL->loadFromEnv(),
|
||||
'jwt_secret' => EnvVars::MERCURE_JWT_SECRET->loadFromEnv(),
|
||||
'jwt_issuer' => 'Shlink',
|
||||
],
|
||||
|
||||
'mercure' => [
|
||||
'public_hub_url' => $publicUrl,
|
||||
'internal_hub_url' => EnvVars::MERCURE_INTERNAL_HUB_URL->loadFromEnv($publicUrl),
|
||||
'jwt_secret' => EnvVars::MERCURE_JWT_SECRET->loadFromEnv(),
|
||||
'jwt_issuer' => 'Shlink',
|
||||
],
|
||||
|
||||
'dependencies' => [
|
||||
'delegators' => [
|
||||
LcobucciJwtProvider::class => [
|
||||
LazyServiceFactory::class,
|
||||
],
|
||||
Hub::class => [
|
||||
LazyServiceFactory::class,
|
||||
],
|
||||
'dependencies' => [
|
||||
'delegators' => [
|
||||
LcobucciJwtProvider::class => [
|
||||
LazyServiceFactory::class,
|
||||
],
|
||||
'lazy_services' => [
|
||||
'class_map' => [
|
||||
LcobucciJwtProvider::class => LcobucciJwtProvider::class,
|
||||
Hub::class => HubInterface::class,
|
||||
],
|
||||
Hub::class => [
|
||||
LazyServiceFactory::class,
|
||||
],
|
||||
],
|
||||
'lazy_services' => [
|
||||
'class_map' => [
|
||||
LcobucciJwtProvider::class => LcobucciJwtProvider::class,
|
||||
Hub::class => HubInterface::class,
|
||||
],
|
||||
],
|
||||
],
|
||||
|
||||
];
|
||||
})();
|
||||
];
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
return [
|
||||
|
||||
'mercure' => [
|
||||
'public_hub_url' => 'http://localhost:8001',
|
||||
'internal_hub_url' => 'http://shlink_mercure_proxy',
|
||||
'jwt_secret' => 'mercure_jwt_key_long_enough_to_avoid_error',
|
||||
],
|
||||
|
||||
];
|
||||
@@ -7,10 +7,10 @@ namespace Shlinkio\Shlink;
|
||||
use Laminas\Stratigility\Middleware\ErrorHandler;
|
||||
use Mezzio\ProblemDetails;
|
||||
use Mezzio\Router;
|
||||
use PhpMiddleware\RequestId\RequestIdMiddleware;
|
||||
use RKA\Middleware\IpAddress;
|
||||
use Shlinkio\Shlink\Common\Middleware\AccessLogMiddleware;
|
||||
use Shlinkio\Shlink\Common\Middleware\ContentLengthMiddleware;
|
||||
use Shlinkio\Shlink\Common\Middleware\RequestIdMiddleware;
|
||||
|
||||
return [
|
||||
|
||||
@@ -47,7 +47,6 @@ return [
|
||||
'rest' => [
|
||||
'path' => '/rest',
|
||||
'middleware' => [
|
||||
Rest\Middleware\ErrorHandler\BackwardsCompatibleProblemDetailsHandler::class,
|
||||
Router\Middleware\ImplicitOptionsMiddleware::class,
|
||||
Rest\Middleware\BodyParserMiddleware::class,
|
||||
Rest\Middleware\AuthenticationMiddleware::class,
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Shlinkio\Shlink\Core\Config\EnvVars;
|
||||
|
||||
use const Shlinkio\Shlink\DEFAULT_QR_CODE_ENABLED_FOR_DISABLED_SHORT_URLS;
|
||||
use const Shlinkio\Shlink\DEFAULT_QR_CODE_ERROR_CORRECTION;
|
||||
use const Shlinkio\Shlink\DEFAULT_QR_CODE_FORMAT;
|
||||
use const Shlinkio\Shlink\DEFAULT_QR_CODE_MARGIN;
|
||||
use const Shlinkio\Shlink\DEFAULT_QR_CODE_ROUND_BLOCK_SIZE;
|
||||
use const Shlinkio\Shlink\DEFAULT_QR_CODE_SIZE;
|
||||
|
||||
return [
|
||||
|
||||
'qr_codes' => [
|
||||
'size' => (int) EnvVars::DEFAULT_QR_CODE_SIZE->loadFromEnv(DEFAULT_QR_CODE_SIZE),
|
||||
'margin' => (int) EnvVars::DEFAULT_QR_CODE_MARGIN->loadFromEnv(DEFAULT_QR_CODE_MARGIN),
|
||||
'format' => EnvVars::DEFAULT_QR_CODE_FORMAT->loadFromEnv(DEFAULT_QR_CODE_FORMAT),
|
||||
'error_correction' => EnvVars::DEFAULT_QR_CODE_ERROR_CORRECTION->loadFromEnv(
|
||||
DEFAULT_QR_CODE_ERROR_CORRECTION,
|
||||
),
|
||||
'round_block_size' => (bool) EnvVars::DEFAULT_QR_CODE_ROUND_BLOCK_SIZE->loadFromEnv(
|
||||
DEFAULT_QR_CODE_ROUND_BLOCK_SIZE,
|
||||
),
|
||||
'enabled_for_disabled_short_urls' => (bool) EnvVars::QR_CODE_FOR_DISABLED_SHORT_URLS->loadFromEnv(
|
||||
DEFAULT_QR_CODE_ENABLED_FOR_DISABLED_SHORT_URLS,
|
||||
),
|
||||
],
|
||||
|
||||
];
|
||||
@@ -6,17 +6,15 @@ use Shlinkio\Shlink\Core\Config\EnvVars;
|
||||
|
||||
return [
|
||||
|
||||
// This config is used by shlink-common. Do not delete
|
||||
'rabbitmq' => [
|
||||
'enabled' => (bool) EnvVars::RABBITMQ_ENABLED->loadFromEnv(false),
|
||||
'enabled' => (bool) EnvVars::RABBITMQ_ENABLED->loadFromEnv(),
|
||||
'host' => EnvVars::RABBITMQ_HOST->loadFromEnv(),
|
||||
'use_ssl' => (bool) EnvVars::RABBITMQ_USE_SSL->loadFromEnv(false),
|
||||
'port' => (int) EnvVars::RABBITMQ_PORT->loadFromEnv('5672'),
|
||||
'use_ssl' => (bool) EnvVars::RABBITMQ_USE_SSL->loadFromEnv(),
|
||||
'port' => (int) EnvVars::RABBITMQ_PORT->loadFromEnv(),
|
||||
'user' => EnvVars::RABBITMQ_USER->loadFromEnv(),
|
||||
'password' => EnvVars::RABBITMQ_PASSWORD->loadFromEnv(),
|
||||
'vhost' => EnvVars::RABBITMQ_VHOST->loadFromEnv('/'),
|
||||
|
||||
// Deprecated
|
||||
'legacy_visits_publishing' => (bool) EnvVars::RABBITMQ_LEGACY_VISITS_PUBLISHING->loadFromEnv(false),
|
||||
'vhost' => EnvVars::RABBITMQ_VHOST->loadFromEnv(),
|
||||
],
|
||||
|
||||
];
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
return [
|
||||
|
||||
'rabbitmq' => [
|
||||
'enabled' => true,
|
||||
'host' => 'shlink_rabbitmq',
|
||||
'user' => 'rabbit',
|
||||
'password' => 'rabbit',
|
||||
],
|
||||
|
||||
];
|
||||
@@ -1,25 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Shlinkio\Shlink\Core\Config\EnvVars;
|
||||
|
||||
use const Shlinkio\Shlink\DEFAULT_REDIRECT_CACHE_LIFETIME;
|
||||
use const Shlinkio\Shlink\DEFAULT_REDIRECT_STATUS_CODE;
|
||||
|
||||
return [
|
||||
|
||||
'not_found_redirects' => [
|
||||
'invalid_short_url' => EnvVars::DEFAULT_INVALID_SHORT_URL_REDIRECT->loadFromEnv(),
|
||||
'regular_404' => EnvVars::DEFAULT_REGULAR_404_REDIRECT->loadFromEnv(),
|
||||
'base_url' => EnvVars::DEFAULT_BASE_URL_REDIRECT->loadFromEnv(),
|
||||
],
|
||||
|
||||
'redirects' => [
|
||||
'redirect_status_code' => (int) EnvVars::REDIRECT_STATUS_CODE->loadFromEnv(DEFAULT_REDIRECT_STATUS_CODE->value),
|
||||
'redirect_cache_lifetime' => (int) EnvVars::REDIRECT_CACHE_LIFETIME->loadFromEnv(
|
||||
DEFAULT_REDIRECT_CACHE_LIFETIME,
|
||||
),
|
||||
],
|
||||
|
||||
];
|
||||
@@ -1,26 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
return [
|
||||
|
||||
'cache' => [
|
||||
'redis' => [
|
||||
'servers' => 'tcp://shlink_redis:6379',
|
||||
// 'servers' => 'tcp://barbar@shlink_redis_acl:6379',
|
||||
// 'servers' => 'tcp://foo:bar@shlink_redis_acl:6379',
|
||||
],
|
||||
],
|
||||
|
||||
'redis' => [
|
||||
'pub_sub_enabled' => true,
|
||||
],
|
||||
|
||||
'dependencies' => [
|
||||
'aliases' => [
|
||||
// With this config, a user could alias 'lock_store' => 'redis_lock_store' to override the default
|
||||
// 'lock_store' => 'redis_lock_store',
|
||||
],
|
||||
],
|
||||
|
||||
];
|
||||
@@ -1,44 +0,0 @@
|
||||
<?php
|
||||
|
||||
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 [
|
||||
|
||||
'request_id' => [
|
||||
'allow_override' => true,
|
||||
'header_name' => 'X-Request-Id',
|
||||
],
|
||||
|
||||
'dependencies' => [
|
||||
'factories' => [
|
||||
RequestId\Generator\RamseyUuid4StaticGenerator::class => InvokableFactory::class,
|
||||
RequestId\RequestIdProviderFactory::class => ConfigAbstractFactory::class,
|
||||
RequestId\RequestIdMiddleware::class => ConfigAbstractFactory::class,
|
||||
RequestId\MonologProcessor::class => ConfigAbstractFactory::class,
|
||||
],
|
||||
'delegators' => [
|
||||
RequestId\MonologProcessor::class => [
|
||||
BackwardsCompatibleMonologProcessorDelegator::class,
|
||||
],
|
||||
],
|
||||
],
|
||||
|
||||
ConfigAbstractFactory::class => [
|
||||
RequestId\RequestIdProviderFactory::class => [
|
||||
RequestId\Generator\RamseyUuid4StaticGenerator::class,
|
||||
'config.request_id.allow_override',
|
||||
'config.request_id.header_name',
|
||||
],
|
||||
RequestId\RequestIdMiddleware::class => [
|
||||
RequestId\RequestIdProviderFactory::class,
|
||||
'config.request_id.header_name',
|
||||
],
|
||||
RequestId\MonologProcessor::class => [RequestId\RequestIdMiddleware::class],
|
||||
],
|
||||
|
||||
];
|
||||
@@ -8,12 +8,12 @@ use Shlinkio\Shlink\Core\Config\EnvVars;
|
||||
return [
|
||||
|
||||
'router' => [
|
||||
'base_path' => EnvVars::BASE_PATH->loadFromEnv(''),
|
||||
'base_path' => EnvVars::BASE_PATH->loadFromEnv(),
|
||||
|
||||
'fastroute' => [
|
||||
// Disabling config cache for cli, ensures it's never used for openswoole/RoadRunner, and also that console
|
||||
// Disabling config cache for cli, ensures it's never used for RoadRunner, and also that console
|
||||
// commands don't generate a cache file that's then used by php-fpm web executions
|
||||
FastRouteRouter::CONFIG_CACHE_ENABLED => PHP_SAPI !== 'cli',
|
||||
FastRouteRouter::CONFIG_CACHE_ENABLED => EnvVars::isProdEnv() && PHP_SAPI !== 'cli',
|
||||
FastRouteRouter::CONFIG_CACHE_FILE => 'data/cache/fastroute_cached_routes.php',
|
||||
],
|
||||
],
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Mezzio\Router\FastRouteRouter;
|
||||
|
||||
return [
|
||||
|
||||
'router' => [
|
||||
// 'base_path' => '',
|
||||
'fastroute' => [
|
||||
FastRouteRouter::CONFIG_CACHE_ENABLED => false,
|
||||
],
|
||||
],
|
||||
|
||||
];
|
||||
@@ -17,12 +17,9 @@ use Shlinkio\Shlink\Rest\Middleware\Mercure\NotConfiguredMercureErrorHandler;
|
||||
use function sprintf;
|
||||
|
||||
return (static function (): array {
|
||||
$contentNegotiationMiddleware = Middleware\ShortUrl\CreateShortUrlContentNegotiationMiddleware::class;
|
||||
$dropDomainMiddleware = Middleware\ShortUrl\DropDefaultDomainFromRequestMiddleware::class;
|
||||
$overrideDomainMiddleware = Middleware\ShortUrl\OverrideDomainMiddleware::class;
|
||||
|
||||
// TODO This should be based on config, not the env var
|
||||
$shortUrlRouteSuffix = EnvVars::SHORT_URL_TRAILING_SLASH->loadFromEnv(false) ? '[/]' : '';
|
||||
$shortUrlRouteSuffix = EnvVars::SHORT_URL_TRAILING_SLASH->loadFromEnv() ? '[/]' : '';
|
||||
|
||||
return [
|
||||
|
||||
@@ -32,9 +29,10 @@ return (static function (): array {
|
||||
...ConfigProvider::applyRoutesPrefix([
|
||||
Action\HealthAction::getRouteDef(),
|
||||
|
||||
// Visits and rules routes must go first, as they have a more specific path, otherwise, when
|
||||
// multi-segment slugs are enabled, routes with a less-specific path might match first
|
||||
|
||||
// Visits.
|
||||
// These routes must go first, as they have a more specific path, otherwise, when multi-segment slugs
|
||||
// are enabled, routes with a less-specific path might match first
|
||||
Action\Visit\ShortUrlVisitsAction::getRouteDef([$dropDomainMiddleware]),
|
||||
Action\ShortUrl\DeleteShortUrlVisitsAction::getRouteDef([$dropDomainMiddleware]),
|
||||
Action\Visit\TagVisitsAction::getRouteDef(),
|
||||
@@ -44,15 +42,18 @@ return (static function (): array {
|
||||
Action\Visit\DeleteOrphanVisitsAction::getRouteDef(),
|
||||
Action\Visit\NonOrphanVisitsAction::getRouteDef(),
|
||||
|
||||
//Redirect rules
|
||||
Action\RedirectRule\ListRedirectRulesAction::getRouteDef([$dropDomainMiddleware]),
|
||||
Action\RedirectRule\SetRedirectRulesAction::getRouteDef([$dropDomainMiddleware]),
|
||||
|
||||
// Short URLs
|
||||
Action\ShortUrl\CreateShortUrlAction::getRouteDef([
|
||||
$contentNegotiationMiddleware,
|
||||
$dropDomainMiddleware,
|
||||
$overrideDomainMiddleware,
|
||||
Middleware\ShortUrl\DefaultShortCodesLengthMiddleware::class,
|
||||
]),
|
||||
Action\ShortUrl\SingleStepCreateShortUrlAction::getRouteDef([
|
||||
$contentNegotiationMiddleware,
|
||||
Middleware\ShortUrl\CreateShortUrlContentNegotiationMiddleware::class,
|
||||
$overrideDomainMiddleware,
|
||||
]),
|
||||
Action\ShortUrl\EditShortUrlAction::getRouteDef([$dropDomainMiddleware]),
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Shlinkio\Shlink\Core\Config\EnvVars;
|
||||
|
||||
use function Shlinkio\Shlink\Config\getOpenswooleConfigFromEnv;
|
||||
|
||||
use const Shlinkio\Shlink\MIN_TASK_WORKERS;
|
||||
|
||||
return (static function (): array {
|
||||
$taskWorkers = (int) EnvVars::TASK_WORKER_NUM->loadFromEnv(16);
|
||||
|
||||
return [
|
||||
|
||||
'mezzio-swoole' => [
|
||||
// Setting this to true can have unexpected behaviors when running several concurrent slow DB queries
|
||||
'enable_coroutine' => false,
|
||||
|
||||
'swoole-http-server' => [
|
||||
'host' => '0.0.0.0',
|
||||
'port' => (int) EnvVars::PORT->loadFromEnv(8080),
|
||||
'process-name' => 'shlink',
|
||||
|
||||
'options' => [
|
||||
...getOpenswooleConfigFromEnv(),
|
||||
'worker_num' => (int) EnvVars::WEB_WORKER_NUM->loadFromEnv(16),
|
||||
'task_worker_num' => max($taskWorkers, MIN_TASK_WORKERS),
|
||||
],
|
||||
],
|
||||
],
|
||||
|
||||
];
|
||||
})();
|
||||
@@ -1,13 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
return [
|
||||
|
||||
'mezzio-swoole' => [
|
||||
'hot-code-reload' => [
|
||||
'enable' => true,
|
||||
],
|
||||
],
|
||||
|
||||
];
|
||||
@@ -1,43 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Shlinkio\Shlink\Core\Config\EnvVars;
|
||||
|
||||
return (static function (): array {
|
||||
/** @var string|null $disableTrackingFrom */
|
||||
$disableTrackingFrom = EnvVars::DISABLE_TRACKING_FROM->loadFromEnv();
|
||||
|
||||
return [
|
||||
|
||||
'tracking' => [
|
||||
// Tells if IP addresses should be anonymized before persisting, to fulfil data protection regulations
|
||||
// This applies only if IP address tracking is enabled
|
||||
'anonymize_remote_addr' => (bool) EnvVars::ANONYMIZE_REMOTE_ADDR->loadFromEnv(true),
|
||||
|
||||
// Tells if visits to not-found URLs should be tracked. The disable_tracking option takes precedence
|
||||
'track_orphan_visits' => (bool) EnvVars::TRACK_ORPHAN_VISITS->loadFromEnv(true),
|
||||
|
||||
// A query param that, if provided, will disable tracking of one particular visit. Always takes precedence
|
||||
'disable_track_param' => EnvVars::DISABLE_TRACK_PARAM->loadFromEnv(),
|
||||
|
||||
// If true, visits will not be tracked at all
|
||||
'disable_tracking' => (bool) EnvVars::DISABLE_TRACKING->loadFromEnv(false),
|
||||
|
||||
// If true, visits will be tracked, but neither the IP address, nor the location will be resolved
|
||||
'disable_ip_tracking' => (bool) EnvVars::DISABLE_IP_TRACKING->loadFromEnv(false),
|
||||
|
||||
// If true, the referrer will not be tracked
|
||||
'disable_referrer_tracking' => (bool) EnvVars::DISABLE_REFERRER_TRACKING->loadFromEnv(false),
|
||||
|
||||
// If true, the user agent will not be tracked
|
||||
'disable_ua_tracking' => (bool) EnvVars::DISABLE_UA_TRACKING->loadFromEnv(false),
|
||||
|
||||
// A list of IP addresses, patterns or CIDR blocks from which tracking is disabled by default
|
||||
'disable_tracking_from' => $disableTrackingFrom === null
|
||||
? []
|
||||
: array_map(trim(...), explode(',', $disableTrackingFrom)),
|
||||
],
|
||||
|
||||
];
|
||||
})();
|
||||
@@ -1,35 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Shlinkio\Shlink\Core\Config\EnvVars;
|
||||
use Shlinkio\Shlink\Core\ShortUrl\Model\ShortUrlMode;
|
||||
|
||||
use const Shlinkio\Shlink\DEFAULT_SHORT_CODES_LENGTH;
|
||||
use const Shlinkio\Shlink\MIN_SHORT_CODES_LENGTH;
|
||||
|
||||
return (static function (): array {
|
||||
$shortCodesLength = max(
|
||||
(int) EnvVars::DEFAULT_SHORT_CODES_LENGTH->loadFromEnv(DEFAULT_SHORT_CODES_LENGTH),
|
||||
MIN_SHORT_CODES_LENGTH,
|
||||
);
|
||||
$modeFromEnv = EnvVars::SHORT_URL_MODE->loadFromEnv(ShortUrlMode::STRICT->value);
|
||||
$mode = ShortUrlMode::tryDeprecated($modeFromEnv) ?? ShortUrlMode::STRICT;
|
||||
|
||||
return [
|
||||
|
||||
'url_shortener' => [
|
||||
'domain' => [ // TODO Refactor this structure to url_shortener.schema and url_shortener.default_domain
|
||||
'schema' => ((bool) EnvVars::IS_HTTPS_ENABLED->loadFromEnv(true)) ? 'https' : 'http',
|
||||
'hostname' => EnvVars::DEFAULT_DOMAIN->loadFromEnv(''),
|
||||
],
|
||||
'default_short_codes_length' => $shortCodesLength,
|
||||
'auto_resolve_titles' => (bool) EnvVars::AUTO_RESOLVE_TITLES->loadFromEnv(false),
|
||||
'append_extra_path' => (bool) EnvVars::REDIRECT_APPEND_EXTRA_PATH->loadFromEnv(false),
|
||||
'multi_segment_slugs_enabled' => (bool) EnvVars::MULTI_SEGMENT_SLUGS_ENABLED->loadFromEnv(false),
|
||||
'trailing_slash_enabled' => (bool) EnvVars::SHORT_URL_TRAILING_SLASH->loadFromEnv(false),
|
||||
'mode' => $mode,
|
||||
],
|
||||
|
||||
];
|
||||
})();
|
||||
@@ -1,24 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use function Shlinkio\Shlink\Config\runningInOpenswoole;
|
||||
use function Shlinkio\Shlink\Config\runningInRoadRunner;
|
||||
|
||||
return [
|
||||
|
||||
'url_shortener' => [
|
||||
'domain' => [
|
||||
'schema' => 'http',
|
||||
'hostname' => sprintf('localhost:%s', match (true) {
|
||||
runningInRoadRunner() => '8800',
|
||||
runningInOpenswoole() => '8080',
|
||||
default => '8000',
|
||||
}),
|
||||
],
|
||||
'auto_resolve_titles' => true,
|
||||
// 'multi_segment_slugs_enabled' => true,
|
||||
// 'trailing_slash_enabled' => true,
|
||||
],
|
||||
|
||||
];
|
||||
@@ -1,20 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Shlinkio\Shlink\Core\Config\EnvVars;
|
||||
|
||||
// Deprecated. Webhooks are no longer supported. To be removed in Shlink 4.0.0
|
||||
return (static function (): array {
|
||||
$webhooks = EnvVars::VISITS_WEBHOOKS->loadFromEnv();
|
||||
|
||||
return [
|
||||
|
||||
'visits_webhooks' => [
|
||||
'webhooks' => $webhooks === null ? [] : explode(',', $webhooks),
|
||||
'notify_orphan_visits_to_webhooks' =>
|
||||
(bool) EnvVars::NOTIFY_ORPHAN_VISITS_TO_WEBHOOKS->loadFromEnv(false),
|
||||
],
|
||||
|
||||
];
|
||||
})();
|
||||
@@ -8,31 +8,13 @@ use Laminas\ConfigAggregator;
|
||||
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\Config\env;
|
||||
use function Shlinkio\Shlink\Config\openswooleIsInstalled;
|
||||
use function Shlinkio\Shlink\Config\runningInRoadRunner;
|
||||
use function Shlinkio\Shlink\Core\enumValues;
|
||||
|
||||
use const PHP_SAPI;
|
||||
|
||||
$isTestEnv = env('APP_ENV') === 'test';
|
||||
$enableSwoole = PHP_SAPI === 'cli' && openswooleIsInstalled() && ! runningInRoadRunner();
|
||||
use Shlinkio\Shlink\Core\Config\EnvVars;
|
||||
|
||||
return (new ConfigAggregator\ConfigAggregator(
|
||||
providers: [
|
||||
! $isTestEnv
|
||||
? new EnvVarLoaderProvider('config/params/generated_config.php', enumValues(Core\Config\EnvVars::class))
|
||||
: new ConfigAggregator\ArrayProvider([]),
|
||||
Mezzio\ConfigProvider::class,
|
||||
Mezzio\Router\ConfigProvider::class,
|
||||
Mezzio\Router\FastRouteRouter\ConfigProvider::class,
|
||||
$enableSwoole && class_exists(Swoole\ConfigProvider::class)
|
||||
? Swoole\ConfigProvider::class
|
||||
: new ConfigAggregator\ArrayProvider([]),
|
||||
ProblemDetails\ConfigProvider::class,
|
||||
Diactoros\ConfigProvider::class,
|
||||
Common\ConfigProvider::class,
|
||||
@@ -44,10 +26,10 @@ return (new ConfigAggregator\ConfigAggregator(
|
||||
CLI\ConfigProvider::class,
|
||||
Rest\ConfigProvider::class,
|
||||
new ConfigAggregator\PhpFileProvider('config/autoload/{,*.}global.php'),
|
||||
// Local config should not be loaded during tests, whereas test config should be loaded ONLY during tests
|
||||
new ConfigAggregator\PhpFileProvider(
|
||||
$isTestEnv ? 'config/test/*.global.php' : 'config/autoload/{,*.}local.php',
|
||||
),
|
||||
// Test config should be loaded ONLY during tests
|
||||
EnvVars::isTestEnv()
|
||||
? new ConfigAggregator\PhpFileProvider('config/test/*.global.php')
|
||||
: new ConfigAggregator\ArrayProvider([]),
|
||||
// Routes have to be loaded last
|
||||
new ConfigAggregator\PhpFileProvider('config/autoload/routes.config.php'),
|
||||
],
|
||||
|
||||
@@ -9,16 +9,15 @@ use Shlinkio\Shlink\Core\Util\RedirectStatus;
|
||||
const DEFAULT_DELETE_SHORT_URL_THRESHOLD = 15;
|
||||
const DEFAULT_SHORT_CODES_LENGTH = 5;
|
||||
const MIN_SHORT_CODES_LENGTH = 4;
|
||||
const DEFAULT_REDIRECT_STATUS_CODE = RedirectStatus::STATUS_302; // Deprecated. Default to 307 for Shlink v4
|
||||
const DEFAULT_REDIRECT_STATUS_CODE = RedirectStatus::STATUS_302;
|
||||
const DEFAULT_REDIRECT_CACHE_LIFETIME = 30;
|
||||
const LOCAL_LOCK_FACTORY = 'Shlinkio\Shlink\LocalLockFactory';
|
||||
const TITLE_TAG_VALUE = '/<title[^>]*>(.*?)<\/title>/i'; // Matches the value inside a html title tag
|
||||
const LOOSE_URI_MATCHER = '/(.+)\:(.+)/i'; // Matches anything starting with a schema.
|
||||
const DEFAULT_QR_CODE_SIZE = 300;
|
||||
const DEFAULT_QR_CODE_MARGIN = 0;
|
||||
const DEFAULT_QR_CODE_FORMAT = 'png';
|
||||
const DEFAULT_QR_CODE_ERROR_CORRECTION = 'l';
|
||||
const DEFAULT_QR_CODE_ROUND_BLOCK_SIZE = true;
|
||||
// Deprecated. Shlink 4.0.0 should change default value to `true`
|
||||
const DEFAULT_QR_CODE_ENABLED_FOR_DISABLED_SHORT_URLS = false;
|
||||
const MIN_TASK_WORKERS = 4;
|
||||
const DEFAULT_QR_CODE_ENABLED_FOR_DISABLED_SHORT_URLS = true;
|
||||
const DEFAULT_QR_CODE_COLOR = '#000000'; // Black
|
||||
const DEFAULT_QR_CODE_BG_COLOR = '#ffffff'; // White
|
||||
|
||||
@@ -6,25 +6,24 @@ use Laminas\ServiceManager\ServiceManager;
|
||||
use Shlinkio\Shlink\Core\Config\EnvVars;
|
||||
use Symfony\Component\Lock;
|
||||
|
||||
use function Shlinkio\Shlink\Config\loadEnvVarsFromConfig;
|
||||
use function Shlinkio\Shlink\Core\enumValues;
|
||||
|
||||
use const Shlinkio\Shlink\LOCAL_LOCK_FACTORY;
|
||||
|
||||
chdir(dirname(__DIR__));
|
||||
|
||||
require 'vendor/autoload.php';
|
||||
|
||||
// Workaround to make this compatible with both openswoole 22 and earlier versions.
|
||||
// Openswoole support is deprecated. Remove in v4.0.0
|
||||
if (! function_exists('swoole_set_process_name')) {
|
||||
// phpcs:disable
|
||||
function swoole_set_process_name(string $name): void
|
||||
{
|
||||
OpenSwoole\Util::setProcessName($name);
|
||||
}
|
||||
// phpcs:enable
|
||||
}
|
||||
// Promote env vars from installer, dev config or test config
|
||||
loadEnvVarsFromConfig(
|
||||
EnvVars::isTestEnv() ? 'config/test/shlink_test_env.php' : 'config/params/*.php',
|
||||
enumValues(EnvVars::class),
|
||||
);
|
||||
|
||||
// This is one of the first files loaded. Configure the timezone here
|
||||
date_default_timezone_set(EnvVars::TIMEZONE->loadFromEnv(date_default_timezone_get()));
|
||||
// This is one of the first files loaded. Configure the timezone and memory limit here
|
||||
ini_set('memory_limit', EnvVars::MEMORY_LIMIT->loadFromEnv());
|
||||
date_default_timezone_set(EnvVars::TIMEZONE->loadFromEnv());
|
||||
|
||||
// This class alias tricks the ConfigAbstractFactory to return Lock\Factory instances even with a different service name
|
||||
// It needs to be placed here as individual config files will not be loaded once config is cached
|
||||
|
||||
1
config/params/.gitignore
vendored
1
config/params/.gitignore
vendored
@@ -1,2 +1,3 @@
|
||||
*
|
||||
!.gitignore
|
||||
!*.dist
|
||||
|
||||
76
config/params/shlink_dev_env.php.dist
Normal file
76
config/params/shlink_dev_env.php.dist
Normal file
@@ -0,0 +1,76 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Shlinkio\Shlink\Core\Config\EnvVars;
|
||||
|
||||
return [
|
||||
|
||||
EnvVars::APP_ENV->value => 'dev',
|
||||
// EnvVars::GEOLITE_LICENSE_KEY->value => '',
|
||||
|
||||
// URL shortener
|
||||
EnvVars::DEFAULT_DOMAIN->value => 'localhost:8800',
|
||||
EnvVars::IS_HTTPS_ENABLED->value => false,
|
||||
|
||||
// Database - MySQL
|
||||
EnvVars::DB_DRIVER->value => 'mysql',
|
||||
EnvVars::DB_USER->value => 'root',
|
||||
EnvVars::DB_PASSWORD->value => 'root',
|
||||
EnvVars::DB_NAME->value => 'shlink',
|
||||
// EnvVars::DB_NAME->value => 'shlink_foo',
|
||||
EnvVars::DB_HOST->value => 'shlink_db_mysql',
|
||||
|
||||
// Database - Maria
|
||||
// EnvVars::DB_DRIVER->value => 'maria',
|
||||
// EnvVars::DB_USER->value => 'root',
|
||||
// EnvVars::DB_PASSWORD->value => 'root',
|
||||
// EnvVars::DB_NAME->value => 'shlink_foo',
|
||||
// EnvVars::DB_HOST->value => 'shlink_db_maria',
|
||||
|
||||
// Database - Postgres
|
||||
// EnvVars::DB_DRIVER->value => 'postgres',
|
||||
// EnvVars::DB_USER->value => 'postgres',
|
||||
// EnvVars::DB_PASSWORD->value => 'root',
|
||||
// EnvVars::DB_NAME->value => 'shlink_foo',
|
||||
// EnvVars::DB_HOST->value => 'shlink_db_postgres',
|
||||
|
||||
// Database - MSSQL
|
||||
// EnvVars::DB_DRIVER->value => 'mssql',
|
||||
// EnvVars::DB_USER->value => 'sa',
|
||||
// EnvVars::DB_PASSWORD->value => 'Passw0rd!',
|
||||
// EnvVars::DB_NAME->value => 'shlink_foo',
|
||||
// EnvVars::DB_HOST->value => 'shlink_db_ms',
|
||||
|
||||
// Matomo
|
||||
// Dev matomo instance needs to be manually configured once before enabling the configuration below:
|
||||
// 1. Go to http://localhost:8003 and follow the installation instructions.
|
||||
// 2. Open data/infra/matomo/config/config.ini.php and replace `trusted_hosts[] = "localhost"` with
|
||||
// `trusted_hosts[] = "localhost:8003"` (see https://github.com/matomo-org/matomo/issues/9549)
|
||||
// 3. Go to http://localhost:8003/index.php?module=SitesManager&action=index and paste the ID for the site you just
|
||||
// created into the `MATOMO_SITE_ID` var below.
|
||||
// 4. Go to http://localhost:8003/index.php?module=UsersManager&action=userSecurity, scroll down, click
|
||||
// "Create new token" and once generated, paste the token into the `MATOMO_API_TOKEN` var below.
|
||||
// 5. Copy the config below and paste it in a new shlink-dev.local.env file.
|
||||
EnvVars::MATOMO_ENABLED->value => false,
|
||||
EnvVars::MATOMO_BASE_URL->value => 'http://shlink_matomo',
|
||||
// EnvVars::MATOMO_SITE_ID->value => ,
|
||||
// EnvVars::MATOMO_API_TOKEN->value => ,
|
||||
|
||||
// Mercure
|
||||
EnvVars::MERCURE_PUBLIC_HUB_URL->value => 'http://localhost:8002',
|
||||
EnvVars::MERCURE_INTERNAL_HUB_URL->value => 'http://shlink_mercure_proxy',
|
||||
EnvVars::MERCURE_JWT_SECRET->value => 'mercure_jwt_key_long_enough_to_avoid_error',
|
||||
|
||||
// RabbitMQ
|
||||
EnvVars::RABBITMQ_ENABLED->value => true,
|
||||
EnvVars::RABBITMQ_HOST->value => 'shlink_rabbitmq',
|
||||
EnvVars::RABBITMQ_PORT->value => 5672,
|
||||
EnvVars::RABBITMQ_USER->value => 'rabbit',
|
||||
EnvVars::RABBITMQ_PASSWORD->value => 'rabbit',
|
||||
|
||||
// Redis
|
||||
EnvVars::REDIS_PUB_SUB_ENABLED->value => true,
|
||||
EnvVars::REDIS_SERVERS->value => 'tcp://shlink_redis:6379',
|
||||
|
||||
];
|
||||
49
config/roadrunner/.rr.test.yml
Normal file
49
config/roadrunner/.rr.test.yml
Normal file
@@ -0,0 +1,49 @@
|
||||
version: '3'
|
||||
|
||||
############################################################################################
|
||||
# Routes here need to be relative to the project root, as API tests are run with `-w .` #
|
||||
# See https://github.com/orgs/roadrunner-server/discussions/1440#discussioncomment-8486186 #
|
||||
############################################################################################
|
||||
|
||||
rpc:
|
||||
listen: tcp://127.0.0.1:6001
|
||||
|
||||
server:
|
||||
command: 'php ./bin/roadrunner-worker.php'
|
||||
|
||||
http:
|
||||
address: '0.0.0.0:9999'
|
||||
middleware: ['static']
|
||||
static:
|
||||
dir: './public'
|
||||
forbid: ['.php', '.htaccess']
|
||||
pool:
|
||||
num_workers: 1
|
||||
debug: false
|
||||
|
||||
jobs:
|
||||
pool:
|
||||
num_workers: 1
|
||||
debug: false
|
||||
timeout: 300
|
||||
consume: ['shlink']
|
||||
pipelines:
|
||||
shlink:
|
||||
driver: memory
|
||||
config:
|
||||
priority: 10
|
||||
prefetch: 10
|
||||
|
||||
logs:
|
||||
encoding: json
|
||||
mode: development
|
||||
channels:
|
||||
http:
|
||||
mode: 'off' # Disable logging as Shlink handles it internally
|
||||
server:
|
||||
encoding: json
|
||||
level: info
|
||||
metrics:
|
||||
level: panic
|
||||
jobs:
|
||||
level: panic
|
||||
@@ -7,12 +7,6 @@ namespace Shlinkio\Shlink\TestUtils;
|
||||
use Doctrine\ORM\EntityManager;
|
||||
use Psr\Container\ContainerInterface;
|
||||
|
||||
use function register_shutdown_function;
|
||||
use function sprintf;
|
||||
|
||||
use const ShlinkioTest\Shlink\API_TESTS_HOST;
|
||||
use const ShlinkioTest\Shlink\API_TESTS_PORT;
|
||||
|
||||
/** @var ContainerInterface $container */
|
||||
$container = require __DIR__ . '/../container.php';
|
||||
$testHelper = $container->get(Helper\TestHelper::class);
|
||||
@@ -20,14 +14,6 @@ $config = $container->get('config');
|
||||
$em = $container->get(EntityManager::class);
|
||||
$httpClient = $container->get('shlink_test_api_client');
|
||||
|
||||
// Dump code coverage when process shuts down
|
||||
register_shutdown_function(function () use ($httpClient): void {
|
||||
$httpClient->request(
|
||||
'GET',
|
||||
sprintf('http://%s:%s/api-tests/stop-coverage', API_TESTS_HOST, API_TESTS_PORT),
|
||||
);
|
||||
});
|
||||
|
||||
$testHelper->createTestDb(
|
||||
createDbCommand: ['bin/cli', 'db:create'],
|
||||
migrateDbCommand: ['bin/cli', 'db:migrate'],
|
||||
|
||||
15
config/test/shlink_test_env.php
Normal file
15
config/test/shlink_test_env.php
Normal file
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Shlinkio\Shlink\Core\Config\EnvVars;
|
||||
|
||||
return [
|
||||
|
||||
EnvVars::APP_ENV->value => 'test',
|
||||
|
||||
// URL shortener
|
||||
EnvVars::DEFAULT_DOMAIN->value => 's.test',
|
||||
EnvVars::IS_HTTPS_ENABLED->value => false,
|
||||
|
||||
];
|
||||
@@ -6,75 +6,38 @@ namespace Shlinkio\Shlink;
|
||||
|
||||
use GuzzleHttp\Client;
|
||||
use Laminas\ConfigAggregator\ConfigAggregator;
|
||||
use Laminas\Diactoros\Response\EmptyResponse;
|
||||
use Laminas\Diactoros\Response\HtmlResponse;
|
||||
use Laminas\ServiceManager\Factory\InvokableFactory;
|
||||
use League\Event\EventDispatcher;
|
||||
use Mezzio\Router\FastRouteRouter;
|
||||
use Monolog\Level;
|
||||
use PHPUnit\Runner\Version;
|
||||
use Psr\Container\ContainerInterface;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Psr\Http\Server\RequestHandlerInterface;
|
||||
use SebastianBergmann\CodeCoverage\CodeCoverage;
|
||||
use SebastianBergmann\CodeCoverage\Driver\Selector;
|
||||
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 Shlinkio\Shlink\TestUtils\ApiTest\CoverageMiddleware;
|
||||
use Shlinkio\Shlink\TestUtils\CliTest\CliCoverageDelegator;
|
||||
use Shlinkio\Shlink\TestUtils\Helper\CoverageHelper;
|
||||
use Symfony\Component\Console\Application;
|
||||
use Symfony\Component\Console\Event\ConsoleCommandEvent;
|
||||
use Symfony\Component\Console\Event\ConsoleTerminateEvent;
|
||||
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
|
||||
|
||||
use function file_exists;
|
||||
use function Laminas\Stratigility\middleware;
|
||||
use function Shlinkio\Shlink\Config\env;
|
||||
use function Shlinkio\Shlink\Core\ArrayUtils\contains;
|
||||
use function sleep;
|
||||
use function sprintf;
|
||||
use function sys_get_temp_dir;
|
||||
|
||||
use const ShlinkioTest\Shlink\API_TESTS_HOST;
|
||||
use const ShlinkioTest\Shlink\API_TESTS_PORT;
|
||||
|
||||
$isApiTest = env('TEST_ENV') === 'api';
|
||||
$isCliTest = env('TEST_ENV') === 'cli';
|
||||
$testEnv = env('TEST_ENV');
|
||||
$isApiTest = $testEnv === 'api';
|
||||
$isCliTest = $testEnv === 'cli';
|
||||
$isE2eTest = $isApiTest || $isCliTest;
|
||||
|
||||
$coverageType = env('GENERATE_COVERAGE');
|
||||
$generateCoverage = contains($coverageType, ['yes', 'pretty']);
|
||||
|
||||
$coverage = null;
|
||||
if ($isE2eTest && $generateCoverage) {
|
||||
$filter = new Filter();
|
||||
$filter->includeDirectory(__DIR__ . '/../../module/Core/src');
|
||||
$filter->includeDirectory(__DIR__ . '/../../module/' . ($isApiTest ? 'Rest' : 'CLI') . '/src');
|
||||
$coverage = new CodeCoverage((new Selector())->forLineCoverage($filter), $filter);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param 'api'|'cli' $type
|
||||
*/
|
||||
$exportCoverage = static function (string $type = 'api') use (&$coverage, $coverageType): void {
|
||||
if ($coverage === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
$basePath = __DIR__ . '/../../build/coverage-' . $type;
|
||||
$covPath = $basePath . '.cov';
|
||||
|
||||
// Every CLI test runs on its own process and dumps the coverage afterwards.
|
||||
// Try to load it and merge it, so that we end up with the whole coverage at the end.
|
||||
if ($type === 'cli' && file_exists($covPath)) {
|
||||
$coverage->merge(require $covPath);
|
||||
}
|
||||
|
||||
if ($coverageType === 'pretty') {
|
||||
(new Html())->process($coverage, $basePath . '/coverage-html');
|
||||
} else {
|
||||
(new PHP())->process($coverage, $covPath);
|
||||
(new Xml(Version::getVersionString()))->process($coverage, $basePath . '/coverage-xml');
|
||||
}
|
||||
};
|
||||
$generateCoverage = $coverageType === 'yes';
|
||||
$coverage = $isE2eTest && $generateCoverage ? CoverageHelper::createCoverageForDirectories(
|
||||
[
|
||||
__DIR__ . '/../../module/Core/src',
|
||||
__DIR__ . '/../../module/' . ($isApiTest ? 'Rest' : 'CLI') . '/src',
|
||||
],
|
||||
__DIR__ . '/../../build/coverage-' . $testEnv,
|
||||
) : null;
|
||||
|
||||
$buildDbConnection = static function (): array {
|
||||
$driver = env('DB_DRIVER', 'sqlite');
|
||||
@@ -89,7 +52,7 @@ $buildDbConnection = static function (): array {
|
||||
'postgres' => [
|
||||
'driver' => 'pdo_pgsql',
|
||||
'host' => $isCi ? '127.0.0.1' : 'shlink_db_postgres',
|
||||
'port' => $isCi ? '5433' : '5432',
|
||||
'port' => $isCi ? '5434' : '5432',
|
||||
'user' => 'postgres',
|
||||
'password' => 'root',
|
||||
'dbname' => 'shlink_test',
|
||||
@@ -128,66 +91,28 @@ return [
|
||||
|
||||
'debug' => true,
|
||||
ConfigAggregator::ENABLE_CACHE => false,
|
||||
FastRouteRouter::CONFIG_CACHE_ENABLED => false,
|
||||
|
||||
'url_shortener' => [
|
||||
'domain' => [
|
||||
'schema' => 'http',
|
||||
'hostname' => 's.test',
|
||||
],
|
||||
],
|
||||
|
||||
'mezzio-swoole' => [
|
||||
'enable_coroutine' => false,
|
||||
'swoole-http-server' => [
|
||||
'host' => API_TESTS_HOST,
|
||||
'port' => API_TESTS_PORT,
|
||||
'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,
|
||||
],
|
||||
],
|
||||
],
|
||||
|
||||
'routes' => !$isApiTest ? [] : [
|
||||
'routes' => [
|
||||
// This route is used to test that title resolution is skipped if the long URL times out
|
||||
[
|
||||
'name' => 'dump_coverage',
|
||||
'path' => '/api-tests/stop-coverage',
|
||||
'middleware' => middleware(static function () use ($exportCoverage) {
|
||||
// TODO I have tried moving this block to a listener so that it's invoked automatically,
|
||||
// but then the coverage is generated empty ¯\_(ツ)_/¯
|
||||
$exportCoverage();
|
||||
return new EmptyResponse();
|
||||
}),
|
||||
'name' => 'long_url_with_timeout',
|
||||
'path' => '/api-tests/long-url-with-timeout',
|
||||
'allowed_methods' => ['GET'],
|
||||
'middleware' => middleware(static function () {
|
||||
sleep(5); // Title resolution times out at 3 seconds
|
||||
return new HtmlResponse('<title>The title</title>');
|
||||
}),
|
||||
],
|
||||
],
|
||||
|
||||
'middleware_pipeline' => !$isApiTest ? [] : [
|
||||
'capture_code_coverage' => [
|
||||
'middleware' => middleware(static function (
|
||||
ServerRequestInterface $req,
|
||||
RequestHandlerInterface $handler,
|
||||
) use (&$coverage): ResponseInterface {
|
||||
$coverage?->start($req->getHeaderLine('x-coverage-id'));
|
||||
|
||||
try {
|
||||
return $handler->handle($req);
|
||||
} finally {
|
||||
$coverage?->stop();
|
||||
}
|
||||
}),
|
||||
'middleware' => new CoverageMiddleware($coverage),
|
||||
'priority' => 9999,
|
||||
],
|
||||
],
|
||||
|
||||
'mercure' => [
|
||||
'public_hub_url' => null,
|
||||
'internal_hub_url' => null,
|
||||
'jwt_secret' => null,
|
||||
],
|
||||
|
||||
'dependencies' => [
|
||||
'services' => [
|
||||
'shlink_test_api_client' => new Client([
|
||||
@@ -200,58 +125,7 @@ return [
|
||||
],
|
||||
'delegators' => $isCliTest ? [
|
||||
Application::class => [
|
||||
static function (
|
||||
ContainerInterface $c,
|
||||
string $serviceName,
|
||||
callable $callback,
|
||||
) use (
|
||||
&$coverage,
|
||||
$exportCoverage,
|
||||
) {
|
||||
/** @var Application $app */
|
||||
$app = $callback();
|
||||
$wrappedEventDispatcher = new EventDispatcher();
|
||||
|
||||
// When the command starts, start collecting coverage
|
||||
$wrappedEventDispatcher->subscribeTo(
|
||||
ConsoleCommandEvent::class,
|
||||
static function () use (&$coverage): void {
|
||||
$id = env('COVERAGE_ID');
|
||||
if ($id === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
$coverage?->start($id);
|
||||
},
|
||||
);
|
||||
// When the command ends, stop collecting coverage
|
||||
$wrappedEventDispatcher->subscribeTo(
|
||||
ConsoleTerminateEvent::class,
|
||||
static function () use (&$coverage, $exportCoverage): void {
|
||||
$id = env('COVERAGE_ID');
|
||||
if ($id === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
$coverage?->stop();
|
||||
$exportCoverage('cli');
|
||||
},
|
||||
);
|
||||
|
||||
$app->setDispatcher(new class ($wrappedEventDispatcher) implements EventDispatcherInterface {
|
||||
public function __construct(private EventDispatcher $wrappedDispatcher)
|
||||
{
|
||||
}
|
||||
|
||||
public function dispatch(object $event, ?string $eventName = null): object
|
||||
{
|
||||
$this->wrappedDispatcher->dispatch($event);
|
||||
return $event;
|
||||
}
|
||||
});
|
||||
|
||||
return $app;
|
||||
},
|
||||
new CliCoverageDelegator($coverage),
|
||||
],
|
||||
] : [],
|
||||
],
|
||||
@@ -262,7 +136,7 @@ return [
|
||||
|
||||
'data_fixtures' => [
|
||||
'paths' => [
|
||||
// TODO These are used for CLI tests too, so maybe should be somewhere else
|
||||
// TODO These are used for other module's tests, so maybe should be somewhere else
|
||||
__DIR__ . '/../../module/Rest/test-api/Fixtures',
|
||||
],
|
||||
],
|
||||
|
||||
@@ -11,7 +11,7 @@ server {
|
||||
|
||||
location ~ \.php$ {
|
||||
fastcgi_split_path_info ^(.+\.php)(/.+)$;
|
||||
fastcgi_pass unix:/var/run/php/php8.2-fpm.sock;
|
||||
fastcgi_pass unix:/var/run/php/php8.3-fpm.sock;
|
||||
fastcgi_index index.php;
|
||||
include fastcgi.conf;
|
||||
}
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
/var/log/shlink/shlink_openswoole.log {
|
||||
su root root
|
||||
daily
|
||||
missingok
|
||||
rotate 120
|
||||
compress
|
||||
delaycompress
|
||||
notifempty
|
||||
create 0640 root root
|
||||
postrotate
|
||||
/etc/init.d/shlink_openswoole restart
|
||||
endscript
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
#!/bin/bash
|
||||
### BEGIN INIT INFO
|
||||
# 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 openswoole
|
||||
### END INIT INFO
|
||||
|
||||
SCRIPT=/path/to/shlink/vendor/bin/laminas\ mezzio:swoole:start
|
||||
RUNAS=root
|
||||
|
||||
PIDFILE=/var/run/shlink_openswoole.pid
|
||||
LOGDIR=/var/log/shlink
|
||||
LOGFILE=${LOGDIR}/shlink_openswoole.log
|
||||
|
||||
start() {
|
||||
if [[ -f "$PIDFILE" ]] && kill -0 $(cat "$PIDFILE"); then
|
||||
echo 'Shlink with openswoole already running' >&2
|
||||
return 1
|
||||
fi
|
||||
echo 'Starting shlink with openswoole' >&2
|
||||
mkdir -p "$LOGDIR"
|
||||
touch "$LOGFILE"
|
||||
local CMD="$SCRIPT &> \"$LOGFILE\" & echo \$!"
|
||||
su -c "$CMD" $RUNAS > "$PIDFILE"
|
||||
echo 'Shlink started' >&2
|
||||
}
|
||||
|
||||
stop() {
|
||||
if [[ ! -f "$PIDFILE" ]] || ! kill -0 $(cat "$PIDFILE"); then
|
||||
echo 'Shlink with openswoole not running' >&2
|
||||
return 1
|
||||
fi
|
||||
echo 'Stopping shlink with openswoole' >&2
|
||||
kill -15 $(cat "$PIDFILE") && rm -f "$PIDFILE"
|
||||
echo 'Shlink stopped' >&2
|
||||
}
|
||||
|
||||
case "$1" in
|
||||
start)
|
||||
start
|
||||
;;
|
||||
stop)
|
||||
stop
|
||||
;;
|
||||
restart)
|
||||
stop
|
||||
start
|
||||
;;
|
||||
*)
|
||||
echo "Usage: $0 {start|stop|restart}"
|
||||
esac
|
||||
@@ -1,8 +1,8 @@
|
||||
FROM php:8.2-fpm-alpine3.17
|
||||
FROM php:8.3-fpm-alpine3.19
|
||||
MAINTAINER Alejandro Celaya <alejandro@alejandrocelaya.com>
|
||||
|
||||
ENV APCU_VERSION 5.1.21
|
||||
ENV PDO_SQLSRV_VERSION 5.11.1
|
||||
ENV APCU_VERSION 5.1.23
|
||||
ENV PDO_SQLSRV_VERSION 5.12.0
|
||||
ENV MS_ODBC_DOWNLOAD 'b/9/f/b9f3cce4-3925-46d4-9f46-da08869c6486'
|
||||
ENV MS_ODBC_SQL_VERSION 18_18.1.1.1
|
||||
|
||||
@@ -46,12 +46,13 @@ RUN mkdir -p /usr/src/php/ext/apcu \
|
||||
&& rm /usr/local/etc/php/conf.d/docker-php-ext-apcu.ini \
|
||||
&& echo extension=apcu.so > /usr/local/etc/php/conf.d/20-php-ext-apcu.ini
|
||||
|
||||
# Install pcov and sqlsrv driver
|
||||
RUN wget https://download.microsoft.com/download/${MS_ODBC_DOWNLOAD}/msodbcsql${MS_ODBC_SQL_VERSION}-1_amd64.apk && \
|
||||
# Install xdebug and sqlsrv driver
|
||||
RUN apk add --update linux-headers && \
|
||||
wget https://download.microsoft.com/download/${MS_ODBC_DOWNLOAD}/msodbcsql${MS_ODBC_SQL_VERSION}-1_amd64.apk && \
|
||||
apk add --allow-untrusted msodbcsql${MS_ODBC_SQL_VERSION}-1_amd64.apk && \
|
||||
apk add --no-cache --virtual .phpize-deps $PHPIZE_DEPS unixodbc-dev && \
|
||||
pecl install pdo_sqlsrv-${PDO_SQLSRV_VERSION} pcov && \
|
||||
docker-php-ext-enable pdo_sqlsrv pcov && \
|
||||
pecl install pdo_sqlsrv-${PDO_SQLSRV_VERSION} xdebug && \
|
||||
docker-php-ext-enable pdo_sqlsrv xdebug && \
|
||||
apk del .phpize-deps && \
|
||||
rm msodbcsql${MS_ODBC_SQL_VERSION}-1_amd64.apk
|
||||
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
display_errors=On
|
||||
error_reporting=-1
|
||||
memory_limit=-1
|
||||
log_errors_max_len=0
|
||||
zend.assertions=1
|
||||
assert.exception=1
|
||||
pcov.enabled=1
|
||||
pcov.directory=module
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
FROM php:8.2-alpine3.17
|
||||
FROM php:8.3-alpine3.19
|
||||
MAINTAINER Alejandro Celaya <alejandro@alejandrocelaya.com>
|
||||
|
||||
ENV APCU_VERSION 5.1.21
|
||||
ENV PDO_SQLSRV_VERSION 5.11.1
|
||||
ENV APCU_VERSION 5.1.23
|
||||
ENV PDO_SQLSRV_VERSION 5.12.0
|
||||
ENV MS_ODBC_DOWNLOAD 'b/9/f/b9f3cce4-3925-46d4-9f46-da08869c6486'
|
||||
ENV MS_ODBC_SQL_VERSION 18_18.1.1.1
|
||||
|
||||
@@ -46,12 +46,13 @@ RUN mkdir -p /usr/src/php/ext/apcu \
|
||||
&& rm /usr/local/etc/php/conf.d/docker-php-ext-apcu.ini \
|
||||
&& echo extension=apcu.so > /usr/local/etc/php/conf.d/20-php-ext-apcu.ini
|
||||
|
||||
# Install pcov and sqlsrv driver
|
||||
RUN wget https://download.microsoft.com/download/${MS_ODBC_DOWNLOAD}/msodbcsql${MS_ODBC_SQL_VERSION}-1_amd64.apk && \
|
||||
# Install xdebug and sqlsrv driver
|
||||
RUN apk add --update linux-headers && \
|
||||
wget https://download.microsoft.com/download/${MS_ODBC_DOWNLOAD}/msodbcsql${MS_ODBC_SQL_VERSION}-1_amd64.apk && \
|
||||
apk add --allow-untrusted msodbcsql${MS_ODBC_SQL_VERSION}-1_amd64.apk && \
|
||||
apk add --no-cache --virtual .phpize-deps $PHPIZE_DEPS unixodbc-dev && \
|
||||
pecl install pdo_sqlsrv-${PDO_SQLSRV_VERSION} pcov && \
|
||||
docker-php-ext-enable pdo_sqlsrv pcov && \
|
||||
pecl install pdo_sqlsrv-${PDO_SQLSRV_VERSION} xdebug && \
|
||||
docker-php-ext-enable pdo_sqlsrv xdebug && \
|
||||
apk del .phpize-deps && \
|
||||
rm msodbcsql${MS_ODBC_SQL_VERSION}-1_amd64.apk
|
||||
|
||||
@@ -72,5 +73,7 @@ CMD \
|
||||
if [[ ! -d "./vendor" ]]; then /usr/local/bin/composer install ; fi && \
|
||||
# Download roadrunner binary
|
||||
if [[ ! -f "./bin/rr" ]]; then ./vendor/bin/rr get --no-interaction --no-config --location bin/ && chmod +x bin/rr ; fi && \
|
||||
# This forces the app to be started every second until the exit code is 0
|
||||
until ./bin/rr serve -c config/roadrunner/.rr.dev.yml; do sleep 1 ; done
|
||||
# Create env file if it does not exist yet
|
||||
if [[ ! -f "./config/params/shlink_dev_env.php" ]]; then cp ./config/params/shlink_dev_env.php.dist ./config/params/shlink_dev_env.php ; fi && \
|
||||
# Run with `exec` so that signals are properly handled
|
||||
exec ./bin/rr serve -c config/roadrunner/.rr.dev.yml
|
||||
|
||||
@@ -1,85 +0,0 @@
|
||||
FROM php:8.2-alpine3.17
|
||||
MAINTAINER Alejandro Celaya <alejandro@alejandrocelaya.com>
|
||||
|
||||
ENV APCU_VERSION 5.1.21
|
||||
ENV INOTIFY_VERSION 3.0.0
|
||||
ENV OPENSWOOLE_VERSION 22.1.0
|
||||
ENV PDO_SQLSRV_VERSION 5.11.1
|
||||
ENV MS_ODBC_DOWNLOAD 'b/9/f/b9f3cce4-3925-46d4-9f46-da08869c6486'
|
||||
ENV MS_ODBC_SQL_VERSION 18_18.1.1.1
|
||||
|
||||
RUN apk update
|
||||
|
||||
# Install common php extensions
|
||||
RUN docker-php-ext-install pdo_mysql
|
||||
RUN docker-php-ext-install calendar
|
||||
|
||||
RUN apk add --no-cache oniguruma-dev
|
||||
RUN docker-php-ext-install mbstring
|
||||
|
||||
RUN apk add --no-cache sqlite-libs
|
||||
RUN apk add --no-cache sqlite-dev
|
||||
RUN docker-php-ext-install pdo_sqlite
|
||||
|
||||
RUN apk add --no-cache icu-dev
|
||||
RUN docker-php-ext-install intl
|
||||
|
||||
RUN apk add --no-cache libzip-dev zlib-dev
|
||||
RUN docker-php-ext-install zip
|
||||
|
||||
RUN apk add --no-cache libpng-dev
|
||||
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 --virtual .phpize-deps $PHPIZE_DEPS linux-headers && \
|
||||
docker-php-ext-install sockets && \
|
||||
apk del .phpize-deps
|
||||
RUN docker-php-ext-install bcmath
|
||||
|
||||
# Install APCu extension
|
||||
ADD https://pecl.php.net/get/apcu-$APCU_VERSION.tgz /tmp/apcu.tar.gz
|
||||
RUN mkdir -p /usr/src/php/ext/apcu \
|
||||
&& tar xf /tmp/apcu.tar.gz -C /usr/src/php/ext/apcu --strip-components=1 \
|
||||
&& docker-php-ext-configure apcu \
|
||||
&& docker-php-ext-install apcu \
|
||||
&& rm /tmp/apcu.tar.gz \
|
||||
&& rm /usr/local/etc/php/conf.d/docker-php-ext-apcu.ini \
|
||||
&& echo extension=apcu.so > /usr/local/etc/php/conf.d/20-php-ext-apcu.ini
|
||||
|
||||
# Install inotify extension
|
||||
ADD https://pecl.php.net/get/inotify-$INOTIFY_VERSION.tgz /tmp/inotify.tar.gz
|
||||
RUN mkdir -p /usr/src/php/ext/inotify \
|
||||
&& tar xf /tmp/inotify.tar.gz -C /usr/src/php/ext/inotify --strip-components=1 \
|
||||
&& docker-php-ext-configure inotify \
|
||||
&& docker-php-ext-install inotify \
|
||||
&& rm /tmp/inotify.tar.gz
|
||||
|
||||
# Install openswoole, pcov and mssql driver
|
||||
RUN wget https://download.microsoft.com/download/${MS_ODBC_DOWNLOAD}/msodbcsql${MS_ODBC_SQL_VERSION}-1_amd64.apk && \
|
||||
apk add --allow-untrusted msodbcsql${MS_ODBC_SQL_VERSION}-1_amd64.apk && \
|
||||
apk add --no-cache --virtual .phpize-deps $PHPIZE_DEPS unixodbc-dev && \
|
||||
pecl install openswoole-${OPENSWOOLE_VERSION} pdo_sqlsrv-${PDO_SQLSRV_VERSION} pcov && \
|
||||
docker-php-ext-enable openswoole pdo_sqlsrv pcov && \
|
||||
apk del .phpize-deps && \
|
||||
rm msodbcsql${MS_ODBC_SQL_VERSION}-1_amd64.apk
|
||||
|
||||
# Install composer
|
||||
COPY --from=composer:2 /usr/bin/composer /usr/local/bin/composer
|
||||
|
||||
# Make home directory writable by anyone
|
||||
RUN chmod 777 /home
|
||||
|
||||
VOLUME /home/shlink
|
||||
WORKDIR /home/shlink
|
||||
|
||||
# Expose openswoole port
|
||||
EXPOSE 8080
|
||||
|
||||
CMD \
|
||||
# Install dependencies if the vendor dir does not exist
|
||||
if [[ ! -d "./vendor" ]]; then /usr/local/bin/composer install ; fi && \
|
||||
# When restarting the container, openswoole might think it is already in execution
|
||||
# This forces the app to be started every second until the exit code is 0
|
||||
until php ./vendor/bin/laminas mezzio:swoole:start; do sleep 1 ; done
|
||||
@@ -1,14 +0,0 @@
|
||||
server {
|
||||
listen 80 default_server;
|
||||
|
||||
error_log /home/shlink/www/data/infra/nginx/swoole_proxy.error.log;
|
||||
|
||||
location / {
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_pass http://shlink_swoole:8080;
|
||||
proxy_read_timeout 90s;
|
||||
}
|
||||
}
|
||||
@@ -1,14 +1,15 @@
|
||||
version: '3'
|
||||
|
||||
services:
|
||||
shlink_db_mysql:
|
||||
user: '0'
|
||||
environment:
|
||||
MYSQL_DATABASE: shlink_test
|
||||
|
||||
shlink_db_postgres:
|
||||
user: '0'
|
||||
environment:
|
||||
POSTGRES_DB: shlink_test
|
||||
|
||||
shlink_db_maria:
|
||||
user: '0'
|
||||
environment:
|
||||
MYSQL_DATABASE: shlink_test
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
version: '3'
|
||||
|
||||
services:
|
||||
shlink_php:
|
||||
user: 1000:1000
|
||||
volumes:
|
||||
- /etc/passwd:/etc/passwd:ro
|
||||
- /etc/group:/etc/group:ro
|
||||
|
||||
shlink_swoole:
|
||||
user: 1000:1000
|
||||
volumes:
|
||||
- /etc/passwd:/etc/passwd:ro
|
||||
- /etc/group:/etc/group:ro
|
||||
|
||||
shlink_roadrunner:
|
||||
user: 1000:1000
|
||||
volumes:
|
||||
- /etc/passwd:/etc/passwd:ro
|
||||
- /etc/group:/etc/group:ro
|
||||
|
||||
shlink_db_mysql:
|
||||
user: 1000:1000
|
||||
volumes:
|
||||
- /etc/passwd:/etc/passwd:ro
|
||||
- /etc/group:/etc/group:ro
|
||||
|
||||
shlink_db_postgres:
|
||||
user: 1000:1000
|
||||
volumes:
|
||||
- /etc/passwd:/etc/passwd:ro
|
||||
- /etc/group:/etc/group:ro
|
||||
|
||||
shlink_db_maria:
|
||||
user: 1000:1000
|
||||
volumes:
|
||||
- /etc/passwd:/etc/passwd:ro
|
||||
- /etc/group:/etc/group:ro
|
||||
@@ -1,5 +1,3 @@
|
||||
version: '3'
|
||||
|
||||
services:
|
||||
shlink_nginx:
|
||||
container_name: shlink_nginx
|
||||
@@ -15,6 +13,7 @@ services:
|
||||
|
||||
shlink_php:
|
||||
container_name: shlink_php
|
||||
user: 1000:1000
|
||||
build:
|
||||
context: .
|
||||
dockerfile: ./data/infra/php.Dockerfile
|
||||
@@ -36,49 +35,13 @@ services:
|
||||
- shlink_matomo
|
||||
environment:
|
||||
LC_ALL: C
|
||||
extra_hosts:
|
||||
- 'host.docker.internal:host-gateway'
|
||||
|
||||
shlink_swoole_proxy:
|
||||
container_name: shlink_swoole_proxy
|
||||
image: nginx:1.25-alpine
|
||||
ports:
|
||||
- "8002:80"
|
||||
volumes:
|
||||
- ./:/home/shlink/www
|
||||
- ./data/infra/swoole_proxy_vhost.conf:/etc/nginx/conf.d/default.conf
|
||||
links:
|
||||
- shlink_swoole
|
||||
|
||||
shlink_swoole:
|
||||
container_name: shlink_swoole
|
||||
build:
|
||||
context: .
|
||||
dockerfile: ./data/infra/swoole.Dockerfile
|
||||
ports:
|
||||
- "8080:8080"
|
||||
- "9001:9001"
|
||||
volumes:
|
||||
- ./:/home/shlink
|
||||
- ./data/infra/php.ini:/usr/local/etc/php/php.ini
|
||||
links:
|
||||
- shlink_db_mysql
|
||||
- shlink_db_postgres
|
||||
- shlink_db_maria
|
||||
- shlink_db_ms
|
||||
- shlink_redis
|
||||
- shlink_redis_acl
|
||||
- shlink_mercure
|
||||
- shlink_mercure_proxy
|
||||
- shlink_rabbitmq
|
||||
- shlink_matomo
|
||||
environment:
|
||||
LC_ALL: C
|
||||
DEFAULT_DOMAIN: localhost:8000
|
||||
extra_hosts:
|
||||
- 'host.docker.internal:host-gateway'
|
||||
|
||||
shlink_roadrunner:
|
||||
container_name: shlink_roadrunner
|
||||
user: 1000:1000
|
||||
build:
|
||||
context: .
|
||||
dockerfile: ./data/infra/roadrunner.Dockerfile
|
||||
@@ -105,6 +68,7 @@ services:
|
||||
|
||||
shlink_db_mysql:
|
||||
container_name: shlink_db_mysql
|
||||
user: 1000:1000
|
||||
image: mysql:8.0
|
||||
ports:
|
||||
- "3307:3306"
|
||||
@@ -117,9 +81,10 @@ services:
|
||||
|
||||
shlink_db_postgres:
|
||||
container_name: shlink_db_postgres
|
||||
image: postgres:12.2-alpine
|
||||
user: 1000:1000
|
||||
image: postgres:16.3-alpine
|
||||
ports:
|
||||
- "5433:5432"
|
||||
- "5434:5432"
|
||||
volumes:
|
||||
- ./:/home/shlink/www
|
||||
- ./data/infra/database_pg:/var/lib/postgresql/data
|
||||
@@ -130,6 +95,7 @@ services:
|
||||
|
||||
shlink_db_maria:
|
||||
container_name: shlink_db_maria
|
||||
user: 1000:1000
|
||||
image: mariadb:10.7
|
||||
ports:
|
||||
- "3308:3306"
|
||||
@@ -143,7 +109,7 @@ services:
|
||||
|
||||
shlink_db_ms:
|
||||
container_name: shlink_db_ms
|
||||
image: mcr.microsoft.com/mssql/server:2019-latest
|
||||
image: mcr.microsoft.com/mssql/server:2022-latest
|
||||
ports:
|
||||
- "1433:1433"
|
||||
environment:
|
||||
@@ -169,7 +135,7 @@ services:
|
||||
container_name: shlink_mercure_proxy
|
||||
image: nginx:1.25-alpine
|
||||
ports:
|
||||
- "8001:80"
|
||||
- "8002:80"
|
||||
volumes:
|
||||
- ./:/home/shlink/www
|
||||
- ./data/infra/mercure_proxy_vhost.conf:/etc/nginx/conf.d/default.conf
|
||||
@@ -185,21 +151,21 @@ services:
|
||||
SERVER_NAME: ":80"
|
||||
MERCURE_PUBLISHER_JWT_KEY: mercure_jwt_key_long_enough_to_avoid_error
|
||||
MERCURE_SUBSCRIBER_JWT_KEY: mercure_jwt_key_long_enough_to_avoid_error
|
||||
MERCURE_EXTRA_DIRECTIVES: "cors_origins https://app.shlink.io http://localhost:3000 http://127.0.0.1:3000"
|
||||
MERCURE_EXTRA_DIRECTIVES: "cors_origins https://app.shlink.io http://localhost:3000 http://127.0.0.1:3000 http://localhost:3002 http://127.0.0.1:3002 http://localhost:3005 http://127.0.0.1:3005"
|
||||
|
||||
shlink_rabbitmq:
|
||||
container_name: shlink_rabbitmq
|
||||
image: rabbitmq:3.11-management-alpine
|
||||
ports:
|
||||
- "15672:15672"
|
||||
- "5672:5672"
|
||||
- "15673:15672"
|
||||
- "5673:5672"
|
||||
environment:
|
||||
RABBITMQ_DEFAULT_USER: "rabbit"
|
||||
RABBITMQ_DEFAULT_PASS: "rabbit"
|
||||
|
||||
shlink_swagger_ui:
|
||||
container_name: shlink_swagger_ui
|
||||
image: swaggerapi/swagger-ui:v5.10.3
|
||||
image: swaggerapi/swagger-ui:v5.11.3
|
||||
ports:
|
||||
- "8005:8080"
|
||||
volumes:
|
||||
@@ -207,7 +173,7 @@ services:
|
||||
|
||||
shlink_matomo:
|
||||
container_name: shlink_matomo
|
||||
image: matomo:4.15-apache
|
||||
image: matomo:5.0-apache
|
||||
ports:
|
||||
- "8003:80"
|
||||
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 [RoadRunner](https://roadrunner.dev) or [openswoole](https://openswoole.com/), which can be linked to external databases to persist data.
|
||||
It exposes a shlink instance served with [RoadRunner](https://roadrunner.dev), which can be linked to external databases to persist data.
|
||||
|
||||
## Usage
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
log_errors_max_len=0
|
||||
zend.assertions=1
|
||||
assert.exception=1
|
||||
memory_limit=512M
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Shlinkio\Shlink;
|
||||
|
||||
use Shlinkio\Shlink\Common\Logger\LoggerType;
|
||||
|
||||
return [
|
||||
|
||||
'logger' => [
|
||||
'Shlink' => [
|
||||
'type' => LoggerType::STREAM->value,
|
||||
'destination' => 'php://stderr',
|
||||
],
|
||||
],
|
||||
|
||||
];
|
||||
@@ -8,31 +8,24 @@ mkdir -p data/cache data/locks data/log data/proxies
|
||||
|
||||
flags="--no-interaction --clear-db-cache"
|
||||
|
||||
# Read env vars through Shlink command, so that it applies the `_FILE` env var fallback logic
|
||||
geolite_license_key=$(bin/cli env-var:read GEOLITE_LICENSE_KEY)
|
||||
skip_initial_geolite_download=$(bin/cli env-var:read SKIP_INITIAL_GEOLITE_DOWNLOAD)
|
||||
initial_api_key=$(bin/cli env-var:read INITIAL_API_KEY)
|
||||
|
||||
# Skip downloading GeoLite2 db file if the license key env var was not defined or skipping was explicitly set
|
||||
if [ -z "${GEOLITE_LICENSE_KEY}" ] || [ "${SKIP_INITIAL_GEOLITE_DOWNLOAD}" = "true" ]; then
|
||||
if [ -z "${geolite_license_key}" ] || [ "${skip_initial_geolite_download}" = "true" ]; then
|
||||
flags="${flags} --skip-download-geolite"
|
||||
fi
|
||||
|
||||
# If INITIAL_API_KEY was provided, create an initial API key
|
||||
if [ -n "${INITIAL_API_KEY}" ]; then
|
||||
flags="${flags} --initial-api-key=${INITIAL_API_KEY}"
|
||||
if [ -n "${initial_api_key}" ]; then
|
||||
flags="${flags} --initial-api-key=${initial_api_key}"
|
||||
fi
|
||||
|
||||
php vendor/bin/shlink-installer init ${flags}
|
||||
|
||||
# Periodically run visit:locate every hour, if ENABLE_PERIODIC_VISIT_LOCATE=true was provided and running as root
|
||||
# FIXME: ENABLE_PERIODIC_VISIT_LOCATE is deprecated. Remove cron support in Shlink 4.0.0
|
||||
if [ "${ENABLE_PERIODIC_VISIT_LOCATE}" = "true" ] && [ "${SHLINK_USER_ID}" = "root" ]; then
|
||||
echo "Configuring periodic visit location..."
|
||||
echo "0 * * * * php /etc/shlink/bin/cli visit:locate -q" > /etc/crontabs/root
|
||||
/usr/sbin/crond &
|
||||
fi
|
||||
|
||||
if [ "$SHLINK_RUNTIME" = 'openswoole' ]; then
|
||||
# Openswoole is deprecated. Remove in Shlink 4.0.0
|
||||
# When restarting the container, openswoole might think it is already in execution
|
||||
# This forces the app to be started every second until the exit code is 0
|
||||
until php vendor/bin/laminas mezzio:swoole:start; do sleep 1 ; done
|
||||
elif [ "$SHLINK_RUNTIME" = 'rr' ]; then
|
||||
./bin/rr serve -c config/roadrunner/.rr.yml
|
||||
if [ "$SHLINK_RUNTIME" = 'rr' ]; then
|
||||
# Run with `exec` so that signals are properly handled
|
||||
exec ./bin/rr serve -c config/roadrunner/.rr.yml
|
||||
fi
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
# Handle dev and tests config via env vars instead of local config files
|
||||
|
||||
* Status: Accepted
|
||||
* Date: 2024-10-24
|
||||
|
||||
## Context and problem statement
|
||||
|
||||
Due to the tools used by Shlink (Zend Expressive first and Mezzio later), configuration has always been handled via the config aggregator, which is a package that continues with Zend Framework 2 config management philosophy:
|
||||
|
||||
1. Define multiple config files, scoped to their own context, that are merge at runtime.
|
||||
2. Overwrite with so-called "local" config files, which define values used only during development, and should not be shipped to production.
|
||||
|
||||
However, since Shlink started to support other runtimes and added an official docker image, env vars have started to become a central part of the config definition system.
|
||||
|
||||
That has evolved into a system where production config can be read from env vars, but dev config is expected to be defined via local config files, forcing to maintain two approaches to load config that need to coexist.
|
||||
|
||||
On top of that, keeping dev configs in multiple files makes it harder to keep track of everything.
|
||||
|
||||
Because of that, I'm proposing to switch to an env-var-based approach for dev custom configs, and get rid of local config files.
|
||||
|
||||
## Considered options
|
||||
|
||||
1. Define dev env vars in a single `.env` file which is loaded to containers via docker compose `env-file` option.
|
||||
2. Define dev env vars in a single `.env` file which is loaded via RoadRunner config.
|
||||
3. Define dev env vars in a single PHP file returning a map that's then loaded with `loadEnvVarsFromConfig`.
|
||||
4. Keep local config files and don't change anything.
|
||||
|
||||
## Decision outcome
|
||||
|
||||
Defining env vars in a PHP file has the benefit that any change will take effect immediately, so the decision is to go with option 3.
|
||||
|
||||
## Pros and Cons of the Options
|
||||
|
||||
### 1 - .env file via docker compose
|
||||
|
||||
* Good: because it does not require any special mechanism to feed the env vars into the app.
|
||||
* Good: because it's a standard format known by many.
|
||||
* Bad: because dev config gets leaked to tests when run inside the container, breaking some existing ones, and forcing to remember this for future tests.
|
||||
* Bad: because any change to the env file requires the containers to be manually restarted, or putting some new mechanism in place to restart them automatically.
|
||||
|
||||
### 2 - .env file via RoadRunner
|
||||
|
||||
* Good: because it does not require any special mechanism to feed the env vars into the app.
|
||||
* Good: because it's a standard format known by many.
|
||||
* Good: because dev config does not get leaked into tests.
|
||||
* Bad: because any change to the env file requires the containers to be manually restarted, or putting some new mechanism in place to restart them automatically.
|
||||
|
||||
### 3 - PHP file via `loadEnvVarsFromConfig`
|
||||
|
||||
* Good: because the existing call to `loadEnvVarsFromConfig` can be reused by tweaking a bit the glob pattern, so no new dependencies are needed.
|
||||
* Good: because dev config does not get leaked into tests, and test-specific env vars can be fed using the same mechanism.
|
||||
* Good: because changes are picked up instantly by both RoadRunner and php-fpm.
|
||||
* Good: because env vars can be imported from `EnvVars` class, removing the chances of human mistakes and typos.
|
||||
* Bad: because people not familiar with the project may not expect env vars to be defined in that format.
|
||||
|
||||
### 4 - keep local config
|
||||
|
||||
* Good: because no changes are needed in the project.
|
||||
* Bad: because managing multiple local config files makes things harder to maintain.
|
||||
* Bad: because setting-up the project from scratch requires more steps, or an external package to handle config files.
|
||||
* Bad: because the project needs to keep two ways to load dev configs, and reading an env var does not warranty you are getting the single source of truth.
|
||||
@@ -2,7 +2,8 @@
|
||||
|
||||
Here listed you will find the different architectural decisions taken in the project, including all the reasoning behind it, options considered, and final outcome.
|
||||
|
||||
* [2023-07-09Build `latest` docker image only for actual releases](2023-07-09-build-latest-docker-image-only-for-actual-releases.md)
|
||||
* [2024-10-24 Handle dev and tests config via env vars instead of local config files](2024-10-24-handle-dev-and-tests-config-via-env-vars-instead-of-local-config-files.md)
|
||||
* [2023-07-09 Build `latest` docker image only for actual releases](2023-07-09-build-latest-docker-image-only-for-actual-releases.md)
|
||||
* [2023-01-06 Support any HTTP method in short URLs](2023-01-06-support-any-http-method-in-short-urls.md)
|
||||
* [2022-08-05 Support multi-segment custom slugs](2022-08-05-support-multi-segment-custom-slugs.md)
|
||||
* [2022-01-15 Update env vars behavior to have precedence over installer options](2022-01-15-update-env-vars-behavior-to-have-precedence-over-installer-options.md)
|
||||
|
||||
@@ -111,9 +111,6 @@
|
||||
"type": "string",
|
||||
"description": "The original long URL."
|
||||
},
|
||||
"deviceLongUrls": {
|
||||
"$ref": "#/components/schemas/DeviceLongUrls"
|
||||
},
|
||||
"dateCreated": {
|
||||
"type": "string",
|
||||
"format": "date-time",
|
||||
@@ -122,11 +119,6 @@
|
||||
"visitsSummary": {
|
||||
"$ref": "#/components/schemas/VisitsSummary"
|
||||
},
|
||||
"visitsCount": {
|
||||
"deprecated": true,
|
||||
"type": "integer",
|
||||
"description": "The number of visits that this short URL has received."
|
||||
},
|
||||
"tags": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
@@ -155,11 +147,6 @@
|
||||
"shortCode": "12C18",
|
||||
"shortUrl": "https://s.test/12C18",
|
||||
"longUrl": "https://store.steampowered.com",
|
||||
"deviceLongUrls": {
|
||||
"android": "https://store.steampowered.com/android",
|
||||
"ios": "https://store.steampowered.com/ios",
|
||||
"desktop": null
|
||||
},
|
||||
"dateCreated": "2016-08-21T20:34:16+02:00",
|
||||
"visitsSummary": {
|
||||
"total": 328,
|
||||
@@ -223,24 +210,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"DeviceLongUrls": {
|
||||
"type": "object",
|
||||
"required": ["android", "ios", "desktop"],
|
||||
"properties": {
|
||||
"android": {
|
||||
"description": "The long URL to redirect to when the short URL is visited from a device running Android",
|
||||
"type": "string"
|
||||
},
|
||||
"ios": {
|
||||
"description": "The long URL to redirect to when the short URL is visited from a device running iOS",
|
||||
"type": "string"
|
||||
},
|
||||
"desktop": {
|
||||
"description": "The long URL to redirect to when the short URL is visited from a desktop browser",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Visit": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -263,6 +232,11 @@
|
||||
"potentialBot": {
|
||||
"type": "boolean",
|
||||
"description": "Tells if Shlink thinks this visit comes potentially from a bot or crawler"
|
||||
},
|
||||
"visitedUrl": {
|
||||
"type": "string",
|
||||
"nullable": true,
|
||||
"description": "The originally visited URL that triggered the tracking of this visit"
|
||||
}
|
||||
},
|
||||
"example": {
|
||||
@@ -278,7 +252,8 @@
|
||||
"regionName": "California",
|
||||
"timezone": "America/Los_Angeles"
|
||||
},
|
||||
"potentialBot": false
|
||||
"potentialBot": false,
|
||||
"visitedUrl": "https://s.test"
|
||||
}
|
||||
},
|
||||
"OrphanVisit": {
|
||||
@@ -287,11 +262,6 @@
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"visitedUrl": {
|
||||
"type": "string",
|
||||
"nullable": true,
|
||||
"description": "The originally visited URL that triggered the tracking of this visit"
|
||||
},
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
|
||||
1167
docs/changelog-archive/CHANGELOG-1.x.md
Normal file
1167
docs/changelog-archive/CHANGELOG-1.x.md
Normal file
File diff suppressed because it is too large
Load Diff
912
docs/changelog-archive/CHANGELOG-2.x.md
Normal file
912
docs/changelog-archive/CHANGELOG-2.x.md
Normal file
@@ -0,0 +1,912 @@
|
||||
# CHANGELOG 2.x
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com), and this project adheres to [Semantic Versioning](https://semver.org).
|
||||
|
||||
## [2.10.3] - 2022-01-23
|
||||
### Added
|
||||
* *Nothing*
|
||||
|
||||
### Changed
|
||||
* *Nothing*
|
||||
|
||||
### Deprecated
|
||||
* *Nothing*
|
||||
|
||||
### Removed
|
||||
* *Nothing*
|
||||
|
||||
### Fixed
|
||||
* [#1349](https://github.com/shlinkio/shlink/issues/1349) Fixed memory leak in cache implementation.
|
||||
|
||||
|
||||
## [2.10.2] - 2022-01-07
|
||||
### Added
|
||||
* *Nothing*
|
||||
|
||||
### Changed
|
||||
* *Nothing*
|
||||
|
||||
### Deprecated
|
||||
* *Nothing*
|
||||
|
||||
### Removed
|
||||
* *Nothing*
|
||||
|
||||
### Fixed
|
||||
* [#1293](https://github.com/shlinkio/shlink/issues/1293) Fixed error when trying to create/import short URLs with a too long title.
|
||||
* [#1306](https://github.com/shlinkio/shlink/issues/1306) Ensured remote IP address is not logged when using swoole/openswoole.
|
||||
* [#1308](https://github.com/shlinkio/shlink/issues/1308) Fixed memory leak when using redis due to the amount of non-expiring keys created by doctrine. Now they have a 24h expiration by default.
|
||||
|
||||
|
||||
## [2.10.1] - 2021-12-21
|
||||
### Added
|
||||
* *Nothing*
|
||||
|
||||
### Changed
|
||||
* *Nothing*
|
||||
|
||||
### Deprecated
|
||||
* *Nothing*
|
||||
|
||||
### Removed
|
||||
* *Nothing*
|
||||
|
||||
### Fixed
|
||||
* [#1285](https://github.com/shlinkio/shlink/issues/1285) Fixed error caused by database connections expiring after some hours of inactivity.
|
||||
* [#1286](https://github.com/shlinkio/shlink/issues/1286) Fixed `x-request-id` header not being generated during non-rest requests.
|
||||
|
||||
|
||||
## [2.10.0] - 2021-12-12
|
||||
### Added
|
||||
* [#1163](https://github.com/shlinkio/shlink/issues/1163) Allowed setting not-found redirects for default domain in the same way it's done for any other domain.
|
||||
|
||||
This implies a few non-breaking changes:
|
||||
|
||||
* The domains list no longer has the values of `INVALID_SHORT_URL_REDIRECT_TO`, `REGULAR_404_REDIRECT_TO` and `BASE_URL_REDIRECT_TO` on the default domain redirects.
|
||||
* The `GET /domains` endpoint includes a new `defaultRedirects` property in the response, with the default redirects set via config or env vars.
|
||||
* The `INVALID_SHORT_URL_REDIRECT_TO`, `REGULAR_404_REDIRECT_TO` and `BASE_URL_REDIRECT_TO` env vars are now deprecated, and should be replaced by `DEFAULT_INVALID_SHORT_URL_REDIRECT`, `DEFAULT_REGULAR_404_REDIRECT` and `DEFAULT_BASE_URL_REDIRECT` respectively. Deprecated ones will continue to work until v3.0.0, where they will be removed.
|
||||
|
||||
* [#868](https://github.com/shlinkio/shlink/issues/868) Added support to publish real-time updates in a RabbitMQ server.
|
||||
|
||||
Shlink will create new exchanges and queues for every topic documented in the [Async API spec](https://api-spec.shlink.io/async-api/), meaning, you will have one queue for orphan visits, one for regular visits, and one queue for every short URL with its visits.
|
||||
|
||||
The RabbitMQ server config can be provided via installer config options, or via environment variables.
|
||||
|
||||
* [#1204](https://github.com/shlinkio/shlink/issues/1204) Added support for `openswoole` and migrated official docker image to `openswoole`.
|
||||
* [#1242](https://github.com/shlinkio/shlink/issues/1242) Added support to import urls and visits from YOURLS.
|
||||
|
||||
In order to do it, you need to first install this [dedicated plugin](https://slnk.to/yourls-import) in YOURLS, and then run the `short-url:import yourls` command, as with any other source.
|
||||
|
||||
* [#1235](https://github.com/shlinkio/shlink/issues/1235) Added support to disable rounding QR codes block sizing via config option, env var or query param.
|
||||
* [#1188](https://github.com/shlinkio/shlink/issues/1188) Added support for PHP 8.1.
|
||||
|
||||
The official docker image has also been updated to use PHP 8.1 by default.
|
||||
|
||||
### Changed
|
||||
* [#844](https://github.com/shlinkio/shlink/issues/844) Added mutation checks to API tests.
|
||||
* [#1218](https://github.com/shlinkio/shlink/issues/1218) Updated to symfony/mercure 0.6.
|
||||
* [#1223](https://github.com/shlinkio/shlink/issues/1223) Updated to phpstan 1.0.
|
||||
* [#1258](https://github.com/shlinkio/shlink/issues/1258) Updated to Symfony 6 components, except symfony/console.
|
||||
* Added `domain` field to `DeleteShortUrlException` exception.
|
||||
|
||||
### Deprecated
|
||||
* [#1260](https://github.com/shlinkio/shlink/issues/1260) Deprecated `USE_HTTPS` env var that was added in previous release, in favor of the new `IS_HTTPS_ENABLED`.
|
||||
|
||||
The old one proved to be confusing and misleading, making people think it was used to actually enable HTTPS transparently, instead of its actual purpose, which is just telling Shlink it is being served with HTTPS.
|
||||
|
||||
### Removed
|
||||
* *Nothing*
|
||||
|
||||
### Fixed
|
||||
* [#1206](https://github.com/shlinkio/shlink/issues/1206) Fixed debugging of the docker image, so that it does not run the commands with `-q` when the `SHELL_VERBOSITY` env var has been provided.
|
||||
* [#1254](https://github.com/shlinkio/shlink/issues/1254) Fixed examples in swagger docs.
|
||||
|
||||
|
||||
## [2.9.3] - 2021-11-15
|
||||
### Added
|
||||
* *Nothing*
|
||||
|
||||
### Changed
|
||||
* *Nothing*
|
||||
|
||||
### Deprecated
|
||||
* *Nothing*
|
||||
|
||||
### Removed
|
||||
* *Nothing*
|
||||
|
||||
### Fixed
|
||||
* [#1232](https://github.com/shlinkio/shlink/issues/1232) Solved potential SQL injection by enforcing `doctrine/dbal` 3.1.4.
|
||||
|
||||
|
||||
## [2.9.2] - 2021-10-23
|
||||
### Added
|
||||
* *Nothing*
|
||||
|
||||
### Changed
|
||||
* *Nothing*
|
||||
|
||||
### Deprecated
|
||||
* *Nothing*
|
||||
|
||||
### Removed
|
||||
* *Nothing*
|
||||
|
||||
### Fixed
|
||||
* [#1210](https://github.com/shlinkio/shlink/issues/1210) Fixed real time updates not being notified due to an incorrect handling of db transactions on multi-process tasks.
|
||||
* [#1211](https://github.com/shlinkio/shlink/issues/1211) Fixed `There is no active transaction` error when running migrations in MySQL/Mariadb after updating to doctrine-migrations 3.3.
|
||||
* [#1197](https://github.com/shlinkio/shlink/issues/1197) Fixed amount of task workers provided via config option or env var not being validated to ensure enough workers to process all parallel tasks.
|
||||
|
||||
|
||||
## [2.9.1] - 2021-10-11
|
||||
### Added
|
||||
* *Nothing*
|
||||
|
||||
### Changed
|
||||
* *Nothing*
|
||||
|
||||
### Deprecated
|
||||
* *Nothing*
|
||||
|
||||
### Removed
|
||||
* *Nothing*
|
||||
|
||||
### Fixed
|
||||
* [#1201](https://github.com/shlinkio/shlink/issues/1201) Fixed crash when using the new `USE_HTTPS`, as it's boolean raw value was being used instead of resolving "https" or "http".
|
||||
|
||||
|
||||
## [2.9.0] - 2021-10-10
|
||||
### Added
|
||||
* [#1015](https://github.com/shlinkio/shlink/issues/1015) Shlink now accepts configuration via env vars even when not using docker.
|
||||
|
||||
The config generated with the installing tool still has precedence over the env vars, so it cannot be combined. Either you use the tool, or use env vars.
|
||||
|
||||
* [#1149](https://github.com/shlinkio/shlink/issues/1149) Allowed to set custom defaults for the QR codes.
|
||||
* [#1112](https://github.com/shlinkio/shlink/issues/1112) Added new option to define if the query string should be forwarded on a per-short URL basis.
|
||||
|
||||
The new `forwardQuery=true|false` param can be provided during short URL creation or edition, via REST API or CLI command, allowing to override the default behavior which makes the query string to always be forwarded.
|
||||
|
||||
* [#1105](https://github.com/shlinkio/shlink/issues/1105) Added support to define placeholders on not-found redirects, so that the redirected URL receives the originally visited path and/or domain.
|
||||
|
||||
Currently, `{DOMAIN}` and `{ORIGINAL_PATH}` placeholders are supported, and they can be used both in the redirected URL's path or query.
|
||||
|
||||
When they are used in the query, the values are URL encoded.
|
||||
|
||||
* [#1119](https://github.com/shlinkio/shlink/issues/1119) Added support to provide redis sentinel when using redis cache.
|
||||
* [#1016](https://github.com/shlinkio/shlink/issues/1016) Added new option to send orphan visits to webhooks, via `NOTIFY_ORPHAN_VISITS_TO_WEBHOOKS` env var or installer tool.
|
||||
|
||||
The option is disabled by default, as the payload is backwards incompatible. You will need to adapt your webhooks to treat the `shortUrl` property as optional before enabling this option.
|
||||
|
||||
* [#1104](https://github.com/shlinkio/shlink/issues/1104) Added ability to disable tracking based on IP addresses.
|
||||
|
||||
IP addresses can be provided in the form of fixed addresses, CIDR blocks, or wildcard patterns (192.168.*.*).
|
||||
|
||||
### Changed
|
||||
* [#1142](https://github.com/shlinkio/shlink/issues/1142) Replaced `doctrine/cache` package with `symfony/cache`.
|
||||
* [#1157](https://github.com/shlinkio/shlink/issues/1157) All routes now support CORS, not only rest ones.
|
||||
* [#1144](https://github.com/shlinkio/shlink/issues/1144) Added experimental builds under PHP 8.1.
|
||||
|
||||
### Deprecated
|
||||
* [#1164](https://github.com/shlinkio/shlink/issues/1164) Deprecated `SHORT_DOMAIN_HOST` and `SHORT_DOMAIN_SCHEMA` env vars. Use `DEFAULT_DOMAIN` and `USE_HTTPS=true|false` instead.
|
||||
|
||||
### Removed
|
||||
* *Nothing*
|
||||
|
||||
### Fixed
|
||||
* [#1165](https://github.com/shlinkio/shlink/issues/1165) Fixed warning displayed when trying to locate visits and there are none pending.
|
||||
* [#1172](https://github.com/shlinkio/shlink/pull/1172) Removed unneeded explicitly defined volumes in docker image.
|
||||
|
||||
|
||||
## [2.8.1] - 2021-08-15
|
||||
### Added
|
||||
* *Nothing*
|
||||
|
||||
### Changed
|
||||
* *Nothing*
|
||||
|
||||
### Deprecated
|
||||
* *Nothing*
|
||||
|
||||
### Removed
|
||||
* *Nothing*
|
||||
|
||||
### Fixed
|
||||
* [#1155](https://github.com/shlinkio/shlink/issues/1155) Fixed numeric query params in long URLs being replaced by `0`.
|
||||
|
||||
|
||||
## [2.8.0] - 2021-08-04
|
||||
### Added
|
||||
* [#1089](https://github.com/shlinkio/shlink/issues/1089) Added new `ENABLE_PERIODIC_VISIT_LOCATE` env var to docker image which schedules the `visit:locate` command every hour when provided with value `true`.
|
||||
* [#1082](https://github.com/shlinkio/shlink/issues/1082) Added support for error correction level on QR codes.
|
||||
|
||||
Now, when calling the `GET /{shorCode}/qr-code` URL, you can pass the `errorCorrection` query param with values `L` for Low, `M` for Medium, `Q` for Quartile or `H` for High.
|
||||
|
||||
* [#1080](https://github.com/shlinkio/shlink/issues/1080) Added support to redirect to URLs as soon as the path starts with a valid short code, appending the rest of the path to the redirected long URL.
|
||||
|
||||
With this, if you have the `https://example.com/abc123` short URL redirecting to `https://www.twitter.com`, a visit to `https://example.com/abc123/shlinkio` will take you to `https://www.twitter.com/shlinkio`.
|
||||
|
||||
This behavior needs to be actively opted in, via installer config options or env vars.
|
||||
|
||||
* [#943](https://github.com/shlinkio/shlink/issues/943) Added support to define different "not-found" redirects for every domain handled by Shlink.
|
||||
|
||||
Shlink will continue to allow defining the default values via env vars or config, but afterwards, you can use the `domain:redirects` command or the `PATCH /domains/redirects` REST endpoint to define specific values for every single domain.
|
||||
|
||||
### Changed
|
||||
* [#1118](https://github.com/shlinkio/shlink/issues/1118) Increased phpstan required level to 8.
|
||||
* [#1127](https://github.com/shlinkio/shlink/issues/1127) Updated to infection 0.24.
|
||||
* [#1139](https://github.com/shlinkio/shlink/issues/1139) Updated project dependencies, including base docker image to use PHP 8.0.9 and Alpine 3.14.
|
||||
|
||||
### Deprecated
|
||||
* *Nothing*
|
||||
|
||||
### Removed
|
||||
* [#1046](https://github.com/shlinkio/shlink/issues/1046) Dropped support for PHP 7.4.
|
||||
|
||||
### Fixed
|
||||
* [#1098](https://github.com/shlinkio/shlink/issues/1098) Fixed errors when using Redis for caching, caused by some third party lib bug that was fixed on dependencies update.
|
||||
|
||||
|
||||
## [2.7.3] - 2021-08-02
|
||||
### Added
|
||||
* *Nothing*
|
||||
|
||||
### Changed
|
||||
* *Nothing*
|
||||
|
||||
### Deprecated
|
||||
* *Nothing*
|
||||
|
||||
### Removed
|
||||
* *Nothing*
|
||||
|
||||
### Fixed
|
||||
* [#1135](https://github.com/shlinkio/shlink/issues/1135) Fixed error when importing short URLs with no visits from another Shlink instance.
|
||||
* [#1136](https://github.com/shlinkio/shlink/issues/1136) Fixed error when fetching tag/short-url/orphan visits for a page lower than 1.
|
||||
|
||||
|
||||
## [2.7.2] - 2021-07-30
|
||||
### Added
|
||||
* *Nothing*
|
||||
|
||||
### Changed
|
||||
* *Nothing*
|
||||
|
||||
### Deprecated
|
||||
* *Nothing*
|
||||
|
||||
### Removed
|
||||
* *Nothing*
|
||||
|
||||
### Fixed
|
||||
* [#1128](https://github.com/shlinkio/shlink/issues/1128) Increased memory limit reserved for the docker image, preventing it from crashing on GeoLite db download.
|
||||
|
||||
|
||||
## [2.7.1] - 2021-05-30
|
||||
### Added
|
||||
* *Nothing*
|
||||
|
||||
### Changed
|
||||
* *Nothing*
|
||||
|
||||
### Deprecated
|
||||
* *Nothing*
|
||||
|
||||
### Removed
|
||||
* *Nothing*
|
||||
|
||||
### Fixed
|
||||
* [#1100](https://github.com/shlinkio/shlink/issues/1100) Fixed Shlink trying to download GeoLite2 db files even when tracking has been disabled.
|
||||
|
||||
|
||||
## [2.7.0] - 2021-05-23
|
||||
### Added
|
||||
* [#1044](https://github.com/shlinkio/shlink/issues/1044) Added ability to set names on API keys, which helps to identify them when the list grows.
|
||||
* [#819](https://github.com/shlinkio/shlink/issues/819) Visits are now always located in real time, even when not using swoole.
|
||||
|
||||
The only side effect is that a GeoLite2 db file is now installed when the docker image starts or during shlink installation or update.
|
||||
|
||||
Also, when using swoole, the file is now updated **after** tracking a visit, which means it will not apply until the next one.
|
||||
|
||||
* [#1059](https://github.com/shlinkio/shlink/issues/1059) Added ability to optionally display author API key and its name when listing short URLs from the command line.
|
||||
* [#1066](https://github.com/shlinkio/shlink/issues/1066) Added support to import short URLs and their visits from another Shlink instance using its API.
|
||||
* [#898](https://github.com/shlinkio/shlink/issues/898) Improved tracking granularity, allowing to disable visits tracking completely, or just parts of it.
|
||||
|
||||
In order to achieve it, Shlink now supports 4 new tracking-related options, that can be customized via env vars for docker, or via installer:
|
||||
|
||||
* `disable_tracking`: If true, visits will not be tracked at all.
|
||||
* `disable_ip_tracking`: If true, visits will be tracked, but neither the IP address, nor the location will be resolved.
|
||||
* `disable_referrer_tracking`: If true, the referrer will not be tracked.
|
||||
* `disable_ua_tracking`: If true, the user agent will not be tracked.
|
||||
|
||||
* [#955](https://github.com/shlinkio/shlink/issues/955) Added new option to set short URLs as crawlable, making them be listed in the robots.txt as Allowed.
|
||||
* [#900](https://github.com/shlinkio/shlink/issues/900) Shlink now tries to detect if the visit is coming from a potential bot or crawler, and allows to exclude those visits from visits lists if desired.
|
||||
|
||||
### Changed
|
||||
* [#1036](https://github.com/shlinkio/shlink/issues/1036) Updated to `happyr/doctrine-specification` 2.0.
|
||||
* [#1039](https://github.com/shlinkio/shlink/issues/1039) Updated to `endroid/qr-code` 4.0.
|
||||
* [#1008](https://github.com/shlinkio/shlink/issues/1008) Ensured all logs are sent to the filesystem while running API tests, which helps debugging the reason for tests to fail.
|
||||
|
||||
### Deprecated
|
||||
* *Nothing*
|
||||
|
||||
### Removed
|
||||
* *Nothing*
|
||||
|
||||
### Fixed
|
||||
* [#1041](https://github.com/shlinkio/shlink/issues/1041) Ensured the default value for the version while building the docker image is `latest`.
|
||||
* [#1067](https://github.com/shlinkio/shlink/issues/1067) Fixed exception when persisting multiple short URLs in one batch which include the same new tags/domains. This can potentially happen when importing URLs.
|
||||
|
||||
|
||||
## [2.6.2] - 2021-03-12
|
||||
### Added
|
||||
* *Nothing*
|
||||
|
||||
### Changed
|
||||
* *Nothing*
|
||||
|
||||
### Deprecated
|
||||
* *Nothing*
|
||||
|
||||
### Removed
|
||||
* *Nothing*
|
||||
|
||||
### Fixed
|
||||
* [#1047](https://github.com/shlinkio/shlink/issues/1047) Fixed error in migrations when doing a fresh installation using PHP8 and MySQL/Mariadb databases.
|
||||
|
||||
|
||||
## [2.6.1] - 2021-02-22
|
||||
### Added
|
||||
* *Nothing*
|
||||
|
||||
### Changed
|
||||
* [#1026](https://github.com/shlinkio/shlink/issues/1026) Removed non-inclusive terms from source code.
|
||||
|
||||
### Deprecated
|
||||
* *Nothing*
|
||||
|
||||
### Removed
|
||||
* *Nothing*
|
||||
|
||||
### Fixed
|
||||
* [#1024](https://github.com/shlinkio/shlink/issues/1024) Fixed migration that is incorrectly skipped due to the wrong condition being used to check it.
|
||||
* [#1031](https://github.com/shlinkio/shlink/issues/1031) Fixed shortening of twitter URLs with URL validation enabled.
|
||||
* [#1034](https://github.com/shlinkio/shlink/issues/1034) Fixed warning displayed when shlink is stopped while running it with swoole.
|
||||
|
||||
|
||||
## [2.6.0] - 2021-02-13
|
||||
### Added
|
||||
* [#856](https://github.com/shlinkio/shlink/issues/856) Added PHP 8.0 support.
|
||||
* [#941](https://github.com/shlinkio/shlink/issues/941) Added support to provide a title for every short URL.
|
||||
|
||||
The title can also be automatically resolved from the long URL, when no title was explicitly provided, but this option needs to be opted in.
|
||||
|
||||
* [#913](https://github.com/shlinkio/shlink/issues/913) Added support to import short URLs from a standard CSV file.
|
||||
|
||||
The file requires the `Long URL` and `Short code` columns, and it also accepts the optional `title`, `domain` and `tags` columns.
|
||||
|
||||
* [#1000](https://github.com/shlinkio/shlink/issues/1000) Added support to provide a `margin` query param when generating some URL's QR code.
|
||||
* [#675](https://github.com/shlinkio/shlink/issues/675) Added ability to track visits to the base URL, invalid short URLs or any other "not found" URL, as known as orphan visits.
|
||||
|
||||
This behavior is enabled by default, but you can opt out via env vars or config options.
|
||||
|
||||
This new orphan visits can be consumed in these ways:
|
||||
|
||||
* The `https://shlink.io/new-orphan-visit` mercure topic, which gets notified when an orphan visit occurs.
|
||||
* The `GET /visits/orphan` REST endpoint, which behaves like the short URL visits and tags visits endpoints, but returns only orphan visits.
|
||||
|
||||
### Changed
|
||||
* [#977](https://github.com/shlinkio/shlink/issues/977) Migrated from `laminas/laminas-paginator` to `pagerfanta/core` to handle pagination.
|
||||
* [#986](https://github.com/shlinkio/shlink/issues/986) Updated official docker image to use PHP 8.
|
||||
* [#1010](https://github.com/shlinkio/shlink/issues/1010) Increased timeout for database commands to 10 minutes.
|
||||
* [#874](https://github.com/shlinkio/shlink/issues/874) Changed how dist files are generated. Now there will be two for every supported PHP version, with and without support for swoole.
|
||||
|
||||
The dist files will have been built under the same PHP version they are meant to be run under, ensuring resolved dependencies are the proper ones.
|
||||
|
||||
### Deprecated
|
||||
* [#959](https://github.com/shlinkio/shlink/issues/959) Deprecated all command flags using camelCase format (like `--expirationDate`), adding kebab-case replacements for all of them (like `--expiration-date`).
|
||||
|
||||
All the existing camelCase flags will continue working for now, but will be removed in Shlink 3.0.0
|
||||
|
||||
* [#862](https://github.com/shlinkio/shlink/issues/862) Deprecated the endpoint to edit tags for a short URL (`PUT /short-urls/{shortCode}/tags`).
|
||||
|
||||
The short URL edition endpoint (`PATCH /short-urls/{shortCode}`) now supports setting the tags too. Use it instead.
|
||||
|
||||
### Removed
|
||||
* *Nothing*
|
||||
|
||||
### Fixed
|
||||
* [#988](https://github.com/shlinkio/shlink/issues/988) Fixed serving zero-byte static files in apache and apache-compatible web servers.
|
||||
* [#990](https://github.com/shlinkio/shlink/issues/990) Fixed short URLs not properly composed in REST API endpoints when both custom domain and custom base path are used.
|
||||
* [#1002](https://github.com/shlinkio/shlink/issues/1002) Fixed weird behavior in which GeoLite2 metadata's `buildEpoch` is parsed as string instead of int.
|
||||
* [#851](https://github.com/shlinkio/shlink/issues/851) Fixed error when trying to schedule swoole tasks in ARM architectures (like raspberry).
|
||||
|
||||
|
||||
## [2.5.2] - 2021-01-24
|
||||
### Added
|
||||
* [#965](https://github.com/shlinkio/shlink/issues/965) Added docs section for Architectural Decision Records, including the one for API key roles.
|
||||
|
||||
### Changed
|
||||
* *Nothing*
|
||||
|
||||
### Deprecated
|
||||
* *Nothing*
|
||||
|
||||
### Removed
|
||||
* *Nothing*
|
||||
|
||||
### Fixed
|
||||
* [#979](https://github.com/shlinkio/shlink/issues/979) Added missing `itemsPerPage` query param to swagger docs for short URLs list.
|
||||
* [#980](https://github.com/shlinkio/shlink/issues/980) Fixed value used for `Access-Control-Allow-Origin`, that could not work as expected when including an IP address.
|
||||
* [#947](https://github.com/shlinkio/shlink/issues/947) Fixed incorrect value returned in `Access-Control-Allow-Methods` header, which always contained all methods.
|
||||
|
||||
|
||||
## [2.5.1] - 2021-01-21
|
||||
### Added
|
||||
* *Nothing*
|
||||
|
||||
### Changed
|
||||
* *Nothing*
|
||||
|
||||
### Deprecated
|
||||
* *Nothing*
|
||||
|
||||
### Removed
|
||||
* *Nothing*
|
||||
|
||||
### Fixed
|
||||
* [#968](https://github.com/shlinkio/shlink/issues/968) Fixed index error in MariaDB while updating to v2.5.0.
|
||||
* [#972](https://github.com/shlinkio/shlink/issues/972) Fixed 500 error when calling single-step short URL creation endpoint.
|
||||
|
||||
|
||||
## [2.5.0] - 2021-01-17
|
||||
### Added
|
||||
* [#795](https://github.com/shlinkio/shlink/issues/795) and [#882](https://github.com/shlinkio/shlink/issues/882) Added new roles system to API keys.
|
||||
|
||||
API keys can have any combinations of these two roles now, allowing to limit their interactions:
|
||||
|
||||
* Can interact only with short URLs created with that API key.
|
||||
* Can interact only with short URLs for a specific domain.
|
||||
|
||||
* [#833](https://github.com/shlinkio/shlink/issues/833) Added support to connect through unix socket when using an external MySQL, MariaDB or Postgres database.
|
||||
|
||||
It can be provided during the installation, or as the `DB_UNIX_SOCKET` env var for the docker image.
|
||||
|
||||
* [#869](https://github.com/shlinkio/shlink/issues/869) Added support for Mercure Hub 0.10.
|
||||
* [#896](https://github.com/shlinkio/shlink/issues/896) Added support for unicode characters in custom slugs.
|
||||
* [#930](https://github.com/shlinkio/shlink/issues/930) Added new `bin/set-option` script that allows changing individual configuration options on existing shlink instances.
|
||||
* [#877](https://github.com/shlinkio/shlink/issues/877) Improved API tests on CORS, and "refined" middleware handling it.
|
||||
|
||||
### Changed
|
||||
* [#912](https://github.com/shlinkio/shlink/issues/912) Changed error templates to be plain html files, removing the dependency on `league/plates` package.
|
||||
* [#875](https://github.com/shlinkio/shlink/issues/875) Updated to `mezzio/mezzio-swoole` v3.1.
|
||||
* [#952](https://github.com/shlinkio/shlink/issues/952) Simplified in-project docs, by keeping only the basics and linking to the websites docs for anything else.
|
||||
|
||||
### Deprecated
|
||||
* [#917](https://github.com/shlinkio/shlink/issues/917) Deprecated `/{shortCode}/qr-code/{size}` URL, in favor of providing the size in the query instead, `/{shortCode}/qr-code?size={size}`.
|
||||
* [#924](https://github.com/shlinkio/shlink/issues/924) Deprecated mechanism to provide config options to the docker image through volumes. Use the env vars instead as a direct replacement.
|
||||
|
||||
### Removed
|
||||
* *Nothing*
|
||||
|
||||
### Fixed
|
||||
* *Nothing*
|
||||
|
||||
|
||||
## [2.4.2] - 2020-11-22
|
||||
### Added
|
||||
* *Nothing*
|
||||
|
||||
### Changed
|
||||
* *Nothing*
|
||||
|
||||
### Deprecated
|
||||
* *Nothing*
|
||||
|
||||
### Removed
|
||||
* *Nothing*
|
||||
|
||||
### Fixed
|
||||
* [#904](https://github.com/shlinkio/shlink/issues/904) Explicitly added missing "Domains" and "Integrations" tags to swagger docs.
|
||||
* [#901](https://github.com/shlinkio/shlink/issues/901) Ensured domains which are not in use on any short URL are not returned on the list of domains.
|
||||
* [#899](https://github.com/shlinkio/shlink/issues/899) Avoided filesystem errors produced while downloading geolite DB files on several shlink instances that share the same filesystem.
|
||||
* [#827](https://github.com/shlinkio/shlink/issues/827) Fixed swoole config getting loaded in config cache if a console command is run before any web execution, when swoole extension is enabled, making subsequent non-swoole web requests fail.
|
||||
|
||||
|
||||
## [2.4.1] - 2020-11-10
|
||||
### Added
|
||||
* *Nothing*
|
||||
|
||||
### Changed
|
||||
* *Nothing*
|
||||
|
||||
### Deprecated
|
||||
* *Nothing*
|
||||
|
||||
### Removed
|
||||
* *Nothing*
|
||||
|
||||
### Fixed
|
||||
* [#891](https://github.com/shlinkio/shlink/issues/891) Fixed error when running migrations in postgres due to incorrect return type hint.
|
||||
* [#846](https://github.com/shlinkio/shlink/issues/846) Fixed base image used for the PHP-FPM dev container.
|
||||
* [#867](https://github.com/shlinkio/shlink/issues/867) Fixed not-found redirects not using proper status (301 or 302) as configured during installation.
|
||||
|
||||
|
||||
## [2.4.0] - 2020-11-08
|
||||
### Added
|
||||
* [#829](https://github.com/shlinkio/shlink/issues/829) Added support for QR codes in SVG format, by passing `?format=svg` to the QR code URL.
|
||||
* [#820](https://github.com/shlinkio/shlink/issues/820) Added new option to force enabling or disabling URL validation on a per-URL basis.
|
||||
|
||||
Currently, there's a global config that tells if long URLs should be validated (by ensuring they are publicly accessible and return a 2xx status). However, this is either always applied or never applied.
|
||||
|
||||
Now, it is possible to enforce validation or enforce disabling validation when a new short URL is created or edited:
|
||||
|
||||
* On the `POST /short-url` and `PATCH /short-url/{shortCode}` endpoints, you can now pass `validateUrl: true/false` in order to enforce enabling or disabling validation, ignoring the global config. If the value is not provided, the global config is still normally applied.
|
||||
* On the `short-url:generate` CLI command, you can pass `--validate-url` or `--no-validate-url` flags, in order to enforce enabling or disabling validation. If none of them is provided, the global config is still normally applied.
|
||||
|
||||
* [#838](https://github.com/shlinkio/shlink/issues/838) Added new endpoint and CLI command to list existing domains.
|
||||
|
||||
It returns both default domain and specific domains that were used for some short URLs.
|
||||
|
||||
* REST endpoint: `GET /rest/v2/domains`
|
||||
* CLI Command: `domain:list`
|
||||
|
||||
* [#832](https://github.com/shlinkio/shlink/issues/832) Added support to customize the port in which the docker image listens by using the `PORT` env var or the `port` config option.
|
||||
|
||||
* [#860](https://github.com/shlinkio/shlink/issues/860) Added support to import links from bit.ly.
|
||||
|
||||
Run the command `short-urls:import bitly` and introduce requested information in order to import all your links.
|
||||
|
||||
Other sources will be supported in future releases.
|
||||
|
||||
### Changed
|
||||
* [#836](https://github.com/shlinkio/shlink/issues/836) Added support for the `<field>-<dir>` notation while determining how to order the short URLs list, as in `?orderBy=shortCode-DESC`. This effectively deprecates the array notation (`?orderBy[shortCode]=DESC`), that will be removed in Shlink 3.0.0
|
||||
* [#782](https://github.com/shlinkio/shlink/issues/782) Added code coverage to API tests.
|
||||
* [#858](https://github.com/shlinkio/shlink/issues/858) Updated to latest infection version. Updated docker images to PHP 7.4.11 and swoole 4.5.5
|
||||
* [#887](https://github.com/shlinkio/shlink/pull/887) Started tracking the API key used to create short URLs, in order to allow restrictions in future releases.
|
||||
|
||||
### Deprecated
|
||||
* [#883](https://github.com/shlinkio/shlink/issues/883) Deprecated `POST /tags` endpoint and `tag:create` command, as tags are created automatically while creating short URLs.
|
||||
|
||||
### Removed
|
||||
* *Nothing*
|
||||
|
||||
### Fixed
|
||||
* [#837](https://github.com/shlinkio/shlink/issues/837) Drastically improved performance when creating a new shortUrl and providing `findIfExists = true`.
|
||||
* [#878](https://github.com/shlinkio/shlink/issues/878) Added missing `gmp` extension to the official docker image.
|
||||
|
||||
|
||||
## [2.3.0] - 2020-08-09
|
||||
### Added
|
||||
* [#746](https://github.com/shlinkio/shlink/issues/746) Allowed to configure the kind of redirect you want to use for your short URLs. You can either set:
|
||||
|
||||
* `302` redirects: Default behavior. Visitors always hit the server.
|
||||
* `301` redirects: Better for SEO. Visitors hit the server the first time and then cache the redirect.
|
||||
|
||||
When selecting 301 redirects, you can also configure the time redirects are cached, to mitigate deviations in stats.
|
||||
|
||||
* [#734](https://github.com/shlinkio/shlink/issues/734) Added support to redirect to deeplinks and other links with schemas different from `http` and `https`.
|
||||
* [#709](https://github.com/shlinkio/shlink/issues/709) Added multi-architecture builds for the docker image.
|
||||
|
||||
* [#707](https://github.com/shlinkio/shlink/issues/707) Added `--all` flag to `short-urls:list` command, which will print all existing URLs in one go, with no pagination.
|
||||
|
||||
It has one limitation, though. Because of the way the CLI tooling works, all rows in the table must be loaded in memory. If the amount of URLs is too high, the command may fail due to too much memory usage.
|
||||
|
||||
### Changed
|
||||
* [#508](https://github.com/shlinkio/shlink/issues/508) Added mutation checks to database tests.
|
||||
* [#790](https://github.com/shlinkio/shlink/issues/790) Updated to doctrine/migrations v3.
|
||||
* [#798](https://github.com/shlinkio/shlink/issues/798) Updated to guzzlehttp/guzzle v7.
|
||||
* [#822](https://github.com/shlinkio/shlink/issues/822) Updated docker image to use PHP 7.4.9 with Alpine 3.12 and swoole 4.5.2.
|
||||
|
||||
### Deprecated
|
||||
* *Nothing*
|
||||
|
||||
### Removed
|
||||
* *Nothing*
|
||||
|
||||
### Fixed
|
||||
* *Nothing*
|
||||
|
||||
|
||||
## [2.2.2] - 2020-06-08
|
||||
### Added
|
||||
* *Nothing*
|
||||
|
||||
### Changed
|
||||
* *Nothing*
|
||||
|
||||
### Deprecated
|
||||
* *Nothing*
|
||||
|
||||
### Removed
|
||||
* *Nothing*
|
||||
|
||||
### Fixed
|
||||
* [#769](https://github.com/shlinkio/shlink/issues/769) Fixed custom slugs not allowing valid URL characters, like `.`, `_` or `~`.
|
||||
* [#781](https://github.com/shlinkio/shlink/issues/781) Fixed memory leak when loading visits for a tag which is used for big amounts of short URLs.
|
||||
|
||||
|
||||
## [2.2.1] - 2020-05-11
|
||||
### Added
|
||||
* *Nothing*
|
||||
|
||||
### Changed
|
||||
* *Nothing*
|
||||
|
||||
### Deprecated
|
||||
* *Nothing*
|
||||
|
||||
### Removed
|
||||
* *Nothing*
|
||||
|
||||
### Fixed
|
||||
* [#764](https://github.com/shlinkio/shlink/issues/764) Fixed error when trying to match an existing short URL which does not have `validSince` and/or `validUntil`, but you are providing either one of them for the new one.
|
||||
|
||||
|
||||
## [2.2.0] - 2020-05-09
|
||||
### Added
|
||||
* [#712](https://github.com/shlinkio/shlink/issues/712) Added support to integrate Shlink with a [mercure hub](https://mercure.rocks/) server.
|
||||
|
||||
Thanks to that, Shlink will be able to publish events that can be consumed in real time.
|
||||
|
||||
For now, two topics (events) are published, when new visits occur. Both include a payload with the visit and the shortUrl:
|
||||
|
||||
* A visit occurs on any short URL: `https://shlink.io/new-visit`.
|
||||
* A visit occurs on short URLs with a specific short code: `https://shlink.io/new-visit/{shortCode}`.
|
||||
|
||||
The updates are only published when serving Shlink with swoole.
|
||||
|
||||
Also, Shlink exposes a new endpoint `GET /rest/v2/mercure-info`, which returns the public URL of the mercure hub, and a valid JWT that can be used to subscribe to updates.
|
||||
|
||||
* [#673](https://github.com/shlinkio/shlink/issues/673) Added new `[GET /visits]` rest endpoint which returns basic visits stats.
|
||||
* [#674](https://github.com/shlinkio/shlink/issues/674) Added new `[GET /tags/{tag}/visits]` rest endpoint which returns visits by tag.
|
||||
|
||||
It works in the same way as the `[GET /short-urls/{shortCode}/visits]` one, returning the same response payload, and supporting the same query params, but the response is the list of visits in all short URLs which have provided tag.
|
||||
|
||||
* [#672](https://github.com/shlinkio/shlink/issues/672) Enhanced `[GET /tags]` rest endpoint so that it is possible to get basic stats info for every tag.
|
||||
|
||||
Now, if the `withStats=true` query param is provided, the response payload will include a new `stats` property which is a list with the amount of short URLs and visits for every tag.
|
||||
|
||||
Also, the `tag:list` CLI command has been changed and it always behaves like this.
|
||||
|
||||
* [#640](https://github.com/shlinkio/shlink/issues/640) Allowed to optionally disable visitors' IP address anonymization. This will make Shlink no longer be GDPR-compliant, but it's OK if you only plan to share your URLs in countries without this regulation.
|
||||
|
||||
### Changed
|
||||
* [#692](https://github.com/shlinkio/shlink/issues/692) Drastically improved performance when loading visits. Specially noticeable when loading big result sets.
|
||||
* [#657](https://github.com/shlinkio/shlink/issues/657) Updated how DB tests are run in travis by using docker containers which allow all engines to be covered.
|
||||
* [#751](https://github.com/shlinkio/shlink/issues/751) Updated PHP and swoole versions used in docker image, and removed mssql-tools, as they are not needed.
|
||||
|
||||
### Deprecated
|
||||
* *Nothing*
|
||||
|
||||
### Removed
|
||||
* *Nothing*
|
||||
|
||||
### Fixed
|
||||
* [#729](https://github.com/shlinkio/shlink/issues/729) Fixed weird error when fetching multiple visits result sets concurrently using mariadb or mysql.
|
||||
* [#735](https://github.com/shlinkio/shlink/issues/735) Fixed error when cleaning metadata cache during installation when APCu is enabled.
|
||||
* [#677](https://github.com/shlinkio/shlink/issues/677) Fixed `/health` endpoint returning `503` fail responses when the database connection has expired.
|
||||
* [#732](https://github.com/shlinkio/shlink/issues/732) Fixed wrong client IP in access logs when serving app with swoole behind load balancer.
|
||||
|
||||
|
||||
## [2.1.4] - 2020-04-30
|
||||
### Added
|
||||
* *Nothing*
|
||||
|
||||
### Changed
|
||||
* *Nothing*
|
||||
|
||||
### Deprecated
|
||||
* *Nothing*
|
||||
|
||||
### Removed
|
||||
* *Nothing*
|
||||
|
||||
### Fixed
|
||||
* [#742](https://github.com/shlinkio/shlink/issues/742) Allowed a custom GeoLite2 license key to be provided, in order to avoid download limits.
|
||||
|
||||
|
||||
## [2.1.3] - 2020-04-09
|
||||
### Added
|
||||
* *Nothing*
|
||||
|
||||
### Changed
|
||||
* *Nothing*
|
||||
|
||||
### Deprecated
|
||||
* *Nothing*
|
||||
|
||||
### Removed
|
||||
* *Nothing*
|
||||
|
||||
### Fixed
|
||||
* [#712](https://github.com/shlinkio/shlink/issues/712) Fixed app set-up not clearing entities metadata cache.
|
||||
* [#711](https://github.com/shlinkio/shlink/issues/711) Fixed `HEAD` requests returning a duplicated `Content-Length` header.
|
||||
* [#716](https://github.com/shlinkio/shlink/issues/716) Fixed Twitter not properly displaying preview for final long URL.
|
||||
* [#717](https://github.com/shlinkio/shlink/issues/717) Fixed DB connection expiring on task workers when using swoole.
|
||||
* [#705](https://github.com/shlinkio/shlink/issues/705) Fixed how the short URL domain is inferred when generating QR codes, making sure the configured domain is respected even if the request is performed using a different one, and only when a custom domain is used, then that one is used instead.
|
||||
|
||||
|
||||
## [2.1.2] - 2020-03-29
|
||||
### Added
|
||||
* *Nothing*
|
||||
|
||||
### Changed
|
||||
* [#696](https://github.com/shlinkio/shlink/issues/696) Updated to infection v0.16.
|
||||
|
||||
### Deprecated
|
||||
* *Nothing*
|
||||
|
||||
### Removed
|
||||
* *Nothing*
|
||||
|
||||
### Fixed
|
||||
* [#700](https://github.com/shlinkio/shlink/issues/700) Fixed migration not working with postgres.
|
||||
* [#690](https://github.com/shlinkio/shlink/issues/690) Fixed tags being incorrectly sluggified when filtering short URL lists, making results not be the expected.
|
||||
|
||||
|
||||
## [2.1.1] - 2020-03-28
|
||||
### Added
|
||||
* *Nothing*
|
||||
|
||||
### Changed
|
||||
* *Nothing*
|
||||
|
||||
### Deprecated
|
||||
* *Nothing*
|
||||
|
||||
### Removed
|
||||
* *Nothing*
|
||||
|
||||
### Fixed
|
||||
* [#697](https://github.com/shlinkio/shlink/issues/697) Recovered `.htaccess` file that was unintentionally removed in v2.1.0, making Shlink unusable with Apache.
|
||||
|
||||
|
||||
## [2.1.0] - 2020-03-28
|
||||
### Added
|
||||
* [#626](https://github.com/shlinkio/shlink/issues/626) Added support for Microsoft SQL Server.
|
||||
* [#556](https://github.com/shlinkio/shlink/issues/556) Short code lengths can now be customized, both globally and on a per-short URL basis.
|
||||
* [#541](https://github.com/shlinkio/shlink/issues/541) Added a request ID that is returned on `X-Request-Id` header, can be provided from outside and is set in log entries.
|
||||
* [#642](https://github.com/shlinkio/shlink/issues/642) IP geolocation is now performed over the non-anonymized IP address when using swoole.
|
||||
* [#521](https://github.com/shlinkio/shlink/issues/521) The long URL for any existing short URL can now be edited using the `PATCH /short-urls/{shortCode}` endpoint.
|
||||
|
||||
### Changed
|
||||
* [#656](https://github.com/shlinkio/shlink/issues/656) Updated to PHPUnit 9.
|
||||
* [#641](https://github.com/shlinkio/shlink/issues/641) Added two new flags to the `visit:locate` command, `--retry` and `--all`.
|
||||
|
||||
* When `--retry` is provided, it will try to re-locate visits which IP address was originally considered not found, in case it was a temporal issue.
|
||||
* When `--all` is provided together with `--retry`, it will try to re-locate all existing visits. A warning and confirmation are displayed, as this can have side effects.
|
||||
|
||||
### Deprecated
|
||||
* *Nothing*
|
||||
|
||||
### Removed
|
||||
* *Nothing*
|
||||
|
||||
### Fixed
|
||||
* [#665](https://github.com/shlinkio/shlink/issues/665) Fixed `base_url_redirect_to` simplified config option not being properly parsed.
|
||||
* [#663](https://github.com/shlinkio/shlink/issues/663) Fixed Shlink allowing short URLs to be created with an empty custom slug.
|
||||
* [#678](https://github.com/shlinkio/shlink/issues/678) Fixed `db` commands not running in a non-interactive way.
|
||||
|
||||
|
||||
## [2.0.5] - 2020-02-09
|
||||
### Added
|
||||
* [#651](https://github.com/shlinkio/shlink/issues/651) Documented how Shlink behaves when using multiple domains.
|
||||
|
||||
### Changed
|
||||
* *Nothing*
|
||||
|
||||
### Deprecated
|
||||
* *Nothing*
|
||||
|
||||
### Removed
|
||||
* *Nothing*
|
||||
|
||||
### Fixed
|
||||
* [#648](https://github.com/shlinkio/shlink/issues/648) Ensured any user can write in log files, in case shlink is run by several system users.
|
||||
* [#650](https://github.com/shlinkio/shlink/issues/650) Ensured default domain is ignored when trying to create a short URL.
|
||||
|
||||
|
||||
## [2.0.4] - 2020-02-02
|
||||
### Added
|
||||
* *Nothing*
|
||||
|
||||
### Changed
|
||||
* [#577](https://github.com/shlinkio/shlink/issues/577) Wrapped params used to customize short URL lists into a DTO with implicit validation.
|
||||
|
||||
### Deprecated
|
||||
* *Nothing*
|
||||
|
||||
### Removed
|
||||
* *Nothing*
|
||||
|
||||
### Fixed
|
||||
* [#620](https://github.com/shlinkio/shlink/issues/620) Ensured "controlled" errors (like validation errors and such) won't be logged with error level, preventing logs to be polluted.
|
||||
* [#637](https://github.com/shlinkio/shlink/issues/637) Fixed several work flows in which short URLs with domain are handled form the API.
|
||||
* [#644](https://github.com/shlinkio/shlink/issues/644) Fixed visits to short URL on non-default domain being linked to the URL on default domain with the same short code.
|
||||
* [#643](https://github.com/shlinkio/shlink/issues/643) Fixed searching on short URL lists not taking into consideration the domain name.
|
||||
|
||||
|
||||
## [2.0.3] - 2020-01-27
|
||||
### Added
|
||||
* *Nothing*
|
||||
|
||||
### Changed
|
||||
* *Nothing*
|
||||
|
||||
### Deprecated
|
||||
* *Nothing*
|
||||
|
||||
### Removed
|
||||
* *Nothing*
|
||||
|
||||
### Fixed
|
||||
* [#624](https://github.com/shlinkio/shlink/issues/624) Fixed order in which headers for remote IP detection are inspected.
|
||||
* [#623](https://github.com/shlinkio/shlink/issues/623) Fixed short URLs metadata being impossible to reset.
|
||||
* [#628](https://github.com/shlinkio/shlink/issues/628) Fixed `GET /short-urls/{shortCode}` REST endpoint returning a 404 for short URLs which are not enabled.
|
||||
* [#621](https://github.com/shlinkio/shlink/issues/621) Fixed permission denied error when updating same GeoLite file version more than once.
|
||||
|
||||
|
||||
## [2.0.2] - 2020-01-12
|
||||
### Added
|
||||
* *Nothing*
|
||||
|
||||
### Changed
|
||||
* *Nothing*
|
||||
|
||||
### Deprecated
|
||||
* *Nothing*
|
||||
|
||||
### Removed
|
||||
* *Nothing*
|
||||
|
||||
### Fixed
|
||||
* [#614](https://github.com/shlinkio/shlink/issues/614) Fixed `OPTIONS` requests including the `Origin` header not always returning an empty body with status 2xx.
|
||||
* [#615](https://github.com/shlinkio/shlink/issues/615) Fixed query args with no value being lost from the long URL when users are redirected.
|
||||
|
||||
|
||||
## [2.0.1] - 2020-01-10
|
||||
### Added
|
||||
* *Nothing*
|
||||
|
||||
### Changed
|
||||
* *Nothing*
|
||||
|
||||
### Deprecated
|
||||
* *Nothing*
|
||||
|
||||
### Removed
|
||||
* *Nothing*
|
||||
|
||||
### Fixed
|
||||
* [#607](https://github.com/shlinkio/shlink/issues/607) Added missing info on UPGRADE.md doc.
|
||||
* [#610](https://github.com/shlinkio/shlink/issues/610) Fixed use of hardcoded quotes on a database migration which makes it fail on postgres.
|
||||
* [#605](https://github.com/shlinkio/shlink/issues/605) Fixed crashes occurring when migrating from old Shlink versions with nullable DB columns that are assigned to non-nullable entity typed props.
|
||||
|
||||
|
||||
## [2.0.0] - 2020-01-08
|
||||
### Added
|
||||
* [#429](https://github.com/shlinkio/shlink/issues/429) Added support for PHP 7.4
|
||||
* [#529](https://github.com/shlinkio/shlink/issues/529) Created an UPGRADING.md file explaining how to upgrade from v1.x to v2.x
|
||||
* [#594](https://github.com/shlinkio/shlink/issues/594) Updated external shlink packages, including installer v4.0, which adds the option to ask for the redis cluster config.
|
||||
|
||||
### Changed
|
||||
* [#592](https://github.com/shlinkio/shlink/issues/592) Updated coding styles to use [shlinkio/php-coding-standard](https://github.com/shlinkio/php-coding-standard) v2.1.0.
|
||||
* [#530](https://github.com/shlinkio/shlink/issues/530) Migrated project from deprecated `zendframework` components to the new `laminas` and `mezzio` ones.
|
||||
|
||||
### Deprecated
|
||||
* *Nothing*
|
||||
|
||||
### Removed
|
||||
* [#429](https://github.com/shlinkio/shlink/issues/429) Dropped support for PHP 7.2 and 7.3
|
||||
|
||||
* [#229](https://github.com/shlinkio/shlink/issues/229) Remove everything which was deprecated, including:
|
||||
|
||||
* Preview generation feature completely removed.
|
||||
* Authentication against REST API using JWT is no longer supported.
|
||||
|
||||
See [UPGRADE](UPGRADE.md#from-v1x-to-v2x) doc in order to get details on how to migrate to this version.
|
||||
|
||||
### Fixed
|
||||
* [#600](https://github.com/shlinkio/shlink/issues/600) Fixed health action so that it works with and without version in the path.
|
||||
@@ -1,17 +0,0 @@
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"android": {
|
||||
"description": "The long URL to redirect to when the short URL is visited from a device running Android",
|
||||
"type": ["string"]
|
||||
},
|
||||
"ios": {
|
||||
"description": "The long URL to redirect to when the short URL is visited from a device running iOS",
|
||||
"type": ["string"]
|
||||
},
|
||||
"desktop": {
|
||||
"description": "The long URL to redirect to when the short URL is visited from a desktop browser",
|
||||
"type": ["string"]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
{
|
||||
"type": "object",
|
||||
"allOf": [{
|
||||
"$ref": "./DeviceLongUrls.json"
|
||||
}],
|
||||
"properties": {
|
||||
"android": {
|
||||
"type": ["null"]
|
||||
},
|
||||
"ios": {
|
||||
"type": ["null"]
|
||||
},
|
||||
"desktop": {
|
||||
"type": ["null"]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"type": "object",
|
||||
"required": ["android", "ios", "desktop"],
|
||||
"allOf": [{
|
||||
"$ref": "./DeviceLongUrlsEdit.json"
|
||||
}]
|
||||
}
|
||||
@@ -1,14 +1,10 @@
|
||||
{
|
||||
"type": "object",
|
||||
"required": ["visitedUrl", "type"],
|
||||
"required": ["type"],
|
||||
"allOf": [{
|
||||
"$ref": "./Visit.json"
|
||||
}],
|
||||
"properties": {
|
||||
"visitedUrl": {
|
||||
"type": ["string", "null"],
|
||||
"description": "The originally visited URL that triggered the tracking of this visit"
|
||||
},
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
|
||||
31
docs/swagger/definitions/SetShortUrlRedirectRule.json
Normal file
31
docs/swagger/definitions/SetShortUrlRedirectRule.json
Normal file
@@ -0,0 +1,31 @@
|
||||
{
|
||||
"type": "object",
|
||||
"required": ["longUrl", "conditions"],
|
||||
"properties": {
|
||||
"longUrl": {
|
||||
"description": "Long URL to redirect to when this condition matches",
|
||||
"type": "string"
|
||||
},
|
||||
"conditions": {
|
||||
"description": "List of conditions that need to match in order to consider this rule matches",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"required": ["type", "matchKey", "matchValue"],
|
||||
"properties": {
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": ["device", "language", "query-param", "ip-address"],
|
||||
"description": "The type of the condition, which will determine the logic used to match it"
|
||||
},
|
||||
"matchKey": {
|
||||
"type": ["string", "null"]
|
||||
},
|
||||
"matchValue": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,9 +4,7 @@
|
||||
"shortCode",
|
||||
"shortUrl",
|
||||
"longUrl",
|
||||
"deviceLongUrls",
|
||||
"dateCreated",
|
||||
"visitsCount",
|
||||
"visitsSummary",
|
||||
"tags",
|
||||
"meta",
|
||||
@@ -28,19 +26,11 @@
|
||||
"type": "string",
|
||||
"description": "The original long URL."
|
||||
},
|
||||
"deviceLongUrls": {
|
||||
"$ref": "./DeviceLongUrlsResp.json"
|
||||
},
|
||||
"dateCreated": {
|
||||
"type": "string",
|
||||
"format": "date-time",
|
||||
"description": "The date in which the short URL was created in ISO format."
|
||||
},
|
||||
"visitsCount": {
|
||||
"deprecated": true,
|
||||
"type": "integer",
|
||||
"description": "**[DEPRECATED]** Use `visitsSummary.total` instead."
|
||||
},
|
||||
"visitsSummary": {
|
||||
"$ref": "./VisitsSummary.json"
|
||||
},
|
||||
|
||||
@@ -5,9 +5,6 @@
|
||||
"description": "The long URL this short URL will redirect to",
|
||||
"type": "string"
|
||||
},
|
||||
"deviceLongUrls": {
|
||||
"$ref": "./DeviceLongUrlsEdit.json"
|
||||
},
|
||||
"validSince": {
|
||||
"description": "The date (in ISO-8601 format) from which this short code will be valid",
|
||||
"type": ["string", "null"]
|
||||
@@ -20,11 +17,6 @@
|
||||
"description": "The maximum number of allowed visits for this short code",
|
||||
"type": ["number", "null"]
|
||||
},
|
||||
"validateUrl": {
|
||||
"deprecated": true,
|
||||
"description": "**[DEPRECATED]** Tells if the long URL should or should not be validated as a reachable URL. Defaults to `false`",
|
||||
"type": "boolean"
|
||||
},
|
||||
"tags": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
|
||||
13
docs/swagger/definitions/ShortUrlRedirectRule.json
Normal file
13
docs/swagger/definitions/ShortUrlRedirectRule.json
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"type": "object",
|
||||
"required": ["priority"],
|
||||
"properties": {
|
||||
"priority": {
|
||||
"description": "Order in which attempting to match the rule. Lower goes first",
|
||||
"type": "number"
|
||||
}
|
||||
},
|
||||
"allOf": [{
|
||||
"$ref": "./SetShortUrlRedirectRule.json"
|
||||
}]
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"type": "object",
|
||||
"required": ["tag", "shortUrlsCount", "visitsSummary", "visitsCount"],
|
||||
"required": ["tag", "shortUrlsCount", "visitsSummary"],
|
||||
"properties": {
|
||||
"tag": {
|
||||
"type": "string",
|
||||
@@ -12,11 +12,6 @@
|
||||
},
|
||||
"visitsSummary": {
|
||||
"$ref": "./VisitsSummary.json"
|
||||
},
|
||||
"visitsCount": {
|
||||
"deprecated": true,
|
||||
"type": "number",
|
||||
"description": "**[DEPRECATED]** Use visitsSummary.total instead"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"type": "object",
|
||||
"required": ["referer", "date", "userAgent", "visitLocation"],
|
||||
"required": ["referer", "date", "userAgent", "visitLocation", "potentialBot", "visitedUrl"],
|
||||
"properties": {
|
||||
"referer": {
|
||||
"type": "string",
|
||||
@@ -21,6 +21,10 @@
|
||||
"potentialBot": {
|
||||
"type": "boolean",
|
||||
"description": "Tells if Shlink thinks this visit comes potentially from a bot or crawler"
|
||||
},
|
||||
"visitedUrl": {
|
||||
"type": ["string", "null"],
|
||||
"description": "The originally visited URL that triggered the tracking of this visit"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,22 +1,12 @@
|
||||
{
|
||||
"type": "object",
|
||||
"required": ["nonOrphanVisits", "orphanVisits", "visitsCount", "orphanVisitsCount"],
|
||||
"required": ["nonOrphanVisits", "orphanVisits"],
|
||||
"properties": {
|
||||
"nonOrphanVisits": {
|
||||
"$ref": "./VisitsSummary.json"
|
||||
},
|
||||
"orphanVisits": {
|
||||
"$ref": "./VisitsSummary.json"
|
||||
},
|
||||
"visitsCount": {
|
||||
"deprecated": true,
|
||||
"type": "number",
|
||||
"description": "**[DEPRECATED]** Use nonOrphanVisits.total instead"
|
||||
},
|
||||
"orphanVisitsCount": {
|
||||
"deprecated": true,
|
||||
"type": "number",
|
||||
"description": "**[DEPRECATED]** Use orphanVisits.total instead"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user