mirror of
https://github.com/shlinkio/shlink.git
synced 2025-12-10 22:13:47 -06:00
Compare commits
476 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
89419e278c | ||
|
|
1996745f64 | ||
|
|
cfab13bc47 | ||
|
|
9432a5ba78 | ||
|
|
7812a85b39 | ||
|
|
1e0b6be67d | ||
|
|
88e5bb5618 | ||
|
|
db1411d3f8 | ||
|
|
933c54e884 | ||
|
|
f3ff059d48 | ||
|
|
039a58bb44 | ||
|
|
0604237b94 | ||
|
|
c8537e4f71 | ||
|
|
c42fb67efc | ||
|
|
ad15ae1922 | ||
|
|
a731e01bd4 | ||
|
|
63bea36c05 | ||
|
|
8a33c6968a | ||
|
|
359129f586 | ||
|
|
fdcc9933a3 | ||
|
|
94adba95eb | ||
|
|
8bafd82e1d | ||
|
|
d2bc9f7c2b | ||
|
|
9f564b9785 | ||
|
|
1b6929acf6 | ||
|
|
91fd5809ff | ||
|
|
c7fd6b3cba | ||
|
|
1eb1f5344c | ||
|
|
f9ec4cea62 | ||
|
|
c3961b139a | ||
|
|
c2aae9640d | ||
|
|
b4043be7fa | ||
|
|
49c67abf0a | ||
|
|
c6f718eb11 | ||
|
|
d3e8e9a735 | ||
|
|
8f1542c7aa | ||
|
|
058c0ebfaf | ||
|
|
b69db91378 | ||
|
|
6113c28768 | ||
|
|
506ed47531 | ||
|
|
10173d2ab8 | ||
|
|
9ee709f0f3 | ||
|
|
0fe28a5eb5 | ||
|
|
2142afae89 | ||
|
|
e7f4b84c65 | ||
|
|
2d83b8d046 | ||
|
|
dfef735c89 | ||
|
|
c34c4e0eea | ||
|
|
f024fd414e | ||
|
|
12d81c3213 | ||
|
|
628fb9ebb5 | ||
|
|
e21cea1971 | ||
|
|
37088b1a4b | ||
|
|
b5f8e8a4cd | ||
|
|
a236f19dc4 | ||
|
|
94426c7bf4 | ||
|
|
9dcc51abde | ||
|
|
70e376d569 | ||
|
|
14a7e3bb05 | ||
|
|
10dab5be20 | ||
|
|
532700028a | ||
|
|
fc54a25c32 | ||
|
|
ba16ba45f2 | ||
|
|
51c732a013 | ||
|
|
0f17990821 | ||
|
|
02500143c1 | ||
|
|
9c22c7fc9c | ||
|
|
7860225c25 | ||
|
|
506ed6207f | ||
|
|
30ed1d7c6b | ||
|
|
b5a9353b85 | ||
|
|
cae18ccfb3 | ||
|
|
f876769b67 | ||
|
|
2b06f56a9a | ||
|
|
1c38ab1217 | ||
|
|
fb9e8cd79f | ||
|
|
eb199a61da | ||
|
|
25de0263c5 | ||
|
|
41c03a66e4 | ||
|
|
13c1b12d84 | ||
|
|
fe10aaf245 | ||
|
|
464e3d7f8e | ||
|
|
ac40a7021b | ||
|
|
c60a5e750b | ||
|
|
785f728afc | ||
|
|
648696f778 | ||
|
|
774a579a94 | ||
|
|
98bbb01165 | ||
|
|
0bcb9e0438 | ||
|
|
edb8b57a48 | ||
|
|
b01f271f72 | ||
|
|
98b504a2de | ||
|
|
075e6347b6 | ||
|
|
92a70b8c11 | ||
|
|
613c7b7368 | ||
|
|
232f6e37c6 | ||
|
|
c818d5603d | ||
|
|
766b227e47 | ||
|
|
95be5a93fc | ||
|
|
20c41690da | ||
|
|
22b5fa5a83 | ||
|
|
0c4d1b6d2f | ||
|
|
d2514b7555 | ||
|
|
2d5734fc8b | ||
|
|
478ac344ff | ||
|
|
e40b82618a | ||
|
|
51dd671174 | ||
|
|
5b5d0aae49 | ||
|
|
56df880a93 | ||
|
|
afa509613a | ||
|
|
3be49a25a0 | ||
|
|
8b259b364d | ||
|
|
13d9b7b0a7 | ||
|
|
2b33095392 | ||
|
|
3a1ce40a49 | ||
|
|
a68300f19a | ||
|
|
3318987d63 | ||
|
|
1f825797f6 | ||
|
|
650fafb7c4 | ||
|
|
978e24d6fa | ||
|
|
c3d3cc6288 | ||
|
|
223901324f | ||
|
|
47293be85c | ||
|
|
18c4c39fee | ||
|
|
e762d28b67 | ||
|
|
f5c6bc8204 | ||
|
|
3369afe22c | ||
|
|
1d96cc0279 | ||
|
|
cd4fcc9b0a | ||
|
|
834bc4ae20 | ||
|
|
92d7a44cee | ||
|
|
c8e3b3df0a | ||
|
|
77244b52c9 | ||
|
|
9e93e34e12 | ||
|
|
733b2e5647 | ||
|
|
26fef87f3b | ||
|
|
f4aaf02d55 | ||
|
|
314a99862d | ||
|
|
240d9df177 | ||
|
|
fb995f2bea | ||
|
|
436be1985c | ||
|
|
850e8574e9 | ||
|
|
c2743cb488 | ||
|
|
f1157aa177 | ||
|
|
497429e685 | ||
|
|
2cad5dd435 | ||
|
|
f38f1ae5da | ||
|
|
9c1db35d81 | ||
|
|
11b8943919 | ||
|
|
27d24a4f15 | ||
|
|
b2dbc4cf52 | ||
|
|
1a7a745f2e | ||
|
|
99bc1a21dd | ||
|
|
cea8a982e2 | ||
|
|
8bd1c6a79a | ||
|
|
71a3b993b1 | ||
|
|
6e25e3c31d | ||
|
|
b15e832cf4 | ||
|
|
851929ebef | ||
|
|
87d5f9bc75 | ||
|
|
b7d9ba8258 | ||
|
|
6526cf8c44 | ||
|
|
a85afb2bee | ||
|
|
8b4067efbe | ||
|
|
c7c2272fab | ||
|
|
bc77750713 | ||
|
|
1ceb38f50b | ||
|
|
d273b56144 | ||
|
|
5cd7305666 | ||
|
|
3040a22c02 | ||
|
|
5eb1808217 | ||
|
|
5eb14c5315 | ||
|
|
a18360a4d6 | ||
|
|
af2d67695b | ||
|
|
449a588796 | ||
|
|
7bbc938743 | ||
|
|
766758ff9b | ||
|
|
63d943d59d | ||
|
|
053e1f3073 | ||
|
|
f3da345bf3 | ||
|
|
745255736a | ||
|
|
8fd53afe3f | ||
|
|
259635ea2a | ||
|
|
a1f2e6dc5c | ||
|
|
81e07bf08d | ||
|
|
c650a3e665 | ||
|
|
65c01034ff | ||
|
|
48f910aaaa | ||
|
|
e511e15a87 | ||
|
|
ed09bf90eb | ||
|
|
0ddfcb75dd | ||
|
|
193be55f0c | ||
|
|
7ffb64eee1 | ||
|
|
0a2cc554c6 | ||
|
|
af783dea57 | ||
|
|
a68a17f6b4 | ||
|
|
e9fe1ac5d4 | ||
|
|
88e97f18ad | ||
|
|
3372a2a9c8 | ||
|
|
f02a8c876c | ||
|
|
1549509eb8 | ||
|
|
62fde5a8e2 | ||
|
|
221e061ea6 | ||
|
|
9ad565f8c8 | ||
|
|
11fa28e489 | ||
|
|
d7e51b388e | ||
|
|
5ef2df3d53 | ||
|
|
9c251b3646 | ||
|
|
2807b9ce2f | ||
|
|
2f39aff2fe | ||
|
|
b8d7917691 | ||
|
|
d228c16f82 | ||
|
|
c34bfac6b1 | ||
|
|
4e7d09035a | ||
|
|
83570f5c25 | ||
|
|
6ad8b03850 | ||
|
|
736e09adfe | ||
|
|
e80af78e09 | ||
|
|
d533adf7ce | ||
|
|
509ef668e6 | ||
|
|
e715a0fb6f | ||
|
|
72a962ec6d | ||
|
|
853c50a819 | ||
|
|
f10a9d3972 | ||
|
|
a77e07f906 | ||
|
|
d4d97c3182 | ||
|
|
55724dbff6 | ||
|
|
9e34183901 | ||
|
|
88c283952c | ||
|
|
2ede615da8 | ||
|
|
84d12f6811 | ||
|
|
4f3c2c7d2d | ||
|
|
b8ac9f3673 | ||
|
|
06c0a94b31 | ||
|
|
5d12b1d952 | ||
|
|
85c4c09afa | ||
|
|
e7c83d0b38 | ||
|
|
58de998596 | ||
|
|
bfaab6c494 | ||
|
|
d83081f4e9 | ||
|
|
c65349d265 | ||
|
|
e74ee793a0 | ||
|
|
ede58efe96 | ||
|
|
3f30af4794 | ||
|
|
6331fa3ed3 | ||
|
|
d121d4d496 | ||
|
|
8499087a3b | ||
|
|
bb72c96ebb | ||
|
|
557c74286b | ||
|
|
67abe21716 | ||
|
|
33cea36b15 | ||
|
|
4e8f3f737a | ||
|
|
35b835ec7b | ||
|
|
eff4f1fca3 | ||
|
|
6f6388b2fc | ||
|
|
19f56e7ab0 | ||
|
|
6a96b72b94 | ||
|
|
7634f55587 | ||
|
|
571a4643ab | ||
|
|
d5544554ef | ||
|
|
85065c9330 | ||
|
|
86cc2b717c | ||
|
|
89f70114e4 | ||
|
|
8274525f75 | ||
|
|
fef512a7a3 | ||
|
|
deb9d4bdc7 | ||
|
|
259aadfdb2 | ||
|
|
fe660654ed | ||
|
|
b2fc19af44 | ||
|
|
7434616a8d | ||
|
|
fbf1aabcf5 | ||
|
|
8ee905882f | ||
|
|
2946b630c5 | ||
|
|
b2bfe9799a | ||
|
|
d7e300e2d5 | ||
|
|
0c75202936 | ||
|
|
81bed53f90 | ||
|
|
a56ff1293e | ||
|
|
c323bfcd63 | ||
|
|
f57f159002 | ||
|
|
fa08014226 | ||
|
|
052c9e76a1 | ||
|
|
8298ef36f8 | ||
|
|
b11d5c6864 | ||
|
|
08394431f8 | ||
|
|
a9ae4a24d0 | ||
|
|
9b7b91402c | ||
|
|
178a99b993 | ||
|
|
a8f046dfff | ||
|
|
42ff0d5b69 | ||
|
|
6aaea2ac26 | ||
|
|
b5ff568651 | ||
|
|
4a0b7e3fc9 | ||
|
|
1fee745786 | ||
|
|
a6e0916272 | ||
|
|
dbef32ffcb | ||
|
|
7ddb3e7a70 | ||
|
|
fd34332e69 | ||
|
|
51d838870d | ||
|
|
4619ebd014 | ||
|
|
f2371b6124 | ||
|
|
b5b5f92eda | ||
|
|
781c083c9f | ||
|
|
a444ed0246 | ||
|
|
9a69d06531 | ||
|
|
15cb3bb73c | ||
|
|
7ca605e216 | ||
|
|
59a4704658 | ||
|
|
48ecef3436 | ||
|
|
a5a98bd578 | ||
|
|
12a08cb373 | ||
|
|
3c6f12aec6 | ||
|
|
d228b88e51 | ||
|
|
95685d958d | ||
|
|
1a278eaf07 | ||
|
|
72f1e243b5 | ||
|
|
d6b103de83 | ||
|
|
fca3891819 | ||
|
|
3ec24e3c67 | ||
|
|
532102e662 | ||
|
|
fcd82522ab | ||
|
|
102169b6c7 | ||
|
|
dba9302f78 | ||
|
|
92ad6d2732 | ||
|
|
7e573bdb9b | ||
|
|
6f837b3b91 | ||
|
|
b08c498b13 | ||
|
|
a661d05100 | ||
|
|
9e6f129de6 | ||
|
|
4c1ff72438 | ||
|
|
6f95acc202 | ||
|
|
bd73362c94 | ||
|
|
f6d70c599e | ||
|
|
1b9c8377ae | ||
|
|
9f6975119e | ||
|
|
a094be2b9e | ||
|
|
819a535bfe | ||
|
|
e4fe7adf00 | ||
|
|
79c5418ac2 | ||
|
|
b5010e4d8c | ||
|
|
3085fa76cf | ||
|
|
1fd7d58084 | ||
|
|
eae001a34a | ||
|
|
d7ecef94f2 | ||
|
|
98364a1aae | ||
|
|
9ccb866e5e | ||
|
|
3f1d61e01e | ||
|
|
93a277a94d | ||
|
|
a10ca655a2 | ||
|
|
bb270396b6 | ||
|
|
525a306ec6 | ||
|
|
1dd71d2ee7 | ||
|
|
ac2e249746 | ||
|
|
af569ad7a5 | ||
|
|
bf121c58ba | ||
|
|
d2403367b5 | ||
|
|
84a187a26f | ||
|
|
3149adebdb | ||
|
|
228bf093d3 | ||
|
|
26589e6126 | ||
|
|
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 | ||
|
|
1901964de1 | ||
|
|
80e9c2452b | ||
|
|
5ad4b39160 | ||
|
|
89b73a9cfa | ||
|
|
e2d8334d69 | ||
|
|
9b16d7acc0 | ||
|
|
6836840746 | ||
|
|
4084d301ca | ||
|
|
added21b18 | ||
|
|
8cd77391cc | ||
|
|
05ebfccc63 | ||
|
|
cb3a690294 | ||
|
|
194a7b0e57 | ||
|
|
98e4d01feb | ||
|
|
c22e3895b5 | ||
|
|
9a76c19615 | ||
|
|
59fa088975 | ||
|
|
163244f40f | ||
|
|
a89b53af4f |
@ -1,5 +1,6 @@
|
||||
bin/rr
|
||||
config/autoload/*local*
|
||||
config/params/shlink_dev_env.*
|
||||
data/infra
|
||||
data/cache/*
|
||||
data/log/*
|
||||
|
||||
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
|
||||
|
||||
49
.github/DISCUSSION_TEMPLATE/help-wanted.yml
vendored
49
.github/DISCUSSION_TEMPLATE/help-wanted.yml
vendored
@ -1,49 +0,0 @@
|
||||
title: 'Help wanted'
|
||||
body:
|
||||
- type: input
|
||||
validations:
|
||||
required: true
|
||||
attributes:
|
||||
label: Shlink version
|
||||
placeholder: x.y.z
|
||||
- type: input
|
||||
validations:
|
||||
required: true
|
||||
attributes:
|
||||
label: PHP version
|
||||
placeholder: x.y.z
|
||||
- type: dropdown
|
||||
validations:
|
||||
required: true
|
||||
attributes:
|
||||
label: How do you serve Shlink
|
||||
options:
|
||||
- Self-hosted Apache
|
||||
- Self-hosted nginx
|
||||
- Self-hosted RoadRunner
|
||||
- Docker image
|
||||
- Other (explain in summary)
|
||||
- type: dropdown
|
||||
validations:
|
||||
required: true
|
||||
attributes:
|
||||
label: Database engine
|
||||
options:
|
||||
- MySQL
|
||||
- MariaDB
|
||||
- PostgreSQL
|
||||
- MicrosoftSQL
|
||||
- SQLite
|
||||
- type: input
|
||||
validations:
|
||||
required: true
|
||||
attributes:
|
||||
label: Database version
|
||||
placeholder: x.y.z
|
||||
- type: textarea
|
||||
validations:
|
||||
required: true
|
||||
attributes:
|
||||
label: Summary
|
||||
value: '<!-- Describe your issue, question or request here. -->'
|
||||
|
||||
7
.github/ISSUE_TEMPLATE.md
vendored
7
.github/ISSUE_TEMPLATE.md
vendored
@ -1,7 +0,0 @@
|
||||
<!--
|
||||
Before opening an issue, just take into account that this is a completely free of charge and open source project.
|
||||
I'm always happy to help and provide support, but some understanding will be expected.
|
||||
I do this in my own free time, so expect some delays when implementing new features and fixing bugs, and don't take it personally if an issue gets eventually closed.
|
||||
You may also be asked to provide tests or ways to reproduce reported bugs.
|
||||
Try to be polite, and understand it is impossible for an OSS project to cover all use cases.
|
||||
-->
|
||||
10
.github/ISSUE_TEMPLATE/Bug.yml
vendored
10
.github/ISSUE_TEMPLATE/Bug.yml
vendored
@ -61,7 +61,11 @@ body:
|
||||
label: Minimum steps to reproduce
|
||||
value: |
|
||||
<!--
|
||||
Emphasis in MINIMUM: What is the simplest way to reproduce the bug?
|
||||
Avoid things like "Create a kubernetes cluster", or anything related with cloud providers, as that is rarely the root cause and the bug may be closed as "not reproducible".
|
||||
If you can provide a simple docker compose config, that's even better.
|
||||
Simple but detailed way to reproduce the bug:
|
||||
|
||||
* Avoid things like "create a kubernetes cluster", or anything related with cloud providers, as that is rarely the root cause.
|
||||
* Avoid too vague steps or one-liners like "Update from v1 to v2".
|
||||
* Providing the reproduction in the form of a self-contained docker-composer is desirable.
|
||||
|
||||
Failing in any of these will cause the issue to be closed as "not reproducible".
|
||||
-->
|
||||
|
||||
2
.github/ISSUE_TEMPLATE/config.yml
vendored
2
.github/ISSUE_TEMPLATE/config.yml
vendored
@ -2,4 +2,4 @@ blank_issues_enabled: true
|
||||
contact_links:
|
||||
- name: Question - Support
|
||||
about: Do you need help setting up or using Shlink?
|
||||
url: https://github.com/shlinkio/shlink/discussions/new?category=help-wanted
|
||||
url: https://github.com/orgs/shlinkio/discussions/new?category=help-wanted
|
||||
|
||||
3
.github/actions/ci-setup/action.yml
vendored
3
.github/actions/ci-setup/action.yml
vendored
@ -40,8 +40,7 @@ runs:
|
||||
php-version: ${{ inputs.php-version }}
|
||||
tools: composer
|
||||
extensions: ${{ inputs.php-extensions }}
|
||||
coverage: pcov
|
||||
ini-values: pcov.directory=module
|
||||
coverage: xdebug
|
||||
- name: Install dependencies
|
||||
if: ${{ inputs.install-deps == 'yes' }}
|
||||
run: composer install --no-interaction --prefer-dist
|
||||
|
||||
12
.github/workflows/ci-db-tests.yml
vendored
12
.github/workflows/ci-db-tests.yml
vendored
@ -10,14 +10,14 @@ on:
|
||||
|
||||
jobs:
|
||||
db-tests:
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-24.04
|
||||
strategy:
|
||||
matrix:
|
||||
php-version: ['8.2', '8.3']
|
||||
php-version: ['8.4', '8.5']
|
||||
env:
|
||||
LC_ALL: C
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
- name: Install MSSQL ODBC
|
||||
if: ${{ inputs.platform == 'ms' }}
|
||||
run: sudo ./data/infra/ci/install-ms-odbc.sh
|
||||
@ -31,12 +31,12 @@ jobs:
|
||||
extensions-cache-key: db-tests-extensions-${{ matrix.php-version }}-${{ inputs.platform }}
|
||||
- name: Create test database
|
||||
if: ${{ inputs.platform == 'ms' }}
|
||||
run: docker compose exec -T shlink_db_ms /opt/mssql-tools/bin/sqlcmd -S localhost -U sa -P 'Passw0rd!' -Q "CREATE DATABASE shlink_test;"
|
||||
run: docker compose exec -T shlink_db_ms /opt/mssql-tools18/bin/sqlcmd -C -S localhost -U sa -P 'Passw0rd!' -Q "CREATE DATABASE shlink_test;"
|
||||
- name: Run tests
|
||||
run: composer test:db:${{ inputs.platform }}
|
||||
- name: Upload code coverage
|
||||
uses: actions/upload-artifact@v4
|
||||
if: ${{ matrix.php-version == '8.2' && inputs.platform == 'sqlite:ci' }}
|
||||
uses: actions/upload-artifact@v5
|
||||
if: ${{ matrix.php-version == '8.4' && inputs.platform == 'sqlite:ci' }}
|
||||
with:
|
||||
name: coverage-db
|
||||
path: |
|
||||
|
||||
10
.github/workflows/ci-docker-image-build.yml
vendored
10
.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,6 @@ on:
|
||||
|
||||
jobs:
|
||||
build-docker-image:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
- run: docker build -t shlink-docker-image:temp .
|
||||
uses: shlinkio/github-actions/.github/workflows/docker-image-build-ci.yml@main
|
||||
with:
|
||||
platforms: 'linux/arm64/v8,linux/amd64'
|
||||
|
||||
10
.github/workflows/ci-tests.yml
vendored
10
.github/workflows/ci-tests.yml
vendored
@ -10,14 +10,14 @@ on:
|
||||
|
||||
jobs:
|
||||
tests:
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-24.04
|
||||
strategy:
|
||||
matrix:
|
||||
php-version: ['8.2', '8.3']
|
||||
php-version: ['8.4', '8.5']
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # rr get-binary picks this env automatically
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
- name: Start postgres database server
|
||||
if: ${{ inputs.test-group == 'api' }}
|
||||
run: docker compose -f docker-compose.yml -f docker-compose.ci.yml up -d shlink_db_postgres
|
||||
@ -32,8 +32,8 @@ jobs:
|
||||
if: ${{ inputs.test-group == 'api' }}
|
||||
run: ./vendor/bin/rr get --no-interaction --no-config --location bin/ && chmod +x bin/rr
|
||||
- run: composer test:${{ inputs.test-group }}:ci
|
||||
- uses: actions/upload-artifact@v4
|
||||
if: ${{ matrix.php-version == '8.2' }}
|
||||
- uses: actions/upload-artifact@v5
|
||||
if: ${{ matrix.php-version == '8.4' }}
|
||||
with:
|
||||
name: coverage-${{ inputs.test-group }}
|
||||
path: |
|
||||
|
||||
25
.github/workflows/ci.yml
vendored
25
.github/workflows/ci.yml
vendored
@ -24,13 +24,13 @@ on:
|
||||
|
||||
jobs:
|
||||
static-analysis:
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-24.04
|
||||
strategy:
|
||||
matrix:
|
||||
php-version: ['8.2']
|
||||
command: ['cs', 'stan', 'swagger:validate']
|
||||
php-version: ['8.4']
|
||||
command: ['cs', 'stan', 'openapi:validate']
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
- uses: './.github/actions/ci-setup'
|
||||
with:
|
||||
php-version: ${{ matrix.php-version }}
|
||||
@ -66,19 +66,18 @@ jobs:
|
||||
- api-tests
|
||||
- cli-tests
|
||||
- db-tests
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-24.04
|
||||
strategy:
|
||||
matrix:
|
||||
php-version: ['8.2']
|
||||
php-version: ['8.4']
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
- name: Use PHP
|
||||
uses: './.github/actions/ci-setup'
|
||||
with:
|
||||
php-version: ${{ matrix.php-version }}
|
||||
extensions-cache-key: tests-extensions-${{ matrix.php-version }}
|
||||
- uses: actions/download-artifact@v4
|
||||
- uses: actions/download-artifact@v6
|
||||
with:
|
||||
path: build
|
||||
- run: mv build/coverage-unit/coverage-unit.cov build/coverage-unit.cov
|
||||
@ -87,16 +86,16 @@ jobs:
|
||||
- run: mv build/coverage-cli/coverage-cli.cov build/coverage-cli.cov
|
||||
- run: vendor/bin/phpcov merge build --clover build/clover.xml
|
||||
- name: Publish coverage
|
||||
uses: codecov/codecov-action@v4
|
||||
uses: codecov/codecov-action@v5
|
||||
with:
|
||||
file: ./build/clover.xml
|
||||
files: ./build/clover.xml
|
||||
|
||||
delete-artifacts:
|
||||
needs:
|
||||
- upload-coverage
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: geekyeggo/delete-artifact@v2
|
||||
- uses: geekyeggo/delete-artifact@v5
|
||||
with:
|
||||
name: |
|
||||
coverage-*
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
name: Publish swagger spec
|
||||
name: Publish openapi spec
|
||||
|
||||
on:
|
||||
push:
|
||||
@ -7,12 +7,12 @@ on:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-24.04
|
||||
strategy:
|
||||
matrix:
|
||||
php-version: ['8.2']
|
||||
php-version: ['8.4']
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
- name: Determine version
|
||||
id: determine_version
|
||||
run: echo "version=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT
|
||||
@ -20,10 +20,10 @@ jobs:
|
||||
- uses: './.github/actions/ci-setup'
|
||||
with:
|
||||
php-version: ${{ matrix.php-version }}
|
||||
extensions-cache-key: publish-swagger-spec-extensions-${{ matrix.php-version }}
|
||||
- run: composer swagger:inline
|
||||
extensions-cache-key: publish-openapi-spec-extensions-${{ matrix.php-version }}
|
||||
- run: composer openapi:inline
|
||||
- run: mkdir ${{ steps.determine_version.outputs.version }}
|
||||
- run: mv docs/swagger/swagger-inlined.json ${{ steps.determine_version.outputs.version }}/open-api-spec.json
|
||||
- run: mv docs/swagger/openapi-inlined.json ${{ steps.determine_version.outputs.version }}/open-api-spec.json
|
||||
- name: Publish spec
|
||||
uses: JamesIves/github-pages-deploy-action@v4
|
||||
with:
|
||||
18
.github/workflows/publish-release.yml
vendored
18
.github/workflows/publish-release.yml
vendored
@ -7,29 +7,29 @@ on:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-24.04
|
||||
strategy:
|
||||
matrix:
|
||||
php-version: ['8.2', '8.3']
|
||||
php-version: ['8.4', '8.4', '8.5']
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
- uses: './.github/actions/ci-setup'
|
||||
with:
|
||||
php-version: ${{ matrix.php-version }}
|
||||
extensions-cache-key: publish-swagger-spec-extensions-${{ matrix.php-version }}
|
||||
install-deps: 'no'
|
||||
- run: ./build.sh ${GITHUB_REF#refs/tags/v}
|
||||
- uses: actions/upload-artifact@v4
|
||||
- uses: actions/upload-artifact@v5
|
||||
with:
|
||||
name: dist-files-${{ matrix.php-version }}
|
||||
path: build
|
||||
|
||||
publish:
|
||||
needs: ['build']
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/download-artifact@v4
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/download-artifact@v6
|
||||
with:
|
||||
path: build
|
||||
- name: Publish release with assets
|
||||
@ -43,8 +43,8 @@ jobs:
|
||||
|
||||
delete-artifacts:
|
||||
needs: ['publish']
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: geekyeggo/delete-artifact@v2
|
||||
- uses: geekyeggo/delete-artifact@v5
|
||||
with:
|
||||
name: dist-files-*
|
||||
|
||||
5
.gitignore
vendored
5
.gitignore
vendored
@ -10,9 +10,6 @@ data/database.sqlite
|
||||
data/shlink-tests.db
|
||||
data/GeoLite2-City.*
|
||||
data/infra/matomo
|
||||
docs/swagger-ui*
|
||||
docs/mercure.html
|
||||
docker-compose.override.yml
|
||||
.phpunit.result.cache
|
||||
docs/swagger/swagger-inlined.json
|
||||
phpcov*
|
||||
docs/swagger/openapi-inlined.json
|
||||
|
||||
488
CHANGELOG.md
488
CHANGELOG.md
@ -4,6 +4,494 @@ 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).
|
||||
|
||||
## [Unreleased]
|
||||
### Added
|
||||
* *Nothing*
|
||||
|
||||
### Changed
|
||||
* [#2522](https://github.com/shlinkio/shlink/issues/2522) Shlink no longer tries to detect trusted proxies automatically, when resolving the visitor's IP address, as this is a potential security issue.
|
||||
|
||||
Instead, if you have more than 1 proxy in front of Shlink, you should provide `TRUSTED_PROXIES` env var, with either a comma-separated list of the IP addresses of your proxies, or a number indicating how many proxies are there in front of Shlink.
|
||||
|
||||
* [#2540](https://github.com/shlinkio/shlink/issues/2540) Update Symfony packages to 8.0.
|
||||
|
||||
### Deprecated
|
||||
* *Nothing*
|
||||
|
||||
### Removed
|
||||
* [#2507](https://github.com/shlinkio/shlink/issues/2507) Drop support for PHP 8.3.
|
||||
* [#2514](https://github.com/shlinkio/shlink/issues/2514) Remove support to generate QR codes. This functionality is now handled by Shlink Web Client and Shlink Dashboard.
|
||||
* [#2517](https://github.com/shlinkio/shlink/issues/2517) Remove `REDIRECT_APPEND_EXTRA_PATH` env var. Use `REDIRECT_EXTRA_PATH_MODE=append` instead.
|
||||
* [#2519](https://github.com/shlinkio/shlink/issues/2519) Disabling API keys by their plain-text key is no longer supported. When calling `api-key:disable`, the first argument is now always assumed to be the name.
|
||||
* [#2520](https://github.com/shlinkio/shlink/issues/2520) Remove deprecated `--including-all-tags` and `--show-api-key-name` deprecated options from `short-url:list` command. Use `--tags-all` and `--show-api-key` instead.
|
||||
* [#2521](https://github.com/shlinkio/shlink/issues/2521) Remove deprecated `--tags` option in all commands using it. Use `--tag` multiple times instead, one per tag.
|
||||
|
||||
### Fixed
|
||||
* *Nothing*
|
||||
|
||||
|
||||
## [4.6.0] - 2025-11-01
|
||||
### Added
|
||||
* [#2327](https://github.com/shlinkio/shlink/issues/2327) Allow filtering short URL lists by those not including certain tags.
|
||||
|
||||
Now, the `GET /short-urls` endpoint accepts two new params: `excludeTags`, which is an array of strings with the tags that should not be included, and `excludeTagsMode`, which accepts the values `any` and `all`, and determines if short URLs should be filtered out if they contain any of the excluded tags, or all the excluded tags.
|
||||
|
||||
Additionally, the `short-url:list` command also supports the same feature via `--exclude-tag` option, which requires a value and can be provided multiple times, and `--exclude-tags-all`, which does not expect a value and determines if the mode should be `all`, or `any`.
|
||||
|
||||
* [#2192](https://github.com/shlinkio/shlink/issues/2192) Allow filtering short URL lists by the API key that was used to create them.
|
||||
|
||||
Now, the `GET /short-urls` endpoint accepts a new `apiKeyName` param, which is ignored if the request is performed with a non-admin API key which name does not match the one provided here.
|
||||
|
||||
Additionally, the `short-url:list` command also supports the same feature via the `--api-key-name` option.
|
||||
|
||||
* [#2330](https://github.com/shlinkio/shlink/issues/2330) Add support to serve Shlink with FrankenPHP, by providing a worker script in `bin/frankenphp-worker.php`.
|
||||
|
||||
* [#2449](https://github.com/shlinkio/shlink/issues/2449) Add support to provide redis credentials separately when using redis sentinels, where provided servers are the sentinels and not the redis instances.
|
||||
|
||||
For this, Shlink supports two new env ras / config options, as `REDIS_SERVERS_USER` and `REDIS_SERVERS_PASSWORD`.
|
||||
|
||||
* [#2498](https://github.com/shlinkio/shlink/issues/2498) Allow orphan visits, non-orphan visits and tag visits lists to be filtered by domain.
|
||||
|
||||
This is done via the `domain` query parameter in API endpoints, and via the `--domain` option in console commands.
|
||||
|
||||
* [#2472](https://github.com/shlinkio/shlink/issues/2472) Add support for PHP 8.5
|
||||
* [#2291](https://github.com/shlinkio/shlink/issues/2291) Add `api-key:delete` console command to delete API keys.
|
||||
|
||||
### Changed
|
||||
* [#2424](https://github.com/shlinkio/shlink/issues/2424) Make simple console commands invokable.
|
||||
|
||||
### Deprecated
|
||||
* *Nothing*
|
||||
|
||||
### Removed
|
||||
* *Nothing*
|
||||
|
||||
### Fixed
|
||||
* *Nothing*
|
||||
|
||||
|
||||
## [4.5.3] - 2025-10-10
|
||||
### Added
|
||||
* *Nothing*
|
||||
|
||||
### Changed
|
||||
* *Nothing*
|
||||
|
||||
### Deprecated
|
||||
* *Nothing*
|
||||
|
||||
### Removed
|
||||
* *Nothing*
|
||||
|
||||
### Fixed
|
||||
* [#2488](https://github.com/shlinkio/shlink/issues/2488) Ensure `Access-Control-Allow-Credentials` is set in all cross-origin responses when `CORS_ALLOW_ORIGIN=true`.
|
||||
|
||||
|
||||
## [4.5.2] - 2025-08-27
|
||||
### Added
|
||||
* *Nothing*
|
||||
|
||||
### Changed
|
||||
* *Nothing*
|
||||
|
||||
### Deprecated
|
||||
* *Nothing*
|
||||
|
||||
### Removed
|
||||
* *Nothing*
|
||||
|
||||
### Fixed
|
||||
* [#2433](https://github.com/shlinkio/shlink/issues/2433) Try to mitigate memory leaks allowing RoadRunner to garbage collect memory after every request and every job, by setting `GC_COLLECT_CYCLES=true`.
|
||||
|
||||
|
||||
## [4.5.1] - 2025-08-24
|
||||
### Added
|
||||
* *Nothing*
|
||||
|
||||
### Changed
|
||||
* *Nothing*
|
||||
|
||||
### Deprecated
|
||||
* *Nothing*
|
||||
|
||||
### Removed
|
||||
* *Nothing*
|
||||
|
||||
### Fixed
|
||||
* [#2433](https://github.com/shlinkio/shlink/issues/2433) Try to mitigate memory leaks by restarting job and http workers every 250 executions when using RoadRunner.
|
||||
|
||||
|
||||
## [4.5.0] - 2025-07-24
|
||||
### Added
|
||||
* [#2438](https://github.com/shlinkio/shlink/issues/2438) Add `MERCURE_ENABLED` env var and corresponding config option, to more easily allow the mercure integration to be toggled.
|
||||
|
||||
For BC, if this env var is not present, we'll still consider the integration enabled if the `MERCURE_PUBLIC_HUB_URL` env var has a value. This is considered deprecated though, and next major version will rely only on `MERCURE_ENABLED`, so if you are using Mercure, make sure to set `MERCURE_ENABLED=true` to be ready.
|
||||
|
||||
* [#2387](https://github.com/shlinkio/shlink/issues/2387) Add `REAL_TIME_UPDATES_TOPICS` env var and corresponding config option, to granularly decide which real-time updates topics should be enabled.
|
||||
* [#2418](https://github.com/shlinkio/shlink/issues/2418) Add more granular control over how Shlink handles CORS. It is now possible to customize the `Access-Control-Allow-Origin`, `Access-Control-Max-Age` and `Access-Control-Allow-Credentials` headers via env vars or config options.
|
||||
* [#2386](https://github.com/shlinkio/shlink/issues/2386) Add new `any-value-query-param` and `valueless-query-param` redirect rule conditions.
|
||||
|
||||
These new rules expand the existing `query-param`, which requires both a specific non-empty value in order to match the condition.
|
||||
|
||||
The new conditions match as soon as a query param exists with any or no value (in the case of `any-value-query-param`), or if a query param exists with no value at all (in the case of `valueless-query-param`).
|
||||
|
||||
* [#2360](https://github.com/shlinkio/shlink/issues/2360) Add `TRUSTED_PROXIES` env var and corresponding config option, to configure a comma-separated list of all the proxies in front of Shlink, or simply the amount of trusted proxies in front of Shlink.
|
||||
|
||||
This is important to properly detect visitor's IP addresses instead of incorrectly matching one of the proxy's IP address, and if provided, it disables a workaround introduced in https://github.com/shlinkio/shlink/pull/2359.
|
||||
|
||||
* [#2274](https://github.com/shlinkio/shlink/issues/2274) Add more supported device types for the `device` redirect condition:
|
||||
|
||||
* `linux`: Will match desktop devices with Linux.
|
||||
* `windows`: Will match desktop devices with Windows.
|
||||
* `macos`: Will match desktop devices with MacOS.
|
||||
* `chromeos`: Will match desktop devices with ChromeOS.
|
||||
* `mobile`: Will match any mobile devices with either Android or iOS.
|
||||
|
||||
* [#2093](https://github.com/shlinkio/shlink/issues/2093) Add `REDIRECT_CACHE_LIFETIME` env var and corresponding config option, so that it is possible to set the `Cache-Control` visibility directive (`public` or `private`) when the `REDIRECT_STATUS_CODE` has been set to `301` or `308`.
|
||||
* [#2323](https://github.com/shlinkio/shlink/issues/2323) Add `LOGS_FORMAT` env var and corresponding config option, to allow the logs generated by Shlink to be in console or JSON formats.
|
||||
|
||||
### Changed
|
||||
* [#2406](https://github.com/shlinkio/shlink/issues/2406) Remove references to bootstrap from error templates, and instead inline the very minimum required styles.
|
||||
|
||||
### Deprecated
|
||||
* [#2408](https://github.com/shlinkio/shlink/issues/2408) Generating QR codes via `/{short-code}/qr-code` is now deprecated and will be removed in Shlink 5.0. Use the equivalent capability from web clients instead.
|
||||
|
||||
### Removed
|
||||
* *Nothing*
|
||||
|
||||
### Fixed
|
||||
* *Nothing*
|
||||
|
||||
|
||||
## [4.4.6] - 2025-03-20
|
||||
### Added
|
||||
* *Nothing*
|
||||
|
||||
### Changed
|
||||
* *Nothing*
|
||||
|
||||
### Deprecated
|
||||
* *Nothing*
|
||||
|
||||
### Removed
|
||||
* *Nothing*
|
||||
|
||||
### Fixed
|
||||
* [#2391](https://github.com/shlinkio/shlink/issues/2391) When sending visits to Matomo, send the country code, not the country name.
|
||||
* Fix error with new option introduced by `endroid/qr-code` 6.0.4.
|
||||
|
||||
|
||||
## [4.4.5] - 2025-03-01
|
||||
### Added
|
||||
* *Nothing*
|
||||
|
||||
### Changed
|
||||
* *Nothing*
|
||||
|
||||
### Deprecated
|
||||
* *Nothing*
|
||||
|
||||
### Removed
|
||||
* *Nothing*
|
||||
|
||||
### Fixed
|
||||
* [#2373](https://github.com/shlinkio/shlink/issues/2373) Ensure deprecation warnings do not end up escalated to `ErrorException`s by `ProblemDetailsMiddleware`.
|
||||
|
||||
In order to do this, Shlink will entirely ignore deprecation warnings when running in production, as those do not mean something is not working, but only that something will break in future versions.
|
||||
|
||||
|
||||
## [4.4.4] - 2025-02-19
|
||||
### Added
|
||||
* *Nothing*
|
||||
|
||||
### Changed
|
||||
* *Nothing*
|
||||
|
||||
### Deprecated
|
||||
* *Nothing*
|
||||
|
||||
### Removed
|
||||
* *Nothing*
|
||||
|
||||
### Fixed
|
||||
* [#2366](https://github.com/shlinkio/shlink/issues/2366) Fix error "Cannot use 'SCRIPT' with redis-cluster" thrown when creating a lock while using a redis cluster.
|
||||
* [#2368](https://github.com/shlinkio/shlink/issues/2368) Fix error when listing non-orphan visits using API key with `AUTHORED_SHORT_URLS` role.
|
||||
|
||||
|
||||
## [4.4.3] - 2025-02-15
|
||||
### Added
|
||||
* *Nothing*
|
||||
|
||||
### Changed
|
||||
* *Nothing*
|
||||
|
||||
### Deprecated
|
||||
* *Nothing*
|
||||
|
||||
### Removed
|
||||
* *Nothing*
|
||||
|
||||
### Fixed
|
||||
* [#2351](https://github.com/shlinkio/shlink/issues/2351) Fix visitor IP address resolution when Shlink is served behind more than one reverse proxy.
|
||||
|
||||
This regression was introduced due to a change in behavior in `akrabat/rka-ip-address-middleware`, that now picks the first address from the right after excluding all trusted proxies.
|
||||
|
||||
Since Shlink does not set trusted proxies, this means the first IP from the right is now picked instead of the first from the left, so we now reverse the list before trying to resolve the IP.
|
||||
|
||||
In the future, Shlink will allow you to define trusted proxies, to avoid other potential side effects because of this reversing of the list.
|
||||
|
||||
* [#2354](https://github.com/shlinkio/shlink/issues/2354) Fix error "NOSCRIPT No matching script. Please use EVAL" thrown when creating a lock in redis.
|
||||
* [#2319](https://github.com/shlinkio/shlink/issues/2319) Fix unique index for `short_code` and `domain_id` in `short_urls` table not being used in Microsoft SQL engines for rows where `domain_id` is `null`.
|
||||
|
||||
## [4.4.2] - 2025-01-29
|
||||
### Added
|
||||
* *Nothing*
|
||||
|
||||
### Changed
|
||||
* *Nothing*
|
||||
|
||||
### Deprecated
|
||||
* *Nothing*
|
||||
|
||||
### Removed
|
||||
* *Nothing*
|
||||
|
||||
### Fixed
|
||||
* [#2346](https://github.com/shlinkio/shlink/issues/2346) Get back docker images for ARM architectures.
|
||||
|
||||
|
||||
## [4.4.1] - 2025-01-28
|
||||
### Added
|
||||
* [#2331](https://github.com/shlinkio/shlink/issues/2331) Add `ADDRESS` env var which allows to customize the IP address to which RoadRunner binds, when using the official docker image.
|
||||
|
||||
### Changed
|
||||
* *Nothing*
|
||||
|
||||
### Deprecated
|
||||
* *Nothing*
|
||||
|
||||
### Removed
|
||||
* *Nothing*
|
||||
|
||||
### Fixed
|
||||
* [#2341](https://github.com/shlinkio/shlink/issues/2341) Ensure all asynchronous jobs that interact with the database do not leave idle connections open.
|
||||
* [#2334](https://github.com/shlinkio/shlink/issues/2334) Improve how page titles are encoded to UTF-8, falling back from mbstring to iconv if available, and ultimately using the original title in case of error, but never causing the short URL creation to fail.
|
||||
|
||||
|
||||
## [4.4.0] - 2024-12-27
|
||||
### Added
|
||||
* [#2265](https://github.com/shlinkio/shlink/issues/2265) Add a new `REDIRECT_EXTRA_PATH_MODE` option that accepts three values:
|
||||
|
||||
* `default`: Short URLs only match if the path matches their short code or custom slug.
|
||||
* `append`: Short URLs are matched as soon as the path starts with the short code or custom slug, and the extra path is appended to the long URL before redirecting.
|
||||
* `ignore`: Short URLs are matched as soon as the path starts with the short code or custom slug, and the extra path is ignored.
|
||||
|
||||
This option effectively replaces the old `REDIRECT_APPEND_EXTRA_PATH` option, which is now deprecated and will be removed in Shlink 5.0.0
|
||||
|
||||
* [#2156](https://github.com/shlinkio/shlink/issues/2156) Be less restrictive on what characters are disallowed in custom slugs.
|
||||
|
||||
All [URI-reserved characters](https://datatracker.ietf.org/doc/html/rfc3986#section-2.2) were disallowed up until now, but from now on, only the gen-delimiters are.
|
||||
|
||||
* [#2229](https://github.com/shlinkio/shlink/issues/2229) Add `logo=disabled` query param to dynamically disable the default logo on QR codes.
|
||||
* [#2206](https://github.com/shlinkio/shlink/issues/2206) Add new `DB_USE_ENCRYPTION` config option to enable SSL database connections trusting any server certificate.
|
||||
* [#2209](https://github.com/shlinkio/shlink/issues/2209) Redirect rules are now imported when importing short URLs from a Shlink >=4.0 instance.
|
||||
|
||||
### Changed
|
||||
* [#2281](https://github.com/shlinkio/shlink/issues/2281) Update docker image to PHP 8.4
|
||||
* [#2124](https://github.com/shlinkio/shlink/issues/2124) Improve how Shlink decides if a GeoLite db file needs to be downloaded, and reduces the chances for API limits to be reached.
|
||||
|
||||
Now Shlink tracks all download attempts, and knows which of them failed and succeeded. This lets it know when was the last error or success, how many consecutive errors have happened, etc.
|
||||
|
||||
It also tracks now the reason for a download to be attempted, and the error that happened when one fails.
|
||||
|
||||
### Deprecated
|
||||
* *Nothing*
|
||||
|
||||
### Removed
|
||||
* [#2247](https://github.com/shlinkio/shlink/issues/2247) Drop support for PHP 8.2
|
||||
|
||||
### Fixed
|
||||
* *Nothing*
|
||||
|
||||
|
||||
## [4.3.1] - 2024-11-25
|
||||
### Added
|
||||
* *Nothing*
|
||||
|
||||
### Changed
|
||||
* *Nothing*
|
||||
|
||||
### Deprecated
|
||||
* *Nothing*
|
||||
|
||||
### Removed
|
||||
* *Nothing*
|
||||
|
||||
### Fixed
|
||||
* [#2285](https://github.com/shlinkio/shlink/issues/2285) Fix performance degradation when using Microsoft SQL due to incorrect order of columns in `unique_short_code_plus_domain` index.
|
||||
|
||||
|
||||
## [4.3.0] - 2024-11-24
|
||||
### Added
|
||||
* [#2159](https://github.com/shlinkio/shlink/issues/2159) Add support for PHP 8.4.
|
||||
* [#2207](https://github.com/shlinkio/shlink/issues/2207) Add `hasRedirectRules` flag to short URL API model. This flag tells if a specific short URL has any redirect rules attached to it.
|
||||
* [#1520](https://github.com/shlinkio/shlink/issues/1520) Allow short URLs list to be filtered by `domain`.
|
||||
|
||||
This change applies both to the `GET /short-urls` endpoint, via the `domain` query parameter, and the `short-url:list` console command, via the `--domain`|`-d` flag.
|
||||
|
||||
* [#1774](https://github.com/shlinkio/shlink/issues/1774) Add new geolocation redirect rules for the dynamic redirects system.
|
||||
|
||||
* `geolocation-country-code`: Allows to perform redirections based on the ISO 3166-1 alpha-2 two-letter country code resolved while geolocating the visitor.
|
||||
* `geolocation-city-name`: Allows to perform redirections based on the city name resolved while geolocating the visitor.
|
||||
|
||||
* [#2032](https://github.com/shlinkio/shlink/issues/2032) Save the URL to which a visitor is redirected when a visit is tracked.
|
||||
|
||||
The value is exposed in the API as a new `redirectUrl` field for visit objects.
|
||||
|
||||
This is useful to know where a visitor was redirected for a short URL with dynamic redirect rules, for special redirects, or simply in case the long URL was changed over time, and you still want to know where visitors were redirected originally.
|
||||
|
||||
Some visits may not have a redirect URL if a redirect didn't happen, like for orphan visits when no special redirects are configured, or when a visit is tracked as part of the pixel action.
|
||||
|
||||
### Changed
|
||||
* [#2193](https://github.com/shlinkio/shlink/issues/2193) API keys are now hashed using SHA256, instead of being saved in plain text.
|
||||
|
||||
As a side effect, API key names have now become more important, and are considered unique.
|
||||
|
||||
When people update to this Shlink version, existing API keys will be hashed for everything to continue working.
|
||||
|
||||
In order to avoid data to be lost, plain-text keys will be written in the `name` field, either together with any existing name, or as the name itself. Then users are responsible for renaming them using the new `api-key:rename` command.
|
||||
|
||||
For newly created API keys, it is recommended to provide a name, but if not provided, a name will be generated from a redacted version of the new API key.
|
||||
|
||||
* Update to Shlink PHP coding standard 2.4
|
||||
* Update to `hidehalo/nanoid-php` 2.0
|
||||
* Update to PHPStan 2.0
|
||||
|
||||
### Deprecated
|
||||
* *Nothing*
|
||||
|
||||
### Removed
|
||||
* *Nothing*
|
||||
|
||||
### Fixed
|
||||
* [#2264](https://github.com/shlinkio/shlink/issues/2264) Fix visits counts not being deleted when deleting short URL or orphan visits.
|
||||
|
||||
|
||||
## [4.2.5] - 2024-11-03
|
||||
### Added
|
||||
* *Nothing*
|
||||
|
||||
### Changed
|
||||
* Update to Shlink PHP coding standard 2.4
|
||||
|
||||
### Deprecated
|
||||
* *Nothing*
|
||||
|
||||
### Removed
|
||||
* *Nothing*
|
||||
|
||||
### Fixed
|
||||
* [#2244](https://github.com/shlinkio/shlink/issues/2244) Fix integration with Redis 7.4 and Valkey.
|
||||
|
||||
|
||||
## [4.2.4] - 2024-10-27
|
||||
### Added
|
||||
* *Nothing*
|
||||
|
||||
### Changed
|
||||
* [#2231](https://github.com/shlinkio/shlink/issues/2231) Update to `endroid/qr-code` 6.0.
|
||||
* [#2221](https://github.com/shlinkio/shlink/issues/2221) Switch to env vars to handle dev/local options.
|
||||
|
||||
### Deprecated
|
||||
* *Nothing*
|
||||
|
||||
### Removed
|
||||
* *Nothing*
|
||||
|
||||
### Fixed
|
||||
* [#2232](https://github.com/shlinkio/shlink/issues/2232) Run RoadRunner in docker with `exec` to ensure signals are properly handled.
|
||||
|
||||
|
||||
## [4.2.3] - 2024-10-17
|
||||
### Added
|
||||
* *Nothing*
|
||||
|
||||
### Changed
|
||||
* *Nothing*
|
||||
|
||||
### Deprecated
|
||||
* *Nothing*
|
||||
|
||||
### Removed
|
||||
* *Nothing*
|
||||
|
||||
### Fixed
|
||||
* [#2225](https://github.com/shlinkio/shlink/issues/2225) Fix regression introduced in v4.2.2, making config options with `null` value to be promoted as env vars with value `''`, instead of being skipped.
|
||||
|
||||
|
||||
## [4.2.2] - 2024-10-14
|
||||
### Added
|
||||
* *Nothing*
|
||||
|
||||
### Changed
|
||||
* [#2208](https://github.com/shlinkio/shlink/issues/2208) Explicitly promote installer config options as env vars, instead of as a side effect of loading the app config.
|
||||
|
||||
### Deprecated
|
||||
* *Nothing*
|
||||
|
||||
### Removed
|
||||
* *Nothing*
|
||||
|
||||
### Fixed
|
||||
* [#2213](https://github.com/shlinkio/shlink/issues/2213) Fix spaces being replaced with underscores in query parameter names, when forwarded from short URL to long URL.
|
||||
* [#2217](https://github.com/shlinkio/shlink/issues/2217) Fix docker image tag suffix being leaked to the version set inside Shlink, producing invalid SemVer version patterns.
|
||||
* [#2212](https://github.com/shlinkio/shlink/issues/2212) Fix env vars read in docker entry point not properly falling back to their `_FILE` suffixed counterpart.
|
||||
|
||||
|
||||
## [4.2.1] - 2024-10-04
|
||||
### Added
|
||||
* [#2183](https://github.com/shlinkio/shlink/issues/2183) Redis database index to be used can now be specified in the connection URI path, and Shlink will honor it.
|
||||
|
||||
### Changed
|
||||
* *Nothing*
|
||||
|
||||
### Deprecated
|
||||
* *Nothing*
|
||||
|
||||
### Removed
|
||||
* *Nothing*
|
||||
|
||||
### Fixed
|
||||
* [#2201](https://github.com/shlinkio/shlink/issues/2201) Fix `MEMORY_LIMIT` option being ignored when provided via installer options.
|
||||
|
||||
|
||||
## [4.2.0] - 2024-08-11
|
||||
### Added
|
||||
* [#2120](https://github.com/shlinkio/shlink/issues/2120) Add new IP address condition for the dynamic rules redirections system.
|
||||
|
||||
The conditions allow you to define IP addresses to match as static IP (1.2.3.4), CIDR block (192.168.1.0/24) or wildcard pattern (1.2.\*.\*).
|
||||
|
||||
* [#2018](https://github.com/shlinkio/shlink/issues/2018) Add option to allow all short URLs to be unconditionally crawlable in robots.txt, via `ROBOTS_ALLOW_ALL_SHORT_URLS=true` env var, or config option.
|
||||
* [#2109](https://github.com/shlinkio/shlink/issues/2109) Add option to customize user agents robots.txt, via `ROBOTS_USER_AGENTS=foo,bar,baz` env var, or config option.
|
||||
* [#2163](https://github.com/shlinkio/shlink/issues/2163) Add `short-urls:edit` command to edit existing short URLs.
|
||||
|
||||
This brings CLI and API interfaces capabilities closer, and solves an overlook since the feature was implemented years ago.
|
||||
|
||||
* [#2164](https://github.com/shlinkio/shlink/pull/2164) Add missing `--title` option to `short-url:create` and `short-url:edit` commands.
|
||||
|
||||
### Changed
|
||||
* [#2096](https://github.com/shlinkio/shlink/issues/2096) Update to RoadRunner 2024.
|
||||
|
||||
### Deprecated
|
||||
* *Nothing*
|
||||
|
||||
### Removed
|
||||
* *Nothing*
|
||||
|
||||
### Fixed
|
||||
* *Nothing*
|
||||
|
||||
|
||||
## [4.1.1] - 2024-05-23
|
||||
### Added
|
||||
* *Nothing*
|
||||
|
||||
@ -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.
|
||||
|
||||
25
Dockerfile
25
Dockerfile
@ -1,23 +1,23 @@
|
||||
FROM php:8.3-alpine3.19 as base
|
||||
FROM php:8.4-alpine3.21 AS base
|
||||
|
||||
ARG SHLINK_VERSION=latest
|
||||
ENV SHLINK_VERSION ${SHLINK_VERSION}
|
||||
ENV SHLINK_VERSION=${SHLINK_VERSION}
|
||||
ARG SHLINK_RUNTIME=rr
|
||||
ENV SHLINK_RUNTIME ${SHLINK_RUNTIME}
|
||||
ENV SHLINK_RUNTIME=${SHLINK_RUNTIME}
|
||||
|
||||
ENV USER_ID '1001'
|
||||
ENV PDO_SQLSRV_VERSION 5.12.0
|
||||
ENV MS_ODBC_DOWNLOAD 'b/9/f/b9f3cce4-3925-46d4-9f46-da08869c6486'
|
||||
ENV MS_ODBC_SQL_VERSION 18_18.1.1.1
|
||||
ENV LC_ALL 'C'
|
||||
ENV USER_ID='1001'
|
||||
ENV PDO_SQLSRV_VERSION='5.12.0'
|
||||
ENV MS_ODBC_DOWNLOAD='7/6/d/76de322a-d860-4894-9945-f0cc5d6a45f8'
|
||||
ENV MS_ODBC_SQL_VERSION='18_18.4.1.1'
|
||||
ENV LC_ALL='C'
|
||||
|
||||
WORKDIR /etc/shlink
|
||||
|
||||
# Install required PHP extensions
|
||||
RUN \
|
||||
# Temp install dev dependencies needed to compile the extensions
|
||||
apk add --no-cache --virtual .dev-deps sqlite-dev postgresql-dev icu-dev libzip-dev zlib-dev libpng-dev linux-headers && \
|
||||
docker-php-ext-install -j"$(nproc)" pdo_mysql pdo_pgsql intl calendar sockets bcmath zip gd && \
|
||||
apk add --no-cache --virtual .dev-deps sqlite-dev postgresql-dev icu-dev libzip-dev zlib-dev linux-headers && \
|
||||
docker-php-ext-install -j"$(nproc)" pdo_mysql pdo_pgsql intl calendar sockets bcmath zip && \
|
||||
apk add --no-cache sqlite-libs && \
|
||||
docker-php-ext-install -j"$(nproc)" pdo_sqlite && \
|
||||
# Remove temp dev extensions, and install prod equivalents that are required at runtime
|
||||
@ -36,14 +36,14 @@ RUN apk add --no-cache --virtual .phpize-deps ${PHPIZE_DEPS} unixodbc-dev && \
|
||||
apk del .phpize-deps
|
||||
|
||||
# Install shlink
|
||||
FROM base as builder
|
||||
FROM base AS builder
|
||||
COPY . .
|
||||
COPY --from=composer:2 /usr/bin/composer ./composer.phar
|
||||
RUN apk add --no-cache git && \
|
||||
php composer.phar install --no-dev --prefer-dist --optimize-autoloader --no-progress --no-interaction && \
|
||||
php composer.phar clear-cache && \
|
||||
rm -r docker composer.* && \
|
||||
sed -i "s/%SHLINK_VERSION%/${SHLINK_VERSION}/g" config/autoload/app_options.global.php
|
||||
sed -i "s/%SHLINK_VERSION%/${SHLINK_VERSION}/g" module/Core/src/Config/Options/AppOptions.php
|
||||
|
||||
|
||||
# Prepare final image
|
||||
@ -61,7 +61,6 @@ EXPOSE 8080
|
||||
|
||||
# Copy config specific for the image
|
||||
COPY docker/docker-entrypoint.sh docker-entrypoint.sh
|
||||
COPY docker/config/shlink_in_docker.local.php config/autoload/shlink_in_docker.local.php
|
||||
COPY docker/config/php.ini ${PHP_INI_DIR}/conf.d/
|
||||
|
||||
USER ${USER_ID}
|
||||
|
||||
12
README.md
12
README.md
@ -7,8 +7,7 @@
|
||||
[](https://github.com/shlinkio/shlink/blob/main/LICENSE)
|
||||
|
||||
[](https://fosstodon.org/@shlinkio)
|
||||
[](https://bsky.app/profile/shlinkio.bsky.social)
|
||||
[](https://twitter.com/shlinkio)
|
||||
[](https://bsky.app/profile/shlink.io)
|
||||
[](https://slnk.to/donate)
|
||||
|
||||
A PHP-based self-hosted URL shortener that can be used to serve shortened URLs under your own domain.
|
||||
@ -37,10 +36,9 @@ The idea is that you can just generate a container using the image and provide t
|
||||
|
||||
First, make sure the host where you are going to run shlink fulfills these requirements:
|
||||
|
||||
* PHP 8.2 or 8.3
|
||||
* PHP 8.4 or 8.5
|
||||
* The next PHP extensions: json, curl, pdo, intl, gd and gmp/bcmath.
|
||||
* apcu extension is recommended if you don't plan to use RoadRunner.
|
||||
* xml extension is required if you want to generate QR codes in svg format.
|
||||
* sockets and bcmath extensions are required if you want to integrate with a RabbitMQ instance.
|
||||
* MySQL, MariaDB, PostgreSQL, MicrosoftSQL or SQLite.
|
||||
* You will also need the corresponding pdo variation for the database you are planning to use: `pdo_mysql`, `pdo_pgsql`, `pdo_sqlsrv` or `pdo_sqlite`.
|
||||
@ -100,6 +98,12 @@ Both the API and CLI allow you to do mostly the same operations, except for API
|
||||
|
||||
If you are trying to find out how to run the project in development mode or how to provide contributions, read the [CONTRIBUTING](CONTRIBUTING.md) doc.
|
||||
|
||||
## Powered by
|
||||
|
||||
Thanks to [JetBrains](https://www.jetbrains.com/) for their continuous support to this project in the form of IDE licenses.
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
> This product includes GeoLite2 data created by MaxMind, available from [https://www.maxmind.com](https://www.maxmind.com)
|
||||
|
||||
36
bin/frankenphp-worker.php
Normal file
36
bin/frankenphp-worker.php
Normal file
@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Shlinkio\Shlink;
|
||||
|
||||
use Laminas\Diactoros\ServerRequestFactory;
|
||||
use Laminas\HttpHandlerRunner\Emitter\EmitterInterface;
|
||||
use Mezzio\Application;
|
||||
use Psr\Container\ContainerInterface;
|
||||
|
||||
use function frankenphp_handle_request;
|
||||
use function gc_collect_cycles;
|
||||
|
||||
(static function (): void {
|
||||
/** @var ContainerInterface $container */
|
||||
$container = include __DIR__ . '/../config/container.php';
|
||||
$app = $container->get(Application::class);
|
||||
$responseEmitter = $container->get(EmitterInterface::class);
|
||||
$handler = static function () use ($app, $responseEmitter): void {
|
||||
$response = $app->handle(ServerRequestFactory::fromGlobals());
|
||||
$responseEmitter->emit($response);
|
||||
};
|
||||
|
||||
$maxRequests = (int) ($_SERVER['MAX_REQUESTS'] ?? 0);
|
||||
for ($nbRequests = 0; !$maxRequests || $nbRequests < $maxRequests; ++$nbRequests) {
|
||||
$keepRunning = frankenphp_handle_request($handler);
|
||||
|
||||
// Call the garbage collector to reduce the chances of it being triggered in the middle of a page generation
|
||||
gc_collect_cycles();
|
||||
|
||||
if (! $keepRunning) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
})();
|
||||
@ -2,18 +2,22 @@
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Shlinkio\Shlink;
|
||||
|
||||
use Mezzio\Application;
|
||||
use Psr\Container\ContainerInterface;
|
||||
use Shlinkio\Shlink\Common\Middleware\RequestIdMiddleware;
|
||||
use Shlinkio\Shlink\EventDispatcher\RoadRunner\RoadRunnerTaskConsumerToListener;
|
||||
use Spiral\RoadRunner\Http\PSR7Worker;
|
||||
|
||||
use function gc_collect_cycles;
|
||||
use function Shlinkio\Shlink\Config\env;
|
||||
|
||||
(static function (): void {
|
||||
/** @var ContainerInterface $container */
|
||||
$container = include __DIR__ . '/../config/container.php';
|
||||
$rrMode = env('RR_MODE');
|
||||
$gcCollectCycles = env('GC_COLLECT_CYCLES', default: false);
|
||||
|
||||
if ($rrMode === 'http') {
|
||||
// This was spin-up as a web worker
|
||||
@ -25,6 +29,10 @@ use function Shlinkio\Shlink\Config\env;
|
||||
$worker->respond($app->handle($req));
|
||||
} catch (Throwable $e) {
|
||||
$worker->getWorker()->error((string) $e);
|
||||
} finally {
|
||||
if ($gcCollectCycles) {
|
||||
gc_collect_cycles();
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
@ -6,6 +6,8 @@ export TEST_RUNTIME="${TEST_RUNTIME:-"rr"}" # rr is the only runtime currently s
|
||||
export DB_DRIVER="${DB_DRIVER:-"postgres"}"
|
||||
export GENERATE_COVERAGE="${GENERATE_COVERAGE:-"no"}"
|
||||
|
||||
[ "$GENERATE_COVERAGE" != 'no' ] && export XDEBUG_MODE=coverage
|
||||
|
||||
# Reset logs
|
||||
OUTPUT_LOGS=data/log/api-tests/output.log
|
||||
rm -rf data/log/api-tests
|
||||
@ -22,7 +24,7 @@ echo 'Starting server...'
|
||||
-o=logs.channels.server.output="${PWD}/${OUTPUT_LOGS}" &
|
||||
sleep 2 # Let's give the server a couple of seconds to start
|
||||
|
||||
vendor/bin/phpunit --order-by=random -c phpunit-api.xml --testdox --colors=always $*
|
||||
vendor/bin/phpunit --order-by=random -c phpunit-api.xml --testdox --testdox-summary $*
|
||||
TESTS_EXIT_CODE=$?
|
||||
|
||||
[ "$TEST_RUNTIME" = 'rr' ] && bin/rr stop -w .
|
||||
|
||||
14
bin/test/run-cli-tests.sh
Executable file
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
|
||||
4
build.sh
4
build.sh
@ -35,8 +35,8 @@ ${composerBin} install --no-dev --prefer-dist --optimize-autoloader --no-progres
|
||||
echo 'Deleting dev files...'
|
||||
rm composer.*
|
||||
|
||||
# Update Shlink version in config
|
||||
sed -i "s/%SHLINK_VERSION%/${version}/g" config/autoload/app_options.global.php
|
||||
# Update Shlink version
|
||||
sed -i "s/%SHLINK_VERSION%/${version}/g" module/Core/src/Config/Options/AppOptions.php
|
||||
|
||||
# Compressing file
|
||||
echo 'Compressing files...'
|
||||
|
||||
189
composer.json
189
composer.json
@ -12,70 +12,67 @@
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": "^8.2",
|
||||
"php": "^8.4",
|
||||
"ext-curl": "*",
|
||||
"ext-gd": "*",
|
||||
"ext-json": "*",
|
||||
"ext-mbstring": "*",
|
||||
"ext-pdo": "*",
|
||||
"akrabat/ip-address-middleware": "^2.1",
|
||||
"cakephp/chronos": "^3.0.2",
|
||||
"doctrine/dbal": "^4.0",
|
||||
"doctrine/migrations": "^3.6",
|
||||
"doctrine/orm": "^3.0",
|
||||
"endroid/qr-code": "^5.0",
|
||||
"akrabat/ip-address-middleware": "^2.6",
|
||||
"cakephp/chronos": "^3.1",
|
||||
"doctrine/dbal": "^4.3",
|
||||
"doctrine/migrations": "^3.9",
|
||||
"doctrine/orm": "^3.5",
|
||||
"donatj/phpuseragentparser": "^1.10",
|
||||
"friendsofphp/proxy-manager-lts": "^1.0",
|
||||
"geoip2/geoip2": "^3.0",
|
||||
"guzzlehttp/guzzle": "^7.5",
|
||||
"hidehalo/nanoid-php": "^1.1",
|
||||
"jaybizzle/crawler-detect": "^1.2.116",
|
||||
"laminas/laminas-config": "^3.8",
|
||||
"laminas/laminas-config-aggregator": "^1.15",
|
||||
"laminas/laminas-diactoros": "^3.3",
|
||||
"laminas/laminas-inputfilter": "^2.27",
|
||||
"laminas/laminas-servicemanager": "^3.21",
|
||||
"laminas/laminas-stdlib": "^3.17",
|
||||
"matomo/matomo-php-tracker": "^3.2",
|
||||
"mezzio/mezzio": "^3.17",
|
||||
"mezzio/mezzio-fastroute": "^3.11",
|
||||
"mezzio/mezzio-problem-details": "^1.13",
|
||||
"mlocati/ip-lib": "^1.18",
|
||||
"mobiledetect/mobiledetectlib": "^4.8",
|
||||
"geoip2/geoip2": "^3.1",
|
||||
"guzzlehttp/guzzle": "^7.9",
|
||||
"hidehalo/nanoid-php": "^2.0",
|
||||
"jaybizzle/crawler-detect": "^1.3",
|
||||
"laminas/laminas-config-aggregator": "^1.17",
|
||||
"laminas/laminas-diactoros": "^3.5",
|
||||
"laminas/laminas-inputfilter": "^2.31",
|
||||
"laminas/laminas-servicemanager": "^3.23",
|
||||
"laminas/laminas-stdlib": "^3.20",
|
||||
"matomo/matomo-php-tracker": "^3.3",
|
||||
"mezzio/mezzio": "^3.20",
|
||||
"mezzio/mezzio-fastroute": "^3.12",
|
||||
"mezzio/mezzio-problem-details": "^1.15",
|
||||
"mlocati/ip-lib": "^1.18.1",
|
||||
"pagerfanta/core": "^3.8",
|
||||
"ramsey/uuid": "^4.7",
|
||||
"shlinkio/doctrine-specification": "^2.1.1",
|
||||
"shlinkio/shlink-common": "^6.1",
|
||||
"shlinkio/shlink-config": "^3.0",
|
||||
"shlinkio/shlink-event-dispatcher": "^4.1",
|
||||
"shlinkio/shlink-importer": "^5.3.2",
|
||||
"shlinkio/shlink-installer": "^9.1",
|
||||
"shlinkio/shlink-ip-geolocation": "^4.0",
|
||||
"shlinkio/shlink-json": "^1.1",
|
||||
"spiral/roadrunner": "^2023.3",
|
||||
"spiral/roadrunner-cli": "^2.6",
|
||||
"spiral/roadrunner-http": "^3.3",
|
||||
"spiral/roadrunner-jobs": "^4.3",
|
||||
"symfony/console": "^7.0",
|
||||
"symfony/filesystem": "^7.0",
|
||||
"symfony/lock": "^7.0",
|
||||
"symfony/process": "^7.0",
|
||||
"symfony/string": "^7.0"
|
||||
"shlinkio/doctrine-specification": "^2.2",
|
||||
"shlinkio/shlink-common": "dev-main#f2550b5 as 7.3.0",
|
||||
"shlinkio/shlink-config": "dev-main#fb186e4 as 4.1.0",
|
||||
"shlinkio/shlink-event-dispatcher": "dev-main#54d4701 as 4.4.0",
|
||||
"shlinkio/shlink-importer": "dev-main#4498f0a as 5.7.0",
|
||||
"shlinkio/shlink-installer": "dev-develop#40e08cb as 10.0.0",
|
||||
"shlinkio/shlink-ip-geolocation": "dev-main#e0c45b2 as 5.0.0",
|
||||
"shlinkio/shlink-json": "dev-main#7c096d6 as 1.3.0",
|
||||
"spiral/roadrunner": "^2025.1",
|
||||
"spiral/roadrunner-cli": "^2.7",
|
||||
"spiral/roadrunner-http": "^3.5",
|
||||
"spiral/roadrunner-jobs": "^4.6",
|
||||
"symfony/console": "^8.0 || ^7.4",
|
||||
"symfony/filesystem": "^8.0",
|
||||
"symfony/lock": "^8.0",
|
||||
"symfony/process": "^8.0",
|
||||
"symfony/string": "^8.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"devizzent/cebe-php-openapi": "^1.0.1",
|
||||
"devizzent/cebe-php-openapi": "^1.1.2",
|
||||
"devster/ubench": "^2.1",
|
||||
"phpstan/phpstan": "^1.11",
|
||||
"phpstan/phpstan-doctrine": "^1.4",
|
||||
"phpstan/phpstan-phpunit": "^1.4",
|
||||
"phpstan/phpstan-symfony": "^1.4",
|
||||
"phpunit/php-code-coverage": "^11.0",
|
||||
"phpunit/phpcov": "^10.0",
|
||||
"phpunit/phpunit": "^11.1",
|
||||
"phpstan/phpstan": "^2.1",
|
||||
"phpstan/phpstan-doctrine": "^2.0",
|
||||
"phpstan/phpstan-phpunit": "^2.0.5",
|
||||
"phpstan/phpstan-symfony": "^2.0",
|
||||
"phpunit/php-code-coverage": "^12.0",
|
||||
"phpunit/phpcov": "^11.0",
|
||||
"phpunit/phpunit": "^12.0.10",
|
||||
"roave/security-advisories": "dev-master",
|
||||
"shlinkio/php-coding-standard": "~2.3.0",
|
||||
"shlinkio/shlink-test-utils": "^4.1",
|
||||
"symfony/var-dumper": "^7.0",
|
||||
"veewee/composer-run-parallel": "^1.3"
|
||||
"shlinkio/php-coding-standard": "~2.5.0",
|
||||
"shlinkio/shlink-test-utils": "^4.4",
|
||||
"symfony/var-dumper": "^8.0",
|
||||
"veewee/composer-run-parallel": "^1.4"
|
||||
},
|
||||
"conflict": {
|
||||
"symfony/var-exporter": ">=6.3.9,<=6.4.0"
|
||||
@ -109,66 +106,56 @@
|
||||
},
|
||||
"scripts": {
|
||||
"ci": [
|
||||
"@parallel cs stan swagger:validate test:unit:ci test:db:sqlite:ci test:db:postgres test:db:mysql test:db:maria test:db:ms",
|
||||
"@parallel cs stan openapi:validate test:unit:ci test:db:sqlite:ci test:db:postgres test:db:mysql test:db:maria test:db:ms",
|
||||
"@parallel test:api:ci test:cli:ci"
|
||||
],
|
||||
"cs": "phpcs -s",
|
||||
"cs:fix": "phpcbf",
|
||||
"stan": "APP_ENV=test php vendor/bin/phpstan analyse module/*/src module/*/test* module/*/config module/*/migrations config docker/config --level=8",
|
||||
"stan": ["@putenv APP_ENV=test", "phpstan analyse"],
|
||||
"test": [
|
||||
"@parallel test:unit test:db",
|
||||
"@parallel test:api test:cli"
|
||||
],
|
||||
"test:unit": "COLUMNS=120 vendor/bin/phpunit --order-by=random --colors=always --testdox",
|
||||
"test:unit:ci": "@test:unit --coverage-php=build/coverage-unit.cov",
|
||||
"test:unit:pretty": "@test:unit --coverage-html build/coverage-unit/coverage-html",
|
||||
"test:unit": ["@putenv COLUMNS=120", "phpunit --order-by=random --testdox --testdox-summary"],
|
||||
"test:unit:ci": ["@putenv XDEBUG_MODE=coverage", "@test:unit --coverage-php=build/coverage-unit.cov"],
|
||||
"test:unit:pretty": ["@putenv XDEBUG_MODE=coverage", "@test:unit --coverage-html build/coverage-unit/coverage-html"],
|
||||
"test:db": "@parallel test:db:sqlite:ci test:db:mysql test:db:maria test:db:postgres test:db:ms",
|
||||
"test:db:sqlite": "APP_ENV=test php vendor/bin/phpunit --order-by=random --colors=always --testdox -c phpunit-db.xml",
|
||||
"test:db:sqlite:ci": "@test:db:sqlite --coverage-php build/coverage-db.cov",
|
||||
"test:db:mysql": "DB_DRIVER=mysql composer test:db:sqlite -- $*",
|
||||
"test:db:maria": "DB_DRIVER=maria composer test:db:sqlite -- $*",
|
||||
"test:db:postgres": "DB_DRIVER=postgres composer test:db:sqlite -- $*",
|
||||
"test:db:ms": "DB_DRIVER=mssql composer test:db:sqlite -- $*",
|
||||
"test:db:sqlite": ["@putenv APP_ENV=test", "phpunit --order-by=random --testdox --testdox-summary -c phpunit-db.xml"],
|
||||
"test:db:sqlite:ci": ["@putenv XDEBUG_MODE=coverage", "@test:db:sqlite --coverage-php build/coverage-db.cov"],
|
||||
"test:db:mysql": ["@putenv DB_DRIVER=mysql", "@test:db:sqlite"],
|
||||
"test:db:maria": ["@putenv DB_DRIVER=maria", "@test:db:sqlite"],
|
||||
"test:db:postgres": ["@putenv DB_DRIVER=postgres", "@test:db:sqlite"],
|
||||
"test:db:ms": ["@putenv DB_DRIVER=mssql", "@test:db:sqlite"],
|
||||
"test:api": "bin/test/run-api-tests.sh",
|
||||
"test:api:sqlite": "DB_DRIVER=sqlite composer test:api -- $*",
|
||||
"test:api:mysql": "DB_DRIVER=mysql composer test:api -- $*",
|
||||
"test:api:maria": "DB_DRIVER=maria composer test:api -- $*",
|
||||
"test:api:mssql": "DB_DRIVER=mssql composer test:api -- $*",
|
||||
"test:api:ci": "GENERATE_COVERAGE=yes composer test:api && vendor/bin/phpcov merge build/coverage-api --php build/coverage-api.cov && rm build/coverage-api/*.cov",
|
||||
"test:api:pretty": "GENERATE_COVERAGE=yes composer test:api && vendor/bin/phpcov merge build/coverage-api --html build/coverage-api/coverage-html && rm build/coverage-api/*.cov",
|
||||
"test:cli": "APP_ENV=test DB_DRIVER=maria TEST_ENV=cli php vendor/bin/phpunit --order-by=random --colors=always --testdox -c phpunit-cli.xml",
|
||||
"test:cli:ci": "GENERATE_COVERAGE=yes composer test:cli && vendor/bin/phpcov merge build/coverage-cli --php build/coverage-cli.cov && rm build/coverage-cli/*.cov",
|
||||
"test:cli:pretty": "GENERATE_COVERAGE=yes composer test:cli && vendor/bin/phpcov merge build/coverage-cli --html build/coverage-cli/coverage-html && rm build/coverage-cli/*.cov",
|
||||
"swagger:validate": "php-openapi validate docs/swagger/swagger.json",
|
||||
"swagger:inline": "php-openapi inline docs/swagger/swagger.json docs/swagger/swagger-inlined.json",
|
||||
"test:api:sqlite": ["@putenv DB_DRIVER=sqlite", "@test:api"],
|
||||
"test:api:mysql": ["@putenv DB_DRIVER=mysql", "@test:api"],
|
||||
"test:api:maria": ["@putenv DB_DRIVER=maria", "@test:api"],
|
||||
"test:api:mssql": ["@putenv DB_DRIVER=mssql", "@test:api"],
|
||||
"test:api:ci": [
|
||||
"@putenv GENERATE_COVERAGE=yes",
|
||||
"@test:api",
|
||||
"phpcov merge build/coverage-api --php build/coverage-api.cov && rm build/coverage-api/*.cov"
|
||||
],
|
||||
"test:api:pretty": [
|
||||
"@putenv GENERATE_COVERAGE=yes",
|
||||
"@test:api",
|
||||
"phpcov merge build/coverage-api --html build/coverage-api/coverage-html && rm build/coverage-api/*.cov"
|
||||
],
|
||||
"test:cli": "bin/test/run-cli-tests.sh",
|
||||
"test:cli:ci": [
|
||||
"@putenv GENERATE_COVERAGE=yes",
|
||||
"@test:cli",
|
||||
"@php -d memory_limit=-1 vendor/bin/phpcov merge build/coverage-cli --php build/coverage-cli.cov && rm build/coverage-cli/*.cov"
|
||||
],
|
||||
"test:cli:pretty": [
|
||||
"@putenv GENERATE_COVERAGE=yes",
|
||||
"@test:cli",
|
||||
"@php -d memory_limit=-1 phpcov merge build/coverage-cli --html build/coverage-cli/coverage-html && rm build/coverage-cli/*.cov"
|
||||
],
|
||||
"openapi:validate": "php-openapi validate docs/swagger/swagger.json",
|
||||
"openapi:inline": "php-openapi inline docs/swagger/swagger.json docs/swagger/openapi-inlined.json",
|
||||
"clean:dev": "rm -f data/database.sqlite && rm -f config/params/generated_config.php"
|
||||
},
|
||||
"scripts-descriptions": {
|
||||
"ci": "<fg=blue;options=bold>Alias for \"cs\", \"stan\", \"swagger:validate\" and \"test:ci\"</>",
|
||||
"cs": "<fg=blue;options=bold>Checks coding styles</>",
|
||||
"cs:fix": "<fg=blue;options=bold>Fixes coding styles, when possible</>",
|
||||
"stan": "<fg=blue;options=bold>Inspects code with phpstan</>",
|
||||
"test": "<fg=blue;options=bold>Runs all test suites</>",
|
||||
"test:unit": "<fg=blue;options=bold>Runs unit test suites</>",
|
||||
"test:unit:ci": "<fg=blue;options=bold>Runs unit test suites, generating all needed reports and logs for CI envs</>",
|
||||
"test:unit:pretty": "<fg=blue;options=bold>Runs unit test suites and generates an HTML code coverage report</>",
|
||||
"test:db": "<fg=blue;options=bold>Runs database test suites on a SQLite, MySQL, MariaDB, PostgreSQL and MsSQL</>",
|
||||
"test:db:sqlite": "<fg=blue;options=bold>Runs database test suites on a SQLite database</>",
|
||||
"test:db:sqlite:ci": "<fg=blue;options=bold>Runs database test suites on a SQLite database, generating all needed reports and logs for CI envs</>",
|
||||
"test:db:mysql": "<fg=blue;options=bold>Runs database test suites on a MySQL database</>",
|
||||
"test:db:maria": "<fg=blue;options=bold>Runs database test suites on a MariaDB database</>",
|
||||
"test:db:postgres": "<fg=blue;options=bold>Runs database test suites on a PostgreSQL database</>",
|
||||
"test:db:ms": "<fg=blue;options=bold>Runs database test suites on a Microsoft SQL Server database</>",
|
||||
"test:api": "<fg=blue;options=bold>Runs API test suites</>",
|
||||
"test:api:ci": "<fg=blue;options=bold>Runs API test suites, and generates code coverage for CI</>",
|
||||
"test:api:pretty": "<fg=blue;options=bold>Runs API test suites, and generates code coverage in HTML format</>",
|
||||
"test:cli": "<fg=blue;options=bold>Runs CLI test suites</>",
|
||||
"test:cli:ci": "<fg=blue;options=bold>Runs CLI test suites, and generates code coverage for CI</>",
|
||||
"test:cli:pretty": "<fg=blue;options=bold>Runs CLI test suites, and generates code coverage in HTML format</>",
|
||||
"swagger:validate": "<fg=blue;options=bold>Validates the swagger docs, making sure they fulfil the spec</>",
|
||||
"swagger:inline": "<fg=blue;options=bold>Inlines swagger docs in a single file</>",
|
||||
"clean:dev": "<fg=blue;options=bold>Deletes artifacts which are gitignored and could affect dev env</>"
|
||||
},
|
||||
"config": {
|
||||
"sort-packages": true,
|
||||
"platform-check": false,
|
||||
|
||||
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,17 +6,19 @@ use Shlinkio\Shlink\Core\Config\EnvVars;
|
||||
|
||||
return (static function (): array {
|
||||
$redisServers = EnvVars::REDIS_SERVERS->loadFromEnv();
|
||||
$redis = ['pub_sub_enabled' => $redisServers !== null && EnvVars::REDIS_PUB_SUB_ENABLED->loadFromEnv(false)];
|
||||
$redis = ['pub_sub_enabled' => $redisServers !== null && EnvVars::REDIS_PUB_SUB_ENABLED->loadFromEnv()];
|
||||
$cacheRedisBlock = $redisServers === null ? [] : [
|
||||
'redis' => [
|
||||
'servers' => $redisServers,
|
||||
'sentinel_service' => EnvVars::REDIS_SENTINEL_SERVICE->loadFromEnv(),
|
||||
'username' => EnvVars::REDIS_SERVERS_USER->loadFromEnv(),
|
||||
'password' => EnvVars::REDIS_SERVERS_PASSWORD->loadFromEnv(),
|
||||
],
|
||||
];
|
||||
|
||||
return [
|
||||
'cache' => [
|
||||
'namespace' => EnvVars::CACHE_NAMESPACE->loadFromEnv('Shlink'),
|
||||
'namespace' => EnvVars::CACHE_NAMESPACE->loadFromEnv(),
|
||||
...$cacheRedisBlock,
|
||||
],
|
||||
'redis' => $redis,
|
||||
|
||||
@ -1,20 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
return [
|
||||
|
||||
'ip_address_resolution' => [
|
||||
'headers_to_inspect' => [
|
||||
'CF-Connecting-IP',
|
||||
'X-Forwarded-For',
|
||||
'X-Forwarded',
|
||||
'Forwarded',
|
||||
'True-Client-IP',
|
||||
'X-Real-IP',
|
||||
'X-Cluster-Client-Ip',
|
||||
'Client-Ip',
|
||||
],
|
||||
],
|
||||
|
||||
];
|
||||
@ -3,13 +3,18 @@
|
||||
declare(strict_types=1);
|
||||
|
||||
use Laminas\ConfigAggregator\ConfigAggregator;
|
||||
use Shlinkio\Shlink\Core\Config\EnvVars;
|
||||
|
||||
return [
|
||||
return (function () {
|
||||
$isDev = EnvVars::isDevEnv();
|
||||
|
||||
'debug' => false,
|
||||
return [
|
||||
|
||||
// Disabling config cache for cli, ensures it's never used for RoadRunner, and also that console
|
||||
// commands don't generate a cache file that's then used by php-fpm web executions
|
||||
ConfigAggregator::ENABLE_CACHE => PHP_SAPI !== 'cli',
|
||||
'debug' => $isDev,
|
||||
|
||||
];
|
||||
// Disabling config cache for cli, ensures it's never used for RoadRunner, and also that console
|
||||
// commands don't generate a cache file that's then used by php-fpm web executions
|
||||
ConfigAggregator::ENABLE_CACHE => ! $isDev && PHP_SAPI !== 'cli',
|
||||
|
||||
];
|
||||
})();
|
||||
|
||||
@ -1,12 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Laminas\ConfigAggregator\ConfigAggregator;
|
||||
|
||||
return [
|
||||
|
||||
'debug' => true,
|
||||
ConfigAggregator::ENABLE_CACHE => false,
|
||||
|
||||
];
|
||||
@ -1,11 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
return [
|
||||
|
||||
'cors' => [
|
||||
'max_age' => 3600,
|
||||
],
|
||||
|
||||
];
|
||||
@ -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),
|
||||
],
|
||||
|
||||
];
|
||||
})();
|
||||
@ -11,6 +11,7 @@ use Psr\Http\Client\ClientInterface;
|
||||
use Psr\Http\Message\ServerRequestFactoryInterface;
|
||||
use Psr\Http\Message\StreamFactoryInterface;
|
||||
use Psr\Http\Message\UploadedFileFactoryInterface;
|
||||
use Shlinkio\Shlink\Core\Config\EnvVars;
|
||||
use Spiral\RoadRunner\Http\PSR7Worker;
|
||||
use Spiral\RoadRunner\WorkerInterface;
|
||||
use Symfony\Component\Filesystem\Filesystem;
|
||||
@ -36,7 +37,7 @@ return [
|
||||
'lazy_services' => [
|
||||
'proxies_target_dir' => 'data/proxies',
|
||||
'proxies_namespace' => 'ShlinkProxy',
|
||||
'write_proxy_files' => true,
|
||||
'write_proxy_files' => EnvVars::isProdEnv(),
|
||||
],
|
||||
],
|
||||
|
||||
|
||||
@ -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));
|
||||
}
|
||||
},
|
||||
],
|
||||
],
|
||||
|
||||
];
|
||||
@ -12,9 +12,10 @@ use function Shlinkio\Shlink\Core\ArrayUtils\contains;
|
||||
|
||||
return (static function (): array {
|
||||
$driver = EnvVars::DB_DRIVER->loadFromEnv();
|
||||
$useEncryption = (bool) EnvVars::DB_USE_ENCRYPTION->loadFromEnv();
|
||||
$isMysqlCompatible = contains($driver, ['maria', 'mysql']);
|
||||
|
||||
$resolveDriver = static fn () => match ($driver) {
|
||||
$doctrineDriver = match ($driver) {
|
||||
'postgres' => 'pdo_pgsql',
|
||||
'mssql' => 'pdo_sqlsrv',
|
||||
default => 'pdo_mysql',
|
||||
@ -23,36 +24,40 @@ return (static function (): array {
|
||||
$value = $envVar->loadFromEnv();
|
||||
return $value === null ? null : (string) $value;
|
||||
};
|
||||
$resolveDefaultPort = static fn () => match ($driver) {
|
||||
'postgres' => '5432',
|
||||
'mssql' => '1433',
|
||||
default => '3306',
|
||||
};
|
||||
$resolveCharset = static fn () => match ($driver) {
|
||||
$charset = match ($driver) {
|
||||
// This does not determine charsets or collations in tables or columns, but the charset used in the data
|
||||
// flowing in the connection, so it has to match what has been set in the database.
|
||||
'maria', 'mysql' => 'utf8mb4',
|
||||
'postgres' => 'utf8',
|
||||
default => null,
|
||||
};
|
||||
|
||||
$resolveConnection = static fn () => match ($driver) {
|
||||
$driverOptions = match ($driver) {
|
||||
'mssql' => ['TrustServerCertificate' => 'true'],
|
||||
'maria', 'mysql' => ! $useEncryption ? [] : [
|
||||
1007 => true, // PDO::MYSQL_ATTR_SSL_KEY: Require using SSL
|
||||
1014 => false, // PDO::MYSQL_ATTR_SSL_VERIFY_SERVER_CERT: Trust any certificate
|
||||
],
|
||||
'postgres' => ! $useEncryption ? [] : [
|
||||
'sslmode' => 'require', // Require connections to be encrypted
|
||||
'sslrootcert' => '', // Allow any certificate
|
||||
],
|
||||
default => [],
|
||||
};
|
||||
$connection = match ($driver) {
|
||||
null, 'sqlite' => [
|
||||
'driver' => 'pdo_sqlite',
|
||||
'path' => 'data/database.sqlite',
|
||||
],
|
||||
default => [
|
||||
'driver' => $resolveDriver(),
|
||||
'dbname' => EnvVars::DB_NAME->loadFromEnv('shlink'),
|
||||
'driver' => $doctrineDriver,
|
||||
'dbname' => EnvVars::DB_NAME->loadFromEnv(),
|
||||
'user' => $readCredentialAsString(EnvVars::DB_USER),
|
||||
'password' => $readCredentialAsString(EnvVars::DB_PASSWORD),
|
||||
'host' => EnvVars::DB_HOST->loadFromEnv(EnvVars::DB_UNIX_SOCKET->loadFromEnv()),
|
||||
'port' => EnvVars::DB_PORT->loadFromEnv($resolveDefaultPort()),
|
||||
'host' => EnvVars::DB_HOST->loadFromEnv(),
|
||||
'port' => EnvVars::DB_PORT->loadFromEnv(),
|
||||
'unix_socket' => $isMysqlCompatible ? EnvVars::DB_UNIX_SOCKET->loadFromEnv() : null,
|
||||
'charset' => $resolveCharset(),
|
||||
'driverOptions' => $driver !== 'mssql' ? [] : [
|
||||
'TrustServerCertificate' => 'true',
|
||||
],
|
||||
'charset' => $charset,
|
||||
'driverOptions' => $driverOptions,
|
||||
],
|
||||
};
|
||||
|
||||
@ -68,7 +73,7 @@ return (static function (): array {
|
||||
Events::postFlush => [ShortUrlVisitsCountTracker::class, OrphanVisitsCountTracker::class],
|
||||
],
|
||||
],
|
||||
'connection' => $resolveConnection(),
|
||||
'connection' => $connection,
|
||||
],
|
||||
|
||||
];
|
||||
|
||||
@ -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',
|
||||
// ],
|
||||
],
|
||||
],
|
||||
|
||||
];
|
||||
@ -8,7 +8,7 @@ return [
|
||||
|
||||
'geolite2' => [
|
||||
'db_location' => __DIR__ . '/../../data/GeoLite2-City.mmdb',
|
||||
'temp_dir' => __DIR__ . '/../../data',
|
||||
'temp_dir' => __DIR__ . '/../../data/temp-geolite',
|
||||
'license_key' => EnvVars::GEOLITE_LICENSE_KEY->loadFromEnv(),
|
||||
],
|
||||
|
||||
|
||||
@ -13,6 +13,7 @@ return [
|
||||
'enabled_options' => [
|
||||
Option\Server\RuntimeConfigOption::class,
|
||||
Option\Server\MemoryLimitConfigOption::class,
|
||||
Option\Server\LogsFormatConfigOption::class,
|
||||
Option\Database\DatabaseDriverConfigOption::class,
|
||||
Option\Database\DatabaseNameConfigOption::class,
|
||||
Option\Database\DatabaseHostConfigOption::class,
|
||||
@ -20,6 +21,7 @@ return [
|
||||
Option\Database\DatabaseUserConfigOption::class,
|
||||
Option\Database\DatabasePasswordConfigOption::class,
|
||||
Option\Database\DatabaseUnixSocketConfigOption::class,
|
||||
Option\Database\DatabaseUseEncryptionConfigOption::class,
|
||||
Option\UrlShortener\ShortDomainHostConfigOption::class,
|
||||
Option\UrlShortener\ShortDomainSchemaConfigOption::class,
|
||||
Option\Redirect\BaseUrlRedirectConfigOption::class,
|
||||
@ -31,6 +33,8 @@ return [
|
||||
Option\Cache\CacheNamespaceConfigOption::class,
|
||||
Option\Redis\RedisServersConfigOption::class,
|
||||
Option\Redis\RedisSentinelServiceConfigOption::class,
|
||||
Option\Redis\RedisServersUserConfigOption::class,
|
||||
Option\Redis\RedisServersPasswordConfigOption::class,
|
||||
Option\Redis\RedisPubSubConfigOption::class,
|
||||
Option\UrlShortener\ShortCodeLengthOption::class,
|
||||
Option\Mercure\EnableMercureConfigOption::class,
|
||||
@ -40,11 +44,14 @@ return [
|
||||
Option\UrlShortener\GeoLiteLicenseKeyConfigOption::class,
|
||||
Option\UrlShortener\RedirectStatusCodeConfigOption::class,
|
||||
Option\UrlShortener\RedirectCacheLifeTimeConfigOption::class,
|
||||
Option\UrlShortener\RedirectCacheVisibilityConfigOption::class,
|
||||
Option\UrlShortener\AutoResolveTitlesConfigOption::class,
|
||||
Option\UrlShortener\AppendExtraPathConfigOption::class,
|
||||
Option\UrlShortener\ExtraPathModeConfigOption::class,
|
||||
Option\UrlShortener\EnableMultiSegmentSlugsConfigOption::class,
|
||||
Option\UrlShortener\EnableTrailingSlashConfigOption::class,
|
||||
Option\UrlShortener\ShortUrlModeConfigOption::class,
|
||||
Option\Robots\RobotsAllowAllShortUrlsConfigOption::class,
|
||||
Option\Robots\RobotsUserAgentsConfigOption::class,
|
||||
Option\Tracking\IpAnonymizationConfigOption::class,
|
||||
Option\Tracking\OrphanVisitsTrackingConfigOption::class,
|
||||
Option\Tracking\DisableTrackParamConfigOption::class,
|
||||
@ -53,15 +60,6 @@ return [
|
||||
Option\Tracking\DisableIpTrackingConfigOption::class,
|
||||
Option\Tracking\DisableReferrerTrackingConfigOption::class,
|
||||
Option\Tracking\DisableUaTrackingConfigOption::class,
|
||||
Option\QrCode\DefaultSizeConfigOption::class,
|
||||
Option\QrCode\DefaultMarginConfigOption::class,
|
||||
Option\QrCode\DefaultFormatConfigOption::class,
|
||||
Option\QrCode\DefaultErrorCorrectionConfigOption::class,
|
||||
Option\QrCode\DefaultRoundBlockSizeConfigOption::class,
|
||||
Option\QrCode\DefaultColorConfigOption::class,
|
||||
Option\QrCode\DefaultBgColorConfigOption::class,
|
||||
Option\QrCode\DefaultLogoUrlConfigOption::class,
|
||||
Option\QrCode\EnabledForDisabledShortUrlsConfigOption::class,
|
||||
Option\RabbitMq\RabbitMqEnabledConfigOption::class,
|
||||
Option\RabbitMq\RabbitMqHostConfigOption::class,
|
||||
Option\RabbitMq\RabbitMqUseSslConfigOption::class,
|
||||
@ -73,6 +71,11 @@ return [
|
||||
Option\Matomo\MatomoBaseUrlConfigOption::class,
|
||||
Option\Matomo\MatomoSiteIdConfigOption::class,
|
||||
Option\Matomo\MatomoApiTokenConfigOption::class,
|
||||
Option\RealTimeUpdates\RealTimeUpdatesTopicsConfigOption::class,
|
||||
Option\Cors\CorsAllowOriginConfigOption::class,
|
||||
Option\Cors\CorsAllowCredentialsConfigOption::class,
|
||||
Option\Cors\CorsMaxAgeConfigOption::class,
|
||||
Option\TrustedProxiesConfigOption::class,
|
||||
],
|
||||
|
||||
'installation_commands' => [
|
||||
|
||||
48
config/autoload/ip-address.global.php
Normal file
48
config/autoload/ip-address.global.php
Normal file
@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use RKA\Middleware\IpAddress;
|
||||
use RKA\Middleware\Mezzio\IpAddressFactory;
|
||||
use Shlinkio\Shlink\Core\Config\EnvVars;
|
||||
|
||||
use function Shlinkio\Shlink\Core\splitByComma;
|
||||
|
||||
use const Shlinkio\Shlink\IP_ADDRESS_REQUEST_ATTRIBUTE;
|
||||
|
||||
return (static function (): array {
|
||||
$trustedProxies = EnvVars::TRUSTED_PROXIES->loadFromEnv();
|
||||
$proxiesIsHopCount = is_numeric($trustedProxies);
|
||||
|
||||
return [
|
||||
|
||||
// Configuration for RKA\Middleware\IpAddress
|
||||
'rka' => [
|
||||
'ip_address' => [
|
||||
'attribute_name' => IP_ADDRESS_REQUEST_ATTRIBUTE,
|
||||
'check_proxy_headers' => true,
|
||||
// List of trusted proxies
|
||||
'trusted_proxies' => $proxiesIsHopCount ? [] : splitByComma($trustedProxies),
|
||||
// Amount of addresses to skip from the right, before finding the visitor IP address
|
||||
'hop_count' => $proxiesIsHopCount ? (int) $trustedProxies : 0,
|
||||
'headers_to_inspect' => [
|
||||
'CF-Connecting-IP',
|
||||
'X-Forwarded-For',
|
||||
'X-Forwarded',
|
||||
'Forwarded',
|
||||
'True-Client-IP',
|
||||
'X-Real-IP',
|
||||
'X-Cluster-Client-Ip',
|
||||
'Client-Ip',
|
||||
],
|
||||
],
|
||||
],
|
||||
|
||||
'dependencies' => [
|
||||
'factories' => [
|
||||
IpAddress::class => IpAddressFactory::class,
|
||||
],
|
||||
],
|
||||
|
||||
];
|
||||
})();
|
||||
@ -14,31 +14,45 @@ use Shlinkio\Shlink\Common\Logger\LoggerFactory;
|
||||
use Shlinkio\Shlink\Common\Logger\LoggerType;
|
||||
use Shlinkio\Shlink\Common\Middleware\AccessLogMiddleware;
|
||||
use Shlinkio\Shlink\Common\Middleware\RequestIdMiddleware;
|
||||
use Shlinkio\Shlink\Core\Config\EnvVars;
|
||||
use Shlinkio\Shlink\Core\EventDispatcher\Helper\RequestIdProvider;
|
||||
use Shlinkio\Shlink\EventDispatcher\Util\RequestIdProviderInterface;
|
||||
|
||||
use function Shlinkio\Shlink\Config\env;
|
||||
use function Shlinkio\Shlink\Config\runningInRoadRunner;
|
||||
|
||||
return (static function (): array {
|
||||
$common = [
|
||||
'level' => Level::Info->value,
|
||||
$isDev = EnvVars::isDevEnv();
|
||||
$format = EnvVars::LOGS_FORMAT->loadFromEnv();
|
||||
$buildCommonConfig = static fn (bool $addNewLine = false) => [
|
||||
'level' => $isDev ? Level::Debug->value : Level::Info->value,
|
||||
'processors' => [RequestIdMiddleware::class],
|
||||
'line_format' =>
|
||||
'[%datetime%] [%extra.' . RequestIdMiddleware::ATTRIBUTE . '%] %channel%.%level_name% - %message%',
|
||||
'formatter' => [
|
||||
'type' => $format,
|
||||
'add_new_line' => $addNewLine,
|
||||
'line_format' =>
|
||||
'[%datetime%] [%extra.' . RequestIdMiddleware::ATTRIBUTE . '%] %channel%.%level_name% - %message%',
|
||||
],
|
||||
];
|
||||
|
||||
// In dev env or the docker container, stream Shlink logs to stderr, otherwise send them to a file
|
||||
$useStreamForShlinkLogger = $isDev || env('SHLINK_RUNTIME') !== null;
|
||||
|
||||
return [
|
||||
|
||||
'logger' => [
|
||||
'Shlink' => [
|
||||
'Shlink' => $useStreamForShlinkLogger ? [
|
||||
'type' => LoggerType::STREAM->value,
|
||||
'destination' => 'php://stderr',
|
||||
...$buildCommonConfig(),
|
||||
] : [
|
||||
'type' => LoggerType::FILE->value,
|
||||
...$common,
|
||||
...$buildCommonConfig(),
|
||||
],
|
||||
'Access' => [
|
||||
'type' => LoggerType::STREAM->value,
|
||||
'destination' => 'php://stderr',
|
||||
'add_new_line' => ! runningInRoadRunner(),
|
||||
...$common,
|
||||
...$buildCommonConfig(! runningInRoadRunner()),
|
||||
],
|
||||
],
|
||||
|
||||
|
||||
@ -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,32 @@ use Shlinkio\Shlink\Core\Config\EnvVars;
|
||||
use Symfony\Component\Mercure\Hub;
|
||||
use Symfony\Component\Mercure\HubInterface;
|
||||
|
||||
return (static function (): array {
|
||||
$publicUrl = EnvVars::MERCURE_PUBLIC_HUB_URL->loadFromEnv();
|
||||
return [
|
||||
|
||||
return [
|
||||
// This config is used by shlink-common. Do not delete
|
||||
'mercure' => [
|
||||
'enabled' => EnvVars::MERCURE_ENABLED->loadFromEnv(),
|
||||
'public_hub_url' => EnvVars::MERCURE_PUBLIC_HUB_URL->loadFromEnv(),
|
||||
'internal_hub_url' => EnvVars::MERCURE_INTERNAL_HUB_URL->loadFromEnv(),
|
||||
'jwt_secret' => EnvVars::MERCURE_JWT_SECRET->loadFromEnv(),
|
||||
'jwt_issuer' => 'Shlink',
|
||||
],
|
||||
|
||||
'mercure' => [
|
||||
'public_hub_url' => $publicUrl,
|
||||
'internal_hub_url' => EnvVars::MERCURE_INTERNAL_HUB_URL->loadFromEnv($publicUrl),
|
||||
'jwt_secret' => EnvVars::MERCURE_JWT_SECRET->loadFromEnv(),
|
||||
'jwt_issuer' => 'Shlink',
|
||||
],
|
||||
|
||||
'dependencies' => [
|
||||
'delegators' => [
|
||||
LcobucciJwtProvider::class => [
|
||||
LazyServiceFactory::class,
|
||||
],
|
||||
Hub::class => [
|
||||
LazyServiceFactory::class,
|
||||
],
|
||||
'dependencies' => [
|
||||
'delegators' => [
|
||||
LcobucciJwtProvider::class => [
|
||||
LazyServiceFactory::class,
|
||||
],
|
||||
'lazy_services' => [
|
||||
'class_map' => [
|
||||
LcobucciJwtProvider::class => LcobucciJwtProvider::class,
|
||||
Hub::class => HubInterface::class,
|
||||
],
|
||||
Hub::class => [
|
||||
LazyServiceFactory::class,
|
||||
],
|
||||
],
|
||||
'lazy_services' => [
|
||||
'class_map' => [
|
||||
LcobucciJwtProvider::class => LcobucciJwtProvider::class,
|
||||
Hub::class => HubInterface::class,
|
||||
],
|
||||
],
|
||||
],
|
||||
|
||||
];
|
||||
})();
|
||||
];
|
||||
|
||||
@ -1,13 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
return [
|
||||
|
||||
'mercure' => [
|
||||
'public_hub_url' => 'http://localhost:8002',
|
||||
'internal_hub_url' => 'http://shlink_mercure_proxy',
|
||||
'jwt_secret' => 'mercure_jwt_key_long_enough_to_avoid_error',
|
||||
],
|
||||
|
||||
];
|
||||
@ -11,6 +11,7 @@ use RKA\Middleware\IpAddress;
|
||||
use Shlinkio\Shlink\Common\Middleware\AccessLogMiddleware;
|
||||
use Shlinkio\Shlink\Common\Middleware\ContentLengthMiddleware;
|
||||
use Shlinkio\Shlink\Common\Middleware\RequestIdMiddleware;
|
||||
use Shlinkio\Shlink\Core\Geolocation\Middleware\IpGeolocationMiddleware;
|
||||
|
||||
return [
|
||||
|
||||
@ -67,8 +68,11 @@ return [
|
||||
],
|
||||
'not-found' => [
|
||||
'middleware' => [
|
||||
// This middleware is in front of tracking actions explicitly. Putting here for orphan visits tracking
|
||||
// These two middlewares are in front of other tracking actions.
|
||||
// Putting them here for orphan visits tracking
|
||||
IpAddress::class,
|
||||
IpGeolocationMiddleware::class,
|
||||
|
||||
Core\ErrorHandler\NotFoundTypeResolverMiddleware::class,
|
||||
Core\ShortUrl\Middleware\ExtraPathRedirectMiddleware::class,
|
||||
Core\ErrorHandler\NotFoundTrackerMiddleware::class,
|
||||
|
||||
@ -1,36 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Shlinkio\Shlink\Core\Config\EnvVars;
|
||||
|
||||
use const Shlinkio\Shlink\DEFAULT_QR_CODE_BG_COLOR;
|
||||
use const Shlinkio\Shlink\DEFAULT_QR_CODE_COLOR;
|
||||
use const Shlinkio\Shlink\DEFAULT_QR_CODE_ENABLED_FOR_DISABLED_SHORT_URLS;
|
||||
use const Shlinkio\Shlink\DEFAULT_QR_CODE_ERROR_CORRECTION;
|
||||
use const Shlinkio\Shlink\DEFAULT_QR_CODE_FORMAT;
|
||||
use const Shlinkio\Shlink\DEFAULT_QR_CODE_MARGIN;
|
||||
use const Shlinkio\Shlink\DEFAULT_QR_CODE_ROUND_BLOCK_SIZE;
|
||||
use const Shlinkio\Shlink\DEFAULT_QR_CODE_SIZE;
|
||||
|
||||
return [
|
||||
|
||||
'qr_codes' => [
|
||||
'size' => (int) EnvVars::DEFAULT_QR_CODE_SIZE->loadFromEnv(DEFAULT_QR_CODE_SIZE),
|
||||
'margin' => (int) EnvVars::DEFAULT_QR_CODE_MARGIN->loadFromEnv(DEFAULT_QR_CODE_MARGIN),
|
||||
'format' => EnvVars::DEFAULT_QR_CODE_FORMAT->loadFromEnv(DEFAULT_QR_CODE_FORMAT),
|
||||
'error_correction' => EnvVars::DEFAULT_QR_CODE_ERROR_CORRECTION->loadFromEnv(
|
||||
DEFAULT_QR_CODE_ERROR_CORRECTION,
|
||||
),
|
||||
'round_block_size' => (bool) EnvVars::DEFAULT_QR_CODE_ROUND_BLOCK_SIZE->loadFromEnv(
|
||||
DEFAULT_QR_CODE_ROUND_BLOCK_SIZE,
|
||||
),
|
||||
'enabled_for_disabled_short_urls' => (bool) EnvVars::QR_CODE_FOR_DISABLED_SHORT_URLS->loadFromEnv(
|
||||
DEFAULT_QR_CODE_ENABLED_FOR_DISABLED_SHORT_URLS,
|
||||
),
|
||||
'color' => EnvVars::DEFAULT_QR_CODE_COLOR->loadFromEnv(DEFAULT_QR_CODE_COLOR),
|
||||
'bg_color' => EnvVars::DEFAULT_QR_CODE_BG_COLOR->loadFromEnv(DEFAULT_QR_CODE_BG_COLOR),
|
||||
'logo_url' => EnvVars::DEFAULT_QR_CODE_LOGO_URL->loadFromEnv(),
|
||||
],
|
||||
|
||||
];
|
||||
@ -6,14 +6,15 @@ use Shlinkio\Shlink\Core\Config\EnvVars;
|
||||
|
||||
return [
|
||||
|
||||
// This config is used by shlink-common. Do not delete
|
||||
'rabbitmq' => [
|
||||
'enabled' => (bool) EnvVars::RABBITMQ_ENABLED->loadFromEnv(false),
|
||||
'enabled' => (bool) EnvVars::RABBITMQ_ENABLED->loadFromEnv(),
|
||||
'host' => EnvVars::RABBITMQ_HOST->loadFromEnv(),
|
||||
'use_ssl' => (bool) EnvVars::RABBITMQ_USE_SSL->loadFromEnv(false),
|
||||
'port' => (int) EnvVars::RABBITMQ_PORT->loadFromEnv('5672'),
|
||||
'use_ssl' => (bool) EnvVars::RABBITMQ_USE_SSL->loadFromEnv(),
|
||||
'port' => (int) EnvVars::RABBITMQ_PORT->loadFromEnv(),
|
||||
'user' => EnvVars::RABBITMQ_USER->loadFromEnv(),
|
||||
'password' => EnvVars::RABBITMQ_PASSWORD->loadFromEnv(),
|
||||
'vhost' => EnvVars::RABBITMQ_VHOST->loadFromEnv('/'),
|
||||
'vhost' => EnvVars::RABBITMQ_VHOST->loadFromEnv(),
|
||||
],
|
||||
|
||||
];
|
||||
|
||||
@ -1,15 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
return [
|
||||
|
||||
'rabbitmq' => [
|
||||
'enabled' => true,
|
||||
'host' => 'shlink_rabbitmq',
|
||||
'port' => '5672',
|
||||
'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',
|
||||
],
|
||||
],
|
||||
|
||||
];
|
||||
@ -8,12 +8,12 @@ use Shlinkio\Shlink\Core\Config\EnvVars;
|
||||
return [
|
||||
|
||||
'router' => [
|
||||
'base_path' => EnvVars::BASE_PATH->loadFromEnv(''),
|
||||
'base_path' => EnvVars::BASE_PATH->loadFromEnv(),
|
||||
|
||||
'fastroute' => [
|
||||
// Disabling config cache for cli, ensures it's never used for RoadRunner, and also that console
|
||||
// commands don't generate a cache file that's then used by php-fpm web executions
|
||||
FastRouteRouter::CONFIG_CACHE_ENABLED => PHP_SAPI !== 'cli',
|
||||
FastRouteRouter::CONFIG_CACHE_ENABLED => EnvVars::isProdEnv() && PHP_SAPI !== 'cli',
|
||||
FastRouteRouter::CONFIG_CACHE_FILE => 'data/cache/fastroute_cached_routes.php',
|
||||
],
|
||||
],
|
||||
|
||||
@ -1,16 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Mezzio\Router\FastRouteRouter;
|
||||
|
||||
return [
|
||||
|
||||
'router' => [
|
||||
// 'base_path' => '',
|
||||
'fastroute' => [
|
||||
FastRouteRouter::CONFIG_CACHE_ENABLED => false,
|
||||
],
|
||||
],
|
||||
|
||||
];
|
||||
@ -8,6 +8,7 @@ use Fig\Http\Message\RequestMethodInterface;
|
||||
use RKA\Middleware\IpAddress;
|
||||
use Shlinkio\Shlink\Core\Action as CoreAction;
|
||||
use Shlinkio\Shlink\Core\Config\EnvVars;
|
||||
use Shlinkio\Shlink\Core\Geolocation\Middleware\IpGeolocationMiddleware;
|
||||
use Shlinkio\Shlink\Core\ShortUrl\Middleware\TrimTrailingSlashMiddleware;
|
||||
use Shlinkio\Shlink\Rest\Action;
|
||||
use Shlinkio\Shlink\Rest\ConfigProvider;
|
||||
@ -19,9 +20,7 @@ use function sprintf;
|
||||
return (static function (): array {
|
||||
$dropDomainMiddleware = Middleware\ShortUrl\DropDefaultDomainFromRequestMiddleware::class;
|
||||
$overrideDomainMiddleware = Middleware\ShortUrl\OverrideDomainMiddleware::class;
|
||||
|
||||
// TODO This should be based on config, not the env var
|
||||
$shortUrlRouteSuffix = EnvVars::SHORT_URL_TRAILING_SLASH->loadFromEnv(false) ? '[/]' : '';
|
||||
$shortUrlRouteSuffix = EnvVars::SHORT_URL_TRAILING_SLASH->loadFromEnv() ? '[/]' : '';
|
||||
|
||||
return [
|
||||
|
||||
@ -90,23 +89,17 @@ return (static function (): array {
|
||||
'path' => '/{shortCode}/track',
|
||||
'middleware' => [
|
||||
IpAddress::class,
|
||||
IpGeolocationMiddleware::class,
|
||||
CoreAction\PixelAction::class,
|
||||
],
|
||||
'allowed_methods' => [RequestMethodInterface::METHOD_GET],
|
||||
],
|
||||
[
|
||||
'name' => CoreAction\QrCodeAction::class,
|
||||
'path' => '/{shortCode}/qr-code',
|
||||
'middleware' => [
|
||||
CoreAction\QrCodeAction::class,
|
||||
],
|
||||
'allowed_methods' => [RequestMethodInterface::METHOD_GET],
|
||||
],
|
||||
[
|
||||
'name' => CoreAction\RedirectAction::class,
|
||||
'path' => sprintf('/{shortCode}%s', $shortUrlRouteSuffix),
|
||||
'middleware' => [
|
||||
IpAddress::class,
|
||||
IpGeolocationMiddleware::class,
|
||||
TrimTrailingSlashMiddleware::class,
|
||||
CoreAction\RedirectAction::class,
|
||||
],
|
||||
|
||||
@ -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::tryFrom($modeFromEnv) ?? ShortUrlMode::STRICT;
|
||||
|
||||
return [
|
||||
|
||||
'url_shortener' => [
|
||||
'domain' => [ // TODO Refactor this structure to url_shortener.schema and url_shortener.default_domain
|
||||
'schema' => ((bool) EnvVars::IS_HTTPS_ENABLED->loadFromEnv(true)) ? 'https' : 'http',
|
||||
'hostname' => EnvVars::DEFAULT_DOMAIN->loadFromEnv(''),
|
||||
],
|
||||
'default_short_codes_length' => $shortCodesLength,
|
||||
'auto_resolve_titles' => (bool) EnvVars::AUTO_RESOLVE_TITLES->loadFromEnv(true),
|
||||
'append_extra_path' => (bool) EnvVars::REDIRECT_APPEND_EXTRA_PATH->loadFromEnv(false),
|
||||
'multi_segment_slugs_enabled' => (bool) EnvVars::MULTI_SEGMENT_SLUGS_ENABLED->loadFromEnv(false),
|
||||
'trailing_slash_enabled' => (bool) EnvVars::SHORT_URL_TRAILING_SLASH->loadFromEnv(false),
|
||||
'mode' => $mode,
|
||||
],
|
||||
|
||||
];
|
||||
})();
|
||||
@ -1,21 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use function Shlinkio\Shlink\Config\runningInRoadRunner;
|
||||
|
||||
return [
|
||||
|
||||
'url_shortener' => [
|
||||
'domain' => [
|
||||
'schema' => 'http',
|
||||
'hostname' => sprintf('localhost:%s', match (true) {
|
||||
runningInRoadRunner() => '8800',
|
||||
default => '8000',
|
||||
}),
|
||||
],
|
||||
// 'multi_segment_slugs_enabled' => true,
|
||||
// 'trailing_slash_enabled' => true,
|
||||
],
|
||||
|
||||
];
|
||||
@ -8,18 +8,10 @@ use Laminas\ConfigAggregator;
|
||||
use Laminas\Diactoros;
|
||||
use Mezzio;
|
||||
use Mezzio\ProblemDetails;
|
||||
use Shlinkio\Shlink\Config\ConfigAggregator\EnvVarLoaderProvider;
|
||||
use Shlinkio\Shlink\Core\Config\EnvVars;
|
||||
|
||||
use function Shlinkio\Shlink\Config\env;
|
||||
use function Shlinkio\Shlink\Core\enumValues;
|
||||
|
||||
$isTestEnv = env('APP_ENV') === 'test';
|
||||
|
||||
return (new ConfigAggregator\ConfigAggregator(
|
||||
return new ConfigAggregator\ConfigAggregator(
|
||||
providers: [
|
||||
! $isTestEnv
|
||||
? new EnvVarLoaderProvider('config/params/generated_config.php', enumValues(Core\Config\EnvVars::class))
|
||||
: new ConfigAggregator\ArrayProvider([]),
|
||||
Mezzio\ConfigProvider::class,
|
||||
Mezzio\Router\ConfigProvider::class,
|
||||
Mezzio\Router\FastRouteRouter\ConfigProvider::class,
|
||||
@ -34,10 +26,10 @@ return (new ConfigAggregator\ConfigAggregator(
|
||||
CLI\ConfigProvider::class,
|
||||
Rest\ConfigProvider::class,
|
||||
new ConfigAggregator\PhpFileProvider('config/autoload/{,*.}global.php'),
|
||||
// Local config should not be loaded during tests, whereas test config should be loaded ONLY during tests
|
||||
new ConfigAggregator\PhpFileProvider(
|
||||
$isTestEnv ? 'config/test/*.global.php' : 'config/autoload/{,*.}local.php',
|
||||
),
|
||||
// Test config should be loaded ONLY during tests
|
||||
EnvVars::isTestEnv()
|
||||
? new ConfigAggregator\PhpFileProvider('config/test/*.global.php')
|
||||
: new ConfigAggregator\ArrayProvider([]),
|
||||
// Routes have to be loaded last
|
||||
new ConfigAggregator\PhpFileProvider('config/autoload/routes.config.php'),
|
||||
],
|
||||
@ -47,4 +39,4 @@ return (new ConfigAggregator\ConfigAggregator(
|
||||
Core\Config\PostProcessor\MultiSegmentSlugProcessor::class,
|
||||
Core\Config\PostProcessor\ShortUrlMethodsProcessor::class,
|
||||
],
|
||||
))->getMergedConfig();
|
||||
)->getMergedConfig();
|
||||
|
||||
@ -11,13 +11,30 @@ const DEFAULT_SHORT_CODES_LENGTH = 5;
|
||||
const MIN_SHORT_CODES_LENGTH = 4;
|
||||
const DEFAULT_REDIRECT_STATUS_CODE = RedirectStatus::STATUS_302;
|
||||
const DEFAULT_REDIRECT_CACHE_LIFETIME = 30;
|
||||
const DEFAULT_REDIRECT_CACHE_VISIBILITY = 'private';
|
||||
const LOCAL_LOCK_FACTORY = 'Shlinkio\Shlink\LocalLockFactory';
|
||||
const LOOSE_URI_MATCHER = '/(.+)\:(.+)/i'; // Matches anything starting with a schema.
|
||||
const DEFAULT_QR_CODE_SIZE = 300;
|
||||
const DEFAULT_QR_CODE_MARGIN = 0;
|
||||
const DEFAULT_QR_CODE_FORMAT = 'png';
|
||||
const DEFAULT_QR_CODE_ERROR_CORRECTION = 'l';
|
||||
const DEFAULT_QR_CODE_ROUND_BLOCK_SIZE = true;
|
||||
const DEFAULT_QR_CODE_ENABLED_FOR_DISABLED_SHORT_URLS = true;
|
||||
const DEFAULT_QR_CODE_COLOR = '#000000'; // Black
|
||||
const DEFAULT_QR_CODE_BG_COLOR = '#ffffff'; // White
|
||||
const IP_ADDRESS_REQUEST_ATTRIBUTE = 'remote_address';
|
||||
const REDIRECT_URL_REQUEST_ATTRIBUTE = 'redirect_url';
|
||||
|
||||
/**
|
||||
* List of ISO 3166-1 alpha-2 two-letter country codes https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2
|
||||
*/
|
||||
const ISO_COUNTRY_CODES = [
|
||||
'AF', 'AX', 'AL', 'DZ', 'AS', 'AD', 'AO', 'AI', 'AQ', 'AG', 'AR', 'AM', 'AW', 'AU', 'AT', 'AZ',
|
||||
'BS', 'BH', 'BD', 'BB', 'BY', 'BE', 'BZ', 'BJ', 'BM', 'BT', 'BO', 'BQ', 'BA', 'BW', 'BV', 'BR',
|
||||
'IO', 'BN', 'BG', 'BF', 'BI', 'CV', 'KH', 'CM', 'CA', 'KY', 'CF', 'TD', 'CL', 'CN', 'CX', 'CC',
|
||||
'CO', 'KM', 'CG', 'CD', 'CK', 'CR', 'CI', 'HR', 'CU', 'CW', 'CY', 'CZ', 'DK', 'DJ', 'DM', 'DO',
|
||||
'EC', 'EG', 'SV', 'GQ', 'ER', 'EE', 'SZ', 'ET', 'FK', 'FO', 'FJ', 'FI', 'FR', 'GF', 'PF', 'TF',
|
||||
'GA', 'GM', 'GE', 'DE', 'GH', 'GI', 'GR', 'GL', 'GD', 'GP', 'GU', 'GT', 'GG', 'GN', 'GW', 'GY',
|
||||
'HT', 'HM', 'VA', 'HN', 'HK', 'HU', 'IS', 'IN', 'ID', 'IR', 'IQ', 'IE', 'IM', 'IL', 'IT', 'JM',
|
||||
'JP', 'JE', 'JO', 'KZ', 'KE', 'KI', 'KP', 'KR', 'KW', 'KG', 'LA', 'LV', 'LB', 'LS', 'LR', 'LY',
|
||||
'LI', 'LT', 'LU', 'MO', 'MG', 'MW', 'MY', 'MV', 'ML', 'MT', 'MH', 'MQ', 'MR', 'MU', 'YT', 'MX',
|
||||
'FM', 'MD', 'MC', 'MN', 'ME', 'MS', 'MA', 'MZ', 'MM', 'NA', 'NR', 'NP', 'NL', 'NC', 'NZ', 'NI',
|
||||
'NE', 'NG', 'NU', 'NF', 'MK', 'MP', 'NO', 'OM', 'PK', 'PW', 'PS', 'PA', 'PG', 'PY', 'PE', 'PH',
|
||||
'PN', 'PL', 'PT', 'PR', 'QA', 'RE', 'RO', 'RU', 'RW', 'BL', 'SH', 'KN', 'LC', 'MF', 'PM', 'VC',
|
||||
'WS', 'SM', 'ST', 'SA', 'SN', 'RS', 'SC', 'SL', 'SG', 'SX', 'SK', 'SI', 'SB', 'SO', 'ZA', 'GS',
|
||||
'SS', 'ES', 'LK', 'SD', 'SR', 'SJ', 'SE', 'CH', 'SY', 'TW', 'TJ', 'TZ', 'TH', 'TL', 'TG', 'TK',
|
||||
'TO', 'TT', 'TN', 'TR', 'TM', 'TC', 'TV', 'UG', 'UA', 'AE', 'GB', 'US', 'UM', 'UY', 'UZ', 'VU',
|
||||
'VE', 'VN', 'VG', 'VI', 'WF', 'EH', 'YE', 'ZM', 'ZW',
|
||||
];
|
||||
|
||||
@ -6,16 +6,29 @@ use Laminas\ServiceManager\ServiceManager;
|
||||
use Shlinkio\Shlink\Core\Config\EnvVars;
|
||||
use Symfony\Component\Lock;
|
||||
|
||||
use function Shlinkio\Shlink\Config\loadEnvVarsFromConfig;
|
||||
use function Shlinkio\Shlink\Core\enumValues;
|
||||
|
||||
use const Shlinkio\Shlink\LOCAL_LOCK_FACTORY;
|
||||
|
||||
// Set current directory to the project's root directory
|
||||
chdir(dirname(__DIR__));
|
||||
|
||||
require 'vendor/autoload.php';
|
||||
|
||||
// Set a default memory limit, but allow custom values
|
||||
ini_set('memory_limit', EnvVars::MEMORY_LIMIT->loadFromEnv('512M'));
|
||||
// This is one of the first files loaded. Configure the timezone here
|
||||
date_default_timezone_set(EnvVars::TIMEZONE->loadFromEnv(date_default_timezone_get()));
|
||||
// Promote env vars from installer, dev config or test config
|
||||
loadEnvVarsFromConfig(
|
||||
EnvVars::isTestEnv() ? 'config/test/shlink_test_env.php' : 'config/params/*.php',
|
||||
enumValues(EnvVars::class),
|
||||
);
|
||||
|
||||
// This is one of the first files loaded. Set global configuration here
|
||||
error_reporting(
|
||||
// Set a less strict error reporting for prod, where deprecation warnings should be ignored
|
||||
EnvVars::isProdEnv() ? E_ALL & ~E_DEPRECATED & ~E_USER_DEPRECATED : E_ALL,
|
||||
);
|
||||
ini_set('memory_limit', EnvVars::MEMORY_LIMIT->loadFromEnv());
|
||||
date_default_timezone_set(EnvVars::TIMEZONE->loadFromEnv());
|
||||
|
||||
// This class alias tricks the ConfigAbstractFactory to return Lock\Factory instances even with a different service name
|
||||
// It needs to be placed here as individual config files will not be loaded once config is cached
|
||||
|
||||
1
config/params/.gitignore
vendored
1
config/params/.gitignore
vendored
@ -1,2 +1,3 @@
|
||||
*
|
||||
!.gitignore
|
||||
!*.dist
|
||||
|
||||
79
config/params/shlink_dev_env.php.dist
Normal file
79
config/params/shlink_dev_env.php.dist
Normal file
@ -0,0 +1,79 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Shlinkio\Shlink\Core\Config\EnvVars;
|
||||
|
||||
use function Shlinkio\Shlink\Config\runningInRoadRunner;
|
||||
|
||||
return [
|
||||
|
||||
EnvVars::APP_ENV->value => 'dev',
|
||||
// EnvVars::GEOLITE_LICENSE_KEY->value => '',
|
||||
|
||||
// URL shortener
|
||||
EnvVars::DEFAULT_DOMAIN->value => runningInRoadRunner() ? 'localhost:8800' : 'localhost:8008',
|
||||
EnvVars::IS_HTTPS_ENABLED->value => false,
|
||||
|
||||
// Database - MySQL
|
||||
EnvVars::DB_DRIVER->value => 'mysql',
|
||||
EnvVars::DB_USER->value => 'root',
|
||||
EnvVars::DB_PASSWORD->value => 'root',
|
||||
EnvVars::DB_NAME->value => 'shlink',
|
||||
// EnvVars::DB_NAME->value => 'shlink_foo',
|
||||
EnvVars::DB_HOST->value => 'shlink_db_mysql',
|
||||
|
||||
// Database - Maria
|
||||
// EnvVars::DB_DRIVER->value => 'maria',
|
||||
// EnvVars::DB_USER->value => 'root',
|
||||
// EnvVars::DB_PASSWORD->value => 'root',
|
||||
// EnvVars::DB_NAME->value => 'shlink_foo',
|
||||
// EnvVars::DB_HOST->value => 'shlink_db_maria',
|
||||
|
||||
// Database - Postgres
|
||||
// EnvVars::DB_DRIVER->value => 'postgres',
|
||||
// EnvVars::DB_USER->value => 'postgres',
|
||||
// EnvVars::DB_PASSWORD->value => 'root',
|
||||
// EnvVars::DB_NAME->value => 'shlink_foo',
|
||||
// EnvVars::DB_HOST->value => 'shlink_db_postgres',
|
||||
|
||||
// Database - MSSQL
|
||||
// EnvVars::DB_DRIVER->value => 'mssql',
|
||||
// EnvVars::DB_USER->value => 'sa',
|
||||
// EnvVars::DB_PASSWORD->value => 'Passw0rd!',
|
||||
// EnvVars::DB_NAME->value => 'shlink_foo',
|
||||
// EnvVars::DB_HOST->value => 'shlink_db_ms',
|
||||
|
||||
// Matomo
|
||||
// Dev matomo instance needs to be manually configured once before enabling the configuration below:
|
||||
// 1. Go to http://localhost:8003 and follow the installation instructions.
|
||||
// 2. Open data/infra/matomo/config/config.ini.php and replace `trusted_hosts[] = "localhost"` with
|
||||
// `trusted_hosts[] = "localhost:8003"` (see https://github.com/matomo-org/matomo/issues/9549)
|
||||
// 3. Go to http://localhost:8003/index.php?module=SitesManager&action=index and paste the ID for the site you just
|
||||
// created into the `MATOMO_SITE_ID` var below.
|
||||
// 4. Go to http://localhost:8003/index.php?module=UsersManager&action=userSecurity, scroll down, click
|
||||
// "Create new token" and once generated, paste the token into the `MATOMO_API_TOKEN` var below.
|
||||
// 5. Copy the config below and paste it in a new shlink-dev.local.env file.
|
||||
EnvVars::MATOMO_ENABLED->value => false,
|
||||
EnvVars::MATOMO_BASE_URL->value => 'http://shlink_matomo',
|
||||
// EnvVars::MATOMO_SITE_ID->value => ,
|
||||
// EnvVars::MATOMO_API_TOKEN->value => ,
|
||||
|
||||
// Mercure
|
||||
EnvVars::MERCURE_ENABLED->value => true,
|
||||
EnvVars::MERCURE_PUBLIC_HUB_URL->value => 'http://localhost:8002',
|
||||
EnvVars::MERCURE_INTERNAL_HUB_URL->value => 'http://shlink_mercure_proxy',
|
||||
EnvVars::MERCURE_JWT_SECRET->value => 'mercure_jwt_key_long_enough_to_avoid_error',
|
||||
|
||||
// RabbitMQ
|
||||
EnvVars::RABBITMQ_ENABLED->value => true,
|
||||
EnvVars::RABBITMQ_HOST->value => 'shlink_rabbitmq',
|
||||
EnvVars::RABBITMQ_PORT->value => 5672,
|
||||
EnvVars::RABBITMQ_USER->value => 'rabbit',
|
||||
EnvVars::RABBITMQ_PASSWORD->value => 'rabbit',
|
||||
|
||||
// Redis
|
||||
EnvVars::REDIS_PUB_SUB_ENABLED->value => true,
|
||||
EnvVars::REDIS_SERVERS->value => 'tcp://shlink_redis:6379',
|
||||
|
||||
];
|
||||
@ -30,13 +30,17 @@ jobs:
|
||||
prefetch: 10
|
||||
|
||||
logs:
|
||||
encoding: console
|
||||
mode: development
|
||||
channels:
|
||||
http:
|
||||
mode: 'off' # Disable logging as Shlink handles it internally
|
||||
server:
|
||||
encoding: console
|
||||
level: info
|
||||
metrics:
|
||||
encoding: console
|
||||
level: debug
|
||||
jobs:
|
||||
encoding: console
|
||||
level: debug
|
||||
|
||||
@ -35,15 +35,16 @@ jobs:
|
||||
prefetch: 10
|
||||
|
||||
logs:
|
||||
encoding: json
|
||||
encoding: console
|
||||
mode: development
|
||||
channels:
|
||||
http:
|
||||
mode: 'off' # Disable logging as Shlink handles it internally
|
||||
server:
|
||||
encoding: json
|
||||
encoding: console
|
||||
level: info
|
||||
metrics:
|
||||
level: panic
|
||||
jobs:
|
||||
encoding: console
|
||||
level: panic
|
||||
|
||||
@ -7,18 +7,20 @@ server:
|
||||
command: 'php -dopcache.enable_cli=1 -dopcache.validate_timestamps=0 ../../bin/roadrunner-worker.php'
|
||||
|
||||
http:
|
||||
address: '0.0.0.0:${PORT:-8080}'
|
||||
address: '${ADDRESS:-0.0.0.0}:${PORT:-8080}'
|
||||
middleware: ['static']
|
||||
static:
|
||||
dir: '../../public'
|
||||
forbid: ['.php', '.htaccess']
|
||||
pool:
|
||||
num_workers: ${WEB_WORKER_NUM:-0}
|
||||
max_jobs: 250 # Restart worker after processing this amount of requests to mitigate memory leaks
|
||||
|
||||
jobs:
|
||||
timeout: 300 # 5 minutes
|
||||
pool:
|
||||
num_workers: ${TASK_WORKER_NUM:-0}
|
||||
max_jobs: 250 # Restart worker after processing this amount of jobs to mitigate memory leaks
|
||||
consume: ['shlink']
|
||||
pipelines:
|
||||
shlink:
|
||||
@ -28,11 +30,14 @@ jobs:
|
||||
prefetch: 10
|
||||
|
||||
logs:
|
||||
encoding: ${LOGS_FORMAT:-console}
|
||||
mode: production
|
||||
channels:
|
||||
http:
|
||||
mode: 'off' # Disable logging as Shlink handles it internally
|
||||
server:
|
||||
encoding: ${LOGS_FORMAT:-console}
|
||||
level: info
|
||||
jobs:
|
||||
encoding: ${LOGS_FORMAT:-console}
|
||||
level: debug
|
||||
|
||||
@ -11,5 +11,11 @@ const ANDROID_USER_AGENT = 'Mozilla/5.0 (Linux; Android 13) AppleWebKit/537.36 (
|
||||
. 'Chrome/109.0.5414.86 Mobile Safari/537.36';
|
||||
const IOS_USER_AGENT = 'Mozilla/5.0 (iPhone; CPU iPhone OS 16_2 like Mac OS X) AppleWebKit/605.1.15 '
|
||||
. '(KHTML, like Gecko) FxiOS/109.0 Mobile/15E148 Safari/605.1.15';
|
||||
const DESKTOP_USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like '
|
||||
. 'Gecko) Chrome/109.0.0.0 Safari/537.36 Edg/109.0.1518.61';
|
||||
const WINDOWS_USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) '
|
||||
. 'Chrome/138.0.0.0 Safari/537.36 Edg/138.0.3351.95';
|
||||
const LINUX_USER_AGENT = 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) '
|
||||
. 'HeadlessChrome/81.0.4044.113 Safari/537.36';
|
||||
const MACOS_USER_AGENT = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 15_5) AppleWebKit/605.1.15 (KHTML, like Gecko) '
|
||||
. 'Version/18.4 Safari/605.1.15';
|
||||
const CHROMEOS_USER_AGENT = 'Mozilla/5.0 (X11; CrOS x86_64 16181.61.0) AppleWebKit/537.36 (KHTML, like Gecko) '
|
||||
. 'Chrome/134.0.6998.198 Safari/537.36';
|
||||
|
||||
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,
|
||||
|
||||
];
|
||||
@ -93,13 +93,6 @@ return [
|
||||
ConfigAggregator::ENABLE_CACHE => false,
|
||||
FastRouteRouter::CONFIG_CACHE_ENABLED => false,
|
||||
|
||||
'url_shortener' => [
|
||||
'domain' => [
|
||||
'schema' => 'http',
|
||||
'hostname' => 's.test',
|
||||
],
|
||||
],
|
||||
|
||||
'routes' => [
|
||||
// This route is used to test that title resolution is skipped if the long URL times out
|
||||
[
|
||||
@ -120,13 +113,6 @@ return [
|
||||
],
|
||||
],
|
||||
|
||||
// Disable mercure integration during E2E tests
|
||||
'mercure' => [
|
||||
'public_hub_url' => null,
|
||||
'internal_hub_url' => null,
|
||||
'jwt_secret' => null,
|
||||
],
|
||||
|
||||
'dependencies' => [
|
||||
'services' => [
|
||||
'shlink_test_api_client' => new Client([
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
set -ex
|
||||
|
||||
curl https://packages.microsoft.com/keys/microsoft.asc | apt-key add -
|
||||
curl https://packages.microsoft.com/config/ubuntu/22.04/prod.list > /etc/apt/sources.list.d/mssql-release.list
|
||||
curl https://packages.microsoft.com/config/ubuntu/24.04/prod.list > /etc/apt/sources.list.d/mssql-release.list
|
||||
apt-get update
|
||||
ACCEPT_EULA=Y apt-get install msodbcsql18
|
||||
# apt-get install unixodbc-dev
|
||||
|
||||
@ -11,7 +11,7 @@ server {
|
||||
|
||||
location ~ \.php$ {
|
||||
fastcgi_split_path_info ^(.+\.php)(/.+)$;
|
||||
fastcgi_pass unix:/var/run/php/php8.3-fpm.sock;
|
||||
fastcgi_pass unix:/var/run/php/php8.4-fpm.sock;
|
||||
fastcgi_index index.php;
|
||||
include fastcgi.conf;
|
||||
}
|
||||
|
||||
52
data/infra/frankenphp.Dockerfile
Normal file
52
data/infra/frankenphp.Dockerfile
Normal file
@ -0,0 +1,52 @@
|
||||
FROM dunglas/frankenphp:1-php8.4-alpine
|
||||
MAINTAINER Alejandro Celaya <alejandro@alejandrocelaya.com>
|
||||
|
||||
ENV PDO_SQLSRV_VERSION='5.12.0'
|
||||
ENV MS_ODBC_DOWNLOAD='7/6/d/76de322a-d860-4894-9945-f0cc5d6a45f8'
|
||||
ENV MS_ODBC_SQL_VERSION='18_18.4.1.1'
|
||||
|
||||
RUN apk update
|
||||
|
||||
# Install common php extensions
|
||||
RUN docker-php-ext-install pdo_mysql
|
||||
RUN docker-php-ext-install calendar
|
||||
|
||||
RUN apk add --no-cache oniguruma-dev
|
||||
RUN docker-php-ext-install mbstring
|
||||
|
||||
RUN apk add --no-cache sqlite-libs
|
||||
RUN apk add --no-cache sqlite-dev
|
||||
RUN docker-php-ext-install pdo_sqlite
|
||||
|
||||
RUN apk add --no-cache icu-dev
|
||||
RUN docker-php-ext-install intl
|
||||
|
||||
RUN apk add --no-cache libzip-dev zlib-dev
|
||||
RUN docker-php-ext-install zip
|
||||
|
||||
RUN apk add --no-cache postgresql-dev
|
||||
RUN docker-php-ext-install pdo_pgsql
|
||||
|
||||
RUN apk add --no-cache --virtual .phpize-deps $PHPIZE_DEPS linux-headers && \
|
||||
docker-php-ext-install sockets && \
|
||||
apk del .phpize-deps
|
||||
RUN docker-php-ext-install bcmath
|
||||
|
||||
# Install xdebug and sqlsrv driver
|
||||
RUN apk add --update linux-headers && \
|
||||
wget https://download.microsoft.com/download/${MS_ODBC_DOWNLOAD}/msodbcsql${MS_ODBC_SQL_VERSION}-1_amd64.apk && \
|
||||
apk add --allow-untrusted msodbcsql${MS_ODBC_SQL_VERSION}-1_amd64.apk && \
|
||||
apk add --no-cache --virtual .phpize-deps $PHPIZE_DEPS unixodbc-dev && \
|
||||
pecl install pdo_sqlsrv-${PDO_SQLSRV_VERSION} xdebug && \
|
||||
docker-php-ext-enable pdo_sqlsrv xdebug && \
|
||||
apk del .phpize-deps && \
|
||||
rm msodbcsql${MS_ODBC_SQL_VERSION}-1_amd64.apk
|
||||
|
||||
# Install composer
|
||||
COPY --from=composer:2 /usr/bin/composer /usr/local/bin/composer
|
||||
|
||||
# Make home directory writable by anyone
|
||||
RUN chmod 777 /home
|
||||
|
||||
VOLUME /home/shlink
|
||||
WORKDIR /home/shlink
|
||||
2
data/infra/frankenphp_caddy_config/.gitignore
vendored
Executable file
2
data/infra/frankenphp_caddy_config/.gitignore
vendored
Executable file
@ -0,0 +1,2 @@
|
||||
*
|
||||
!.gitignore
|
||||
2
data/infra/frankenphp_caddy_data/.gitignore
vendored
Executable file
2
data/infra/frankenphp_caddy_data/.gitignore
vendored
Executable file
@ -0,0 +1,2 @@
|
||||
*
|
||||
!.gitignore
|
||||
@ -1,10 +1,10 @@
|
||||
FROM php:8.3-fpm-alpine3.19
|
||||
FROM php:8.4-fpm-alpine3.21
|
||||
MAINTAINER Alejandro Celaya <alejandro@alejandrocelaya.com>
|
||||
|
||||
ENV APCU_VERSION 5.1.23
|
||||
ENV PDO_SQLSRV_VERSION 5.12.0
|
||||
ENV MS_ODBC_DOWNLOAD 'b/9/f/b9f3cce4-3925-46d4-9f46-da08869c6486'
|
||||
ENV MS_ODBC_SQL_VERSION 18_18.1.1.1
|
||||
ENV APCU_VERSION='5.1.24'
|
||||
ENV PDO_SQLSRV_VERSION='5.12.0'
|
||||
ENV MS_ODBC_DOWNLOAD='7/6/d/76de322a-d860-4894-9945-f0cc5d6a45f8'
|
||||
ENV MS_ODBC_SQL_VERSION='18_18.4.1.1'
|
||||
|
||||
RUN apk update
|
||||
|
||||
@ -25,9 +25,6 @@ RUN docker-php-ext-install intl
|
||||
RUN apk add --no-cache libzip-dev zlib-dev
|
||||
RUN docker-php-ext-install zip
|
||||
|
||||
RUN apk add --no-cache libpng-dev
|
||||
RUN docker-php-ext-install gd
|
||||
|
||||
RUN apk add --no-cache postgresql-dev
|
||||
RUN docker-php-ext-install pdo_pgsql
|
||||
|
||||
@ -46,12 +43,13 @@ RUN mkdir -p /usr/src/php/ext/apcu \
|
||||
&& rm /usr/local/etc/php/conf.d/docker-php-ext-apcu.ini \
|
||||
&& echo extension=apcu.so > /usr/local/etc/php/conf.d/20-php-ext-apcu.ini
|
||||
|
||||
# Install pcov and sqlsrv driver
|
||||
RUN wget https://download.microsoft.com/download/${MS_ODBC_DOWNLOAD}/msodbcsql${MS_ODBC_SQL_VERSION}-1_amd64.apk && \
|
||||
# Install xdebug and sqlsrv driver
|
||||
RUN apk add --update linux-headers && \
|
||||
wget https://download.microsoft.com/download/${MS_ODBC_DOWNLOAD}/msodbcsql${MS_ODBC_SQL_VERSION}-1_amd64.apk && \
|
||||
apk add --allow-untrusted msodbcsql${MS_ODBC_SQL_VERSION}-1_amd64.apk && \
|
||||
apk add --no-cache --virtual .phpize-deps $PHPIZE_DEPS unixodbc-dev && \
|
||||
pecl install pdo_sqlsrv-${PDO_SQLSRV_VERSION} pcov && \
|
||||
docker-php-ext-enable pdo_sqlsrv pcov && \
|
||||
pecl install pdo_sqlsrv-${PDO_SQLSRV_VERSION} xdebug && \
|
||||
docker-php-ext-enable pdo_sqlsrv xdebug && \
|
||||
apk del .phpize-deps && \
|
||||
rm msodbcsql${MS_ODBC_SQL_VERSION}-1_amd64.apk
|
||||
|
||||
|
||||
@ -1,7 +1,4 @@
|
||||
display_errors=On
|
||||
error_reporting=-1
|
||||
log_errors_max_len=0
|
||||
zend.assertions=1
|
||||
assert.exception=1
|
||||
pcov.enabled=1
|
||||
pcov.directory=module
|
||||
|
||||
@ -1,10 +1,9 @@
|
||||
FROM php:8.3-alpine3.19
|
||||
FROM php:8.4-alpine3.21
|
||||
MAINTAINER Alejandro Celaya <alejandro@alejandrocelaya.com>
|
||||
|
||||
ENV APCU_VERSION 5.1.23
|
||||
ENV PDO_SQLSRV_VERSION 5.12.0
|
||||
ENV MS_ODBC_DOWNLOAD 'b/9/f/b9f3cce4-3925-46d4-9f46-da08869c6486'
|
||||
ENV MS_ODBC_SQL_VERSION 18_18.1.1.1
|
||||
ENV PDO_SQLSRV_VERSION='5.12.0'
|
||||
ENV MS_ODBC_DOWNLOAD='7/6/d/76de322a-d860-4894-9945-f0cc5d6a45f8'
|
||||
ENV MS_ODBC_SQL_VERSION='18_18.4.1.1'
|
||||
|
||||
RUN apk update
|
||||
|
||||
@ -25,9 +24,6 @@ RUN docker-php-ext-install intl
|
||||
RUN apk add --no-cache libzip-dev zlib-dev
|
||||
RUN docker-php-ext-install zip
|
||||
|
||||
RUN apk add --no-cache libpng-dev
|
||||
RUN docker-php-ext-install gd
|
||||
|
||||
RUN apk add --no-cache postgresql-dev
|
||||
RUN docker-php-ext-install pdo_pgsql
|
||||
|
||||
@ -36,22 +32,13 @@ RUN apk add --no-cache --virtual .phpize-deps $PHPIZE_DEPS linux-headers && \
|
||||
apk del .phpize-deps
|
||||
RUN docker-php-ext-install bcmath
|
||||
|
||||
# Install APCu extension
|
||||
ADD https://pecl.php.net/get/apcu-$APCU_VERSION.tgz /tmp/apcu.tar.gz
|
||||
RUN mkdir -p /usr/src/php/ext/apcu \
|
||||
&& tar xf /tmp/apcu.tar.gz -C /usr/src/php/ext/apcu --strip-components=1 \
|
||||
&& docker-php-ext-configure apcu \
|
||||
&& docker-php-ext-install apcu \
|
||||
&& rm /tmp/apcu.tar.gz \
|
||||
&& rm /usr/local/etc/php/conf.d/docker-php-ext-apcu.ini \
|
||||
&& echo extension=apcu.so > /usr/local/etc/php/conf.d/20-php-ext-apcu.ini
|
||||
|
||||
# Install pcov and sqlsrv driver
|
||||
RUN wget https://download.microsoft.com/download/${MS_ODBC_DOWNLOAD}/msodbcsql${MS_ODBC_SQL_VERSION}-1_amd64.apk && \
|
||||
# Install xdebug and sqlsrv driver
|
||||
RUN apk add --update linux-headers && \
|
||||
wget https://download.microsoft.com/download/${MS_ODBC_DOWNLOAD}/msodbcsql${MS_ODBC_SQL_VERSION}-1_amd64.apk && \
|
||||
apk add --allow-untrusted msodbcsql${MS_ODBC_SQL_VERSION}-1_amd64.apk && \
|
||||
apk add --no-cache --virtual .phpize-deps $PHPIZE_DEPS unixodbc-dev && \
|
||||
pecl install pdo_sqlsrv-${PDO_SQLSRV_VERSION} pcov && \
|
||||
docker-php-ext-enable pdo_sqlsrv pcov && \
|
||||
pecl install pdo_sqlsrv-${PDO_SQLSRV_VERSION} xdebug && \
|
||||
docker-php-ext-enable pdo_sqlsrv xdebug && \
|
||||
apk del .phpize-deps && \
|
||||
rm msodbcsql${MS_ODBC_SQL_VERSION}-1_amd64.apk
|
||||
|
||||
@ -72,5 +59,7 @@ CMD \
|
||||
if [[ ! -d "./vendor" ]]; then /usr/local/bin/composer install ; fi && \
|
||||
# Download roadrunner binary
|
||||
if [[ ! -f "./bin/rr" ]]; then ./vendor/bin/rr get --no-interaction --no-config --location bin/ && chmod +x bin/rr ; fi && \
|
||||
# This forces the app to be started every second until the exit code is 0
|
||||
until ./bin/rr serve -c config/roadrunner/.rr.dev.yml; do sleep 1 ; done
|
||||
# Create env file if it does not exist yet
|
||||
if [[ ! -f "./config/params/shlink_dev_env.php" ]]; then cp ./config/params/shlink_dev_env.php.dist ./config/params/shlink_dev_env.php ; fi && \
|
||||
# Run with `exec` so that signals are properly handled
|
||||
exec ./bin/rr serve -c config/roadrunner/.rr.dev.yml
|
||||
|
||||
2
data/temp-geolite/.gitignore
vendored
Executable file
2
data/temp-geolite/.gitignore
vendored
Executable file
@ -0,0 +1,2 @@
|
||||
*
|
||||
!.gitignore
|
||||
@ -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,32 +0,0 @@
|
||||
version: '3'
|
||||
|
||||
services:
|
||||
shlink_php:
|
||||
user: 1000:1000
|
||||
volumes:
|
||||
- /etc/passwd:/etc/passwd:ro
|
||||
- /etc/group:/etc/group:ro
|
||||
|
||||
shlink_roadrunner:
|
||||
user: 1000:1000
|
||||
volumes:
|
||||
- /etc/passwd:/etc/passwd:ro
|
||||
- /etc/group:/etc/group:ro
|
||||
|
||||
shlink_db_mysql:
|
||||
user: 1000:1000
|
||||
volumes:
|
||||
- /etc/passwd:/etc/passwd:ro
|
||||
- /etc/group:/etc/group:ro
|
||||
|
||||
shlink_db_postgres:
|
||||
user: 1000:1000
|
||||
volumes:
|
||||
- /etc/passwd:/etc/passwd:ro
|
||||
- /etc/group:/etc/group:ro
|
||||
|
||||
shlink_db_maria:
|
||||
user: 1000:1000
|
||||
volumes:
|
||||
- /etc/passwd:/etc/passwd:ro
|
||||
- /etc/group:/etc/group:ro
|
||||
@ -1,5 +1,3 @@
|
||||
version: '3'
|
||||
|
||||
services:
|
||||
shlink_nginx:
|
||||
container_name: shlink_nginx
|
||||
@ -15,6 +13,7 @@ services:
|
||||
|
||||
shlink_php:
|
||||
container_name: shlink_php
|
||||
user: 1000:1000
|
||||
build:
|
||||
context: .
|
||||
dockerfile: ./data/infra/php.Dockerfile
|
||||
@ -36,11 +35,13 @@ services:
|
||||
- shlink_matomo
|
||||
environment:
|
||||
LC_ALL: C
|
||||
DEFAULT_DOMAIN: localhost:8000
|
||||
extra_hosts:
|
||||
- 'host.docker.internal:host-gateway'
|
||||
|
||||
shlink_roadrunner:
|
||||
container_name: shlink_roadrunner
|
||||
user: 1000:1000
|
||||
build:
|
||||
context: .
|
||||
dockerfile: ./data/infra/roadrunner.Dockerfile
|
||||
@ -65,8 +66,40 @@ services:
|
||||
extra_hosts:
|
||||
- 'host.docker.internal:host-gateway'
|
||||
|
||||
shlink_frankenphp:
|
||||
container_name: shlink_frankenphp
|
||||
user: 1000:1000
|
||||
build:
|
||||
context: .
|
||||
dockerfile: ./data/infra/frankenphp.Dockerfile
|
||||
ports:
|
||||
- "8008:8008"
|
||||
volumes:
|
||||
- ./:/home/shlink
|
||||
- ./data/infra/php.ini:/usr/local/etc/php/php.ini
|
||||
- ./data/infra/frankenphp_caddy_data:/data
|
||||
- ./data/infra/frankenphp_caddy_config:/config
|
||||
links:
|
||||
- shlink_db_mysql
|
||||
- shlink_db_postgres
|
||||
- shlink_db_maria
|
||||
- shlink_db_ms
|
||||
- shlink_redis
|
||||
- shlink_redis_acl
|
||||
- shlink_mercure
|
||||
- shlink_mercure_proxy
|
||||
- shlink_rabbitmq
|
||||
- shlink_matomo
|
||||
environment:
|
||||
FRANKENPHP_CONFIG: 'worker /home/shlink/bin/frankenphp-worker.php'
|
||||
SERVER_NAME: ':8008 https:8009'
|
||||
extra_hosts:
|
||||
- 'host.docker.internal:host-gateway'
|
||||
tty: true
|
||||
|
||||
shlink_db_mysql:
|
||||
container_name: shlink_db_mysql
|
||||
user: 1000:1000
|
||||
image: mysql:8.0
|
||||
ports:
|
||||
- "3307:3306"
|
||||
@ -79,7 +112,8 @@ services:
|
||||
|
||||
shlink_db_postgres:
|
||||
container_name: shlink_db_postgres
|
||||
image: postgres:12.2-alpine
|
||||
user: 1000:1000
|
||||
image: postgres:16.3-alpine
|
||||
ports:
|
||||
- "5434:5432"
|
||||
volumes:
|
||||
@ -92,6 +126,7 @@ services:
|
||||
|
||||
shlink_db_maria:
|
||||
container_name: shlink_db_maria
|
||||
user: 1000:1000
|
||||
image: mariadb:10.7
|
||||
ports:
|
||||
- "3308:3306"
|
||||
@ -114,13 +149,13 @@ services:
|
||||
|
||||
shlink_redis:
|
||||
container_name: shlink_redis
|
||||
image: redis:6.2-alpine
|
||||
image: redis:7.4-alpine
|
||||
ports:
|
||||
- "6380:6379"
|
||||
|
||||
shlink_redis_acl:
|
||||
container_name: shlink_redis_acl
|
||||
image: redis:6.2-alpine
|
||||
image: redis:7.4-alpine
|
||||
command: ["redis-server", "/usr/local/etc/redis/redis.conf"]
|
||||
ports:
|
||||
- "6382:6379"
|
||||
@ -140,14 +175,14 @@ services:
|
||||
|
||||
shlink_mercure:
|
||||
container_name: shlink_mercure
|
||||
image: dunglas/mercure:v0.15
|
||||
image: dunglas/mercure:v0.18
|
||||
ports:
|
||||
- "3080:80"
|
||||
environment:
|
||||
SERVER_NAME: ":80"
|
||||
MERCURE_PUBLISHER_JWT_KEY: mercure_jwt_key_long_enough_to_avoid_error
|
||||
MERCURE_SUBSCRIBER_JWT_KEY: mercure_jwt_key_long_enough_to_avoid_error
|
||||
MERCURE_EXTRA_DIRECTIVES: "cors_origins https://app.shlink.io http://localhost:3000 http://127.0.0.1:3000"
|
||||
MERCURE_EXTRA_DIRECTIVES: "cors_origins https://app.shlink.io http://localhost:3000 http://127.0.0.1:3000 http://localhost:3002 http://127.0.0.1:3002 http://localhost:3005 http://127.0.0.1:3005"
|
||||
|
||||
shlink_rabbitmq:
|
||||
container_name: shlink_rabbitmq
|
||||
|
||||
@ -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',
|
||||
],
|
||||
],
|
||||
|
||||
];
|
||||
@ -4,22 +4,28 @@ set -e
|
||||
cd /etc/shlink
|
||||
|
||||
# Create data directories if they do not exist. This allows data dir to be mounted as an empty dir if needed
|
||||
mkdir -p data/cache data/locks data/log data/proxies
|
||||
mkdir -p data/cache data/locks data/log data/proxies data/temp-geolite
|
||||
|
||||
flags="--no-interaction --clear-db-cache"
|
||||
|
||||
# Read env vars through Shlink command, so that it applies the `_FILE` env var fallback logic
|
||||
geolite_license_key=$(bin/cli env-var:read GEOLITE_LICENSE_KEY)
|
||||
skip_initial_geolite_download=$(bin/cli env-var:read SKIP_INITIAL_GEOLITE_DOWNLOAD)
|
||||
initial_api_key=$(bin/cli env-var:read INITIAL_API_KEY)
|
||||
|
||||
# Skip downloading GeoLite2 db file if the license key env var was not defined or skipping was explicitly set
|
||||
if [ -z "${GEOLITE_LICENSE_KEY}" ] || [ "${SKIP_INITIAL_GEOLITE_DOWNLOAD}" = "true" ]; then
|
||||
if [ -z "${geolite_license_key}" ] || [ "${skip_initial_geolite_download}" = "true" ]; then
|
||||
flags="${flags} --skip-download-geolite"
|
||||
fi
|
||||
|
||||
# If INITIAL_API_KEY was provided, create an initial API key
|
||||
if [ -n "${INITIAL_API_KEY}" ]; then
|
||||
flags="${flags} --initial-api-key=${INITIAL_API_KEY}"
|
||||
if [ -n "${initial_api_key}" ]; then
|
||||
flags="${flags} --initial-api-key=${initial_api_key}"
|
||||
fi
|
||||
|
||||
php vendor/bin/shlink-installer init ${flags}
|
||||
|
||||
if [ "$SHLINK_RUNTIME" = 'rr' ]; then
|
||||
./bin/rr serve -c config/roadrunner/.rr.yml
|
||||
# Run with `exec` so that signals are properly handled
|
||||
exec ./bin/rr serve -c config/roadrunner/.rr.yml
|
||||
fi
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -141,6 +141,14 @@
|
||||
"crawlable": {
|
||||
"type": "boolean",
|
||||
"description": "Tells if this URL will be included as 'Allow' in Shlink's robots.txt."
|
||||
},
|
||||
"forwardQuery": {
|
||||
"type": "boolean",
|
||||
"description": "Tells if this URL will forward the query params to the long URL when visited, as explained in [the docs](https://shlink.io/documentation/some-features/#query-params-forwarding)."
|
||||
},
|
||||
"hasRedirectRules": {
|
||||
"type": "boolean",
|
||||
"description": "Whether this short URL has redirect rules attached to it or not. Use [this endpoint](https://api-spec.shlink.io/#/Redirect%20rules/listShortUrlRedirectRules) to get the actual list of rules."
|
||||
}
|
||||
},
|
||||
"example": {
|
||||
@ -164,7 +172,9 @@
|
||||
},
|
||||
"domain": "example.com",
|
||||
"title": "The title",
|
||||
"crawlable": false
|
||||
"crawlable": false,
|
||||
"forwardQuery": false,
|
||||
"hasRedirectRules": true
|
||||
}
|
||||
},
|
||||
"ShortUrlMeta": {
|
||||
@ -237,6 +247,11 @@
|
||||
"type": "string",
|
||||
"nullable": true,
|
||||
"description": "The originally visited URL that triggered the tracking of this visit"
|
||||
},
|
||||
"redirectUrl": {
|
||||
"type": "string",
|
||||
"nullable": true,
|
||||
"description": "The URL to which the visitor was redirected, or null if a redirect did not occur, like for 404 requests or pixel tracking"
|
||||
}
|
||||
},
|
||||
"example": {
|
||||
|
||||
@ -15,14 +15,23 @@
|
||||
"properties": {
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": ["device", "language", "query-param"],
|
||||
"description": "The type of the condition, which will condition the logic used to match it"
|
||||
"enum": [
|
||||
"device",
|
||||
"language",
|
||||
"query-param",
|
||||
"any-value-query-param",
|
||||
"valueless-query-param",
|
||||
"ip-address",
|
||||
"geolocation-country-code",
|
||||
"geolocation-city-name"
|
||||
],
|
||||
"description": "The type of the condition, which will determine the logic used to match it"
|
||||
},
|
||||
"matchKey": {
|
||||
"type": ["string", "null"]
|
||||
},
|
||||
"matchValue": {
|
||||
"type": "string"
|
||||
"type": ["string", "null"]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -11,7 +11,8 @@
|
||||
"domain",
|
||||
"title",
|
||||
"crawlable",
|
||||
"forwardQuery"
|
||||
"forwardQuery",
|
||||
"hasRedirectRules"
|
||||
],
|
||||
"properties": {
|
||||
"shortCode": {
|
||||
@ -59,6 +60,10 @@
|
||||
"forwardQuery": {
|
||||
"type": "boolean",
|
||||
"description": "Tells if this URL will forward the query params to the long URL when visited, as explained in [the docs](https://shlink.io/documentation/some-features/#query-params-forwarding)."
|
||||
},
|
||||
"hasRedirectRules": {
|
||||
"type": "boolean",
|
||||
"description": "Whether this short URL has redirect rules attached to it or not. Use [this endpoint](https://api-spec.shlink.io/#/Redirect%20rules/listShortUrlRedirectRules) to get the actual list of rules."
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -25,6 +25,10 @@
|
||||
"visitedUrl": {
|
||||
"type": ["string", "null"],
|
||||
"description": "The originally visited URL that triggered the tracking of this visit"
|
||||
},
|
||||
"redirectUrl": {
|
||||
"type": ["string", "null"],
|
||||
"description": "The URL to which the visitor was redirected, or null if a redirect did not occur, like for 404 requests or pixel tracking"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -31,7 +31,7 @@
|
||||
{
|
||||
"name": "searchTerm",
|
||||
"in": "query",
|
||||
"description": "A query used to filter results by searching for it on the longUrl and shortCode fields. (Since v1.3.0)",
|
||||
"description": "A query used to filter results by searching for it on the longUrl and shortCode fields.",
|
||||
"required": false,
|
||||
"schema": {
|
||||
"type": "string"
|
||||
@ -40,7 +40,7 @@
|
||||
{
|
||||
"name": "tags[]",
|
||||
"in": "query",
|
||||
"description": "A list of tags used to filter the result set. Only short URLs tagged with at least one of the provided tags will be returned. (Since v1.3.0)",
|
||||
"description": "A list of tags used to filter the result set. Only short URLs **with** these tags will be returned.",
|
||||
"required": false,
|
||||
"schema": {
|
||||
"type": "array",
|
||||
@ -52,7 +52,29 @@
|
||||
{
|
||||
"name": "tagsMode",
|
||||
"in": "query",
|
||||
"description": "Tells how the filtering by tags should work, returning short URLs containing \"any\" of the tags, or \"all\" the tags. It's ignored if no tags are provided, and defaults to \"any\" if not provided.",
|
||||
"description": "Tells how the filtering by `tags` should work, returning short URLs containing \"any\" of the tags, or \"all\" the tags. Defaults to \"any\".<br />It's ignored if `tags` is not provided.",
|
||||
"required": false,
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"enum": ["any", "all"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "excludeTags[]",
|
||||
"in": "query",
|
||||
"description": "A list of tags used to filter the result set. Only short URLs **without** these tags will be returned.",
|
||||
"required": false,
|
||||
"schema": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "excludeTagsMode",
|
||||
"in": "query",
|
||||
"description": "Tells how the filtering by `excludeTags` should work, returning short URLs not containing \"any\" of the tags, or not containing \"all\" the tags. Defaults to \"any\".<br />It's ignored if `excludeTags` is not provided.",
|
||||
"required": false,
|
||||
"schema": {
|
||||
"type": "string",
|
||||
@ -125,6 +147,24 @@
|
||||
"false"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "domain",
|
||||
"in": "query",
|
||||
"description": "Get short URLs for this particular domain only. Use **DEFAULT** keyword for default domain.",
|
||||
"required": false,
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "apiKeyName",
|
||||
"in": "query",
|
||||
"description": "Only get short URLs created with this API key.<br />This value is **ignored** if the request is performed with a non-admin API key that does not match this name.",
|
||||
"required": false,
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
],
|
||||
"security": [
|
||||
@ -180,7 +220,9 @@
|
||||
},
|
||||
"domain": null,
|
||||
"title": "Welcome to Steam",
|
||||
"crawlable": false
|
||||
"crawlable": false,
|
||||
"forwardQuery": true,
|
||||
"hasRedirectRules": true
|
||||
},
|
||||
{
|
||||
"shortCode": "12Kb3",
|
||||
@ -202,7 +244,9 @@
|
||||
},
|
||||
"domain": null,
|
||||
"title": null,
|
||||
"crawlable": false
|
||||
"crawlable": false,
|
||||
"forwardQuery": true,
|
||||
"hasRedirectRules": false
|
||||
},
|
||||
{
|
||||
"shortCode": "123bA",
|
||||
@ -222,7 +266,9 @@
|
||||
},
|
||||
"domain": "example.com",
|
||||
"title": null,
|
||||
"crawlable": false
|
||||
"crawlable": false,
|
||||
"forwardQuery": false,
|
||||
"hasRedirectRules": true
|
||||
}
|
||||
],
|
||||
"pagination": {
|
||||
@ -337,7 +383,9 @@
|
||||
},
|
||||
"domain": null,
|
||||
"title": null,
|
||||
"crawlable": false
|
||||
"crawlable": false,
|
||||
"forwardQuery": true,
|
||||
"hasRedirectRules": false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -72,7 +72,9 @@
|
||||
},
|
||||
"domain": null,
|
||||
"title": null,
|
||||
"crawlable": false
|
||||
"crawlable": false,
|
||||
"forwardQuery": true,
|
||||
"hasRedirectRules": false
|
||||
}
|
||||
},
|
||||
"text/plain": {
|
||||
|
||||
@ -50,7 +50,9 @@
|
||||
},
|
||||
"domain": null,
|
||||
"title": null,
|
||||
"crawlable": false
|
||||
"crawlable": false,
|
||||
"forwardQuery": true,
|
||||
"hasRedirectRules": true
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -163,7 +165,9 @@
|
||||
},
|
||||
"domain": null,
|
||||
"title": "Shlink - The URL shortener",
|
||||
"crawlable": false
|
||||
"crawlable": false,
|
||||
"forwardQuery": false,
|
||||
"hasRedirectRules": true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -64,6 +64,10 @@
|
||||
"type": "string",
|
||||
"enum": ["true"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"$ref": "../parameters/domain.json",
|
||||
"description": "Return visits for short URLs that belong to this domain. Use **DEFAULT** keyword to return visits from default domain."
|
||||
}
|
||||
],
|
||||
"security": [
|
||||
|
||||
@ -55,6 +55,10 @@
|
||||
"type": "string",
|
||||
"enum": ["true"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"$ref": "../parameters/domain.json",
|
||||
"description": "Return visits for short URLs that belong to this domain. Use **DEFAULT** keyword to return visits from default domain."
|
||||
}
|
||||
],
|
||||
"security": [
|
||||
|
||||
@ -65,6 +65,10 @@
|
||||
"type": "string",
|
||||
"enum": ["invalid_short_url", "base_url", "regular_404"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"$ref": "../parameters/domain.json",
|
||||
"description": "Return only visits for this domain. Use **DEFAULT** keyword to return visits from default domain."
|
||||
}
|
||||
],
|
||||
"security": [
|
||||
|
||||
@ -1,110 +0,0 @@
|
||||
{
|
||||
"get": {
|
||||
"operationId": "shortUrlQrCode",
|
||||
"tags": [
|
||||
"URL Shortener"
|
||||
],
|
||||
"summary": "Short URL QR code",
|
||||
"description": "Generates a QR code image pointing to a short URL.<br />Since this is not an API endpoint but an image one, when an invalid value is provided for any of the query params, they will fall to their default values instead of throwing an error.",
|
||||
"parameters": [
|
||||
{
|
||||
"$ref": "../parameters/shortCode.json"
|
||||
},
|
||||
{
|
||||
"name": "size",
|
||||
"in": "query",
|
||||
"description": "The size of the image to be returned.",
|
||||
"required": false,
|
||||
"schema": {
|
||||
"type": "integer",
|
||||
"minimum": 50,
|
||||
"maximum": 1000,
|
||||
"default": 300
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "format",
|
||||
"in": "query",
|
||||
"description": "The format for the QR code image, being valid values png and svg. Not providing the param or providing any other value will fall back to png.",
|
||||
"required": false,
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"enum": ["png", "svg"],
|
||||
"default": "png"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "margin",
|
||||
"in": "query",
|
||||
"description": "The margin around the QR code image.",
|
||||
"required": false,
|
||||
"schema": {
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"default": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "errorCorrection",
|
||||
"in": "query",
|
||||
"description": "The error correction level to apply to the QR code: **[L]**ow, **[M]**edium, **[Q]**uartile or **[H]**igh. See [docs](https://www.qrcode.com/en/about/error_correction.html).",
|
||||
"required": false,
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"enum": ["L", "M", "Q", "H"],
|
||||
"default": "L"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "roundBlockSize",
|
||||
"in": "query",
|
||||
"description": "Allows to disable block size rounding, which might reduce the readability of the QR code, but ensures no extra margin is added.",
|
||||
"required": false,
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"enum": ["true", "false"],
|
||||
"default": "false"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "color",
|
||||
"in": "query",
|
||||
"description": "The QR code foreground color. It should be an hex representation of a color, in 3 or 6 characters, optionally preceded by the \"#\" character.",
|
||||
"required": false,
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"default": "#000000"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "bgColor",
|
||||
"in": "query",
|
||||
"description": "The QR code background color. It should be an hex representation of a color, in 3 or 6 characters, optionally preceded by the \"#\" character.",
|
||||
"required": false,
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"default": "#ffffff"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "QR code in PNG format",
|
||||
"content": {
|
||||
"image/png": {
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"format": "binary"
|
||||
}
|
||||
},
|
||||
"image/svg+xml": {
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"format": "binary"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -133,9 +133,6 @@
|
||||
},
|
||||
"/{shortCode}/track": {
|
||||
"$ref": "paths/{shortCode}_track.json"
|
||||
},
|
||||
"/{shortCode}/qr-code": {
|
||||
"$ref": "paths/{shortCode}_qr-code.json"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -9,6 +9,7 @@ return [
|
||||
'cli' => [
|
||||
'commands' => [
|
||||
Command\ShortUrl\CreateShortUrlCommand::NAME => Command\ShortUrl\CreateShortUrlCommand::class,
|
||||
Command\ShortUrl\EditShortUrlCommand::NAME => Command\ShortUrl\EditShortUrlCommand::class,
|
||||
Command\ShortUrl\ResolveUrlCommand::NAME => Command\ShortUrl\ResolveUrlCommand::class,
|
||||
Command\ShortUrl\ListShortUrlsCommand::NAME => Command\ShortUrl\ListShortUrlsCommand::class,
|
||||
Command\ShortUrl\GetShortUrlVisitsCommand::NAME => Command\ShortUrl\GetShortUrlVisitsCommand::class,
|
||||
@ -25,8 +26,10 @@ return [
|
||||
|
||||
Command\Api\GenerateKeyCommand::NAME => Command\Api\GenerateKeyCommand::class,
|
||||
Command\Api\DisableKeyCommand::NAME => Command\Api\DisableKeyCommand::class,
|
||||
Command\Api\DeleteKeyCommand::NAME => Command\Api\DeleteKeyCommand::class,
|
||||
Command\Api\ListKeysCommand::NAME => Command\Api\ListKeysCommand::class,
|
||||
Command\Api\InitialApiKeyCommand::NAME => Command\Api\InitialApiKeyCommand::class,
|
||||
Command\Api\RenameApiKeyCommand::NAME => Command\Api\RenameApiKeyCommand::class,
|
||||
|
||||
Command\Tag\ListTagsCommand::NAME => Command\Tag\ListTagsCommand::class,
|
||||
Command\Tag\RenameTagCommand::NAME => Command\Tag\RenameTagCommand::class,
|
||||
@ -44,6 +47,8 @@ return [
|
||||
Command\RedirectRule\ManageRedirectRulesCommand::class,
|
||||
|
||||
Command\Integration\MatomoSendVisitsCommand::NAME => Command\Integration\MatomoSendVisitsCommand::class,
|
||||
|
||||
Command\Config\ReadEnvVarCommand::NAME => Command\Config\ReadEnvVarCommand::class,
|
||||
],
|
||||
],
|
||||
|
||||
|
||||
@ -7,25 +7,21 @@ namespace Shlinkio\Shlink\CLI;
|
||||
use Laminas\ServiceManager\AbstractFactory\ConfigAbstractFactory;
|
||||
use Laminas\ServiceManager\Factory\InvokableFactory;
|
||||
use Shlinkio\Shlink\Common\Doctrine\NoDbNameConnectionFactory;
|
||||
use Shlinkio\Shlink\Core\Config\Options\UrlShortenerOptions;
|
||||
use Shlinkio\Shlink\Core\Domain\DomainService;
|
||||
use Shlinkio\Shlink\Core\Geolocation\GeolocationDbUpdater;
|
||||
use Shlinkio\Shlink\Core\Matomo;
|
||||
use Shlinkio\Shlink\Core\Options\TrackingOptions;
|
||||
use Shlinkio\Shlink\Core\Options\UrlShortenerOptions;
|
||||
use Shlinkio\Shlink\Core\RedirectRule\ShortUrlRedirectRuleService;
|
||||
use Shlinkio\Shlink\Core\ShortUrl;
|
||||
use Shlinkio\Shlink\Core\ShortUrl\Helper\ShortUrlStringifier;
|
||||
use Shlinkio\Shlink\Core\Tag\TagService;
|
||||
use Shlinkio\Shlink\Core\Visit;
|
||||
use Shlinkio\Shlink\Installer\Factory\ProcessHelperFactory;
|
||||
use Shlinkio\Shlink\IpGeolocation\GeoLite2\DbUpdater;
|
||||
use Shlinkio\Shlink\IpGeolocation\GeoLite2\GeoLite2ReaderFactory;
|
||||
use Shlinkio\Shlink\Rest\Service\ApiKeyService;
|
||||
use Symfony\Component\Console as SymfonyCli;
|
||||
use Symfony\Component\Lock\LockFactory;
|
||||
use Symfony\Component\Process\PhpExecutableFinder;
|
||||
|
||||
use const Shlinkio\Shlink\LOCAL_LOCK_FACTORY;
|
||||
|
||||
return [
|
||||
|
||||
'dependencies' => [
|
||||
@ -34,13 +30,13 @@ return [
|
||||
SymfonyCli\Helper\ProcessHelper::class => ProcessHelperFactory::class,
|
||||
PhpExecutableFinder::class => InvokableFactory::class,
|
||||
|
||||
GeoLite\GeolocationDbUpdater::class => ConfigAbstractFactory::class,
|
||||
RedirectRule\RedirectRuleHandler::class => InvokableFactory::class,
|
||||
Util\ProcessRunner::class => ConfigAbstractFactory::class,
|
||||
|
||||
ApiKey\RoleResolver::class => ConfigAbstractFactory::class,
|
||||
|
||||
Command\ShortUrl\CreateShortUrlCommand::class => ConfigAbstractFactory::class,
|
||||
Command\ShortUrl\EditShortUrlCommand::class => ConfigAbstractFactory::class,
|
||||
Command\ShortUrl\ResolveUrlCommand::class => ConfigAbstractFactory::class,
|
||||
Command\ShortUrl\ListShortUrlsCommand::class => ConfigAbstractFactory::class,
|
||||
Command\ShortUrl\GetShortUrlVisitsCommand::class => ConfigAbstractFactory::class,
|
||||
@ -56,8 +52,10 @@ return [
|
||||
|
||||
Command\Api\GenerateKeyCommand::class => ConfigAbstractFactory::class,
|
||||
Command\Api\DisableKeyCommand::class => ConfigAbstractFactory::class,
|
||||
Command\Api\DeleteKeyCommand::class => ConfigAbstractFactory::class,
|
||||
Command\Api\ListKeysCommand::class => ConfigAbstractFactory::class,
|
||||
Command\Api\InitialApiKeyCommand::class => ConfigAbstractFactory::class,
|
||||
Command\Api\RenameApiKeyCommand::class => ConfigAbstractFactory::class,
|
||||
|
||||
Command\Tag\ListTagsCommand::class => ConfigAbstractFactory::class,
|
||||
Command\Tag\RenameTagCommand::class => ConfigAbstractFactory::class,
|
||||
@ -74,24 +72,21 @@ return [
|
||||
Command\RedirectRule\ManageRedirectRulesCommand::class => ConfigAbstractFactory::class,
|
||||
|
||||
Command\Integration\MatomoSendVisitsCommand::class => ConfigAbstractFactory::class,
|
||||
|
||||
Command\Config\ReadEnvVarCommand::class => InvokableFactory::class,
|
||||
],
|
||||
],
|
||||
|
||||
ConfigAbstractFactory::class => [
|
||||
GeoLite\GeolocationDbUpdater::class => [
|
||||
DbUpdater::class,
|
||||
GeoLite2ReaderFactory::class,
|
||||
LOCAL_LOCK_FACTORY,
|
||||
TrackingOptions::class,
|
||||
],
|
||||
Util\ProcessRunner::class => [SymfonyCli\Helper\ProcessHelper::class],
|
||||
ApiKey\RoleResolver::class => [DomainService::class, 'config.url_shortener.domain.hostname'],
|
||||
ApiKey\RoleResolver::class => [DomainService::class, UrlShortenerOptions::class],
|
||||
|
||||
Command\ShortUrl\CreateShortUrlCommand::class => [
|
||||
ShortUrl\UrlShortener::class,
|
||||
ShortUrlStringifier::class,
|
||||
UrlShortenerOptions::class,
|
||||
],
|
||||
Command\ShortUrl\EditShortUrlCommand::class => [ShortUrl\ShortUrlService::class, ShortUrlStringifier::class],
|
||||
Command\ShortUrl\ResolveUrlCommand::class => [ShortUrl\ShortUrlResolver::class],
|
||||
Command\ShortUrl\ListShortUrlsCommand::class => [
|
||||
ShortUrl\ShortUrlListService::class,
|
||||
@ -102,7 +97,7 @@ return [
|
||||
Command\ShortUrl\DeleteShortUrlVisitsCommand::class => [ShortUrl\ShortUrlVisitsDeleter::class],
|
||||
Command\ShortUrl\DeleteExpiredShortUrlsCommand::class => [ShortUrl\DeleteShortUrlService::class],
|
||||
|
||||
Command\Visit\DownloadGeoLiteDbCommand::class => [GeoLite\GeolocationDbUpdater::class],
|
||||
Command\Visit\DownloadGeoLiteDbCommand::class => [GeolocationDbUpdater::class],
|
||||
Command\Visit\LocateVisitsCommand::class => [
|
||||
Visit\Geolocation\VisitLocator::class,
|
||||
Visit\Geolocation\VisitToLocationHelper::class,
|
||||
@ -114,8 +109,10 @@ return [
|
||||
|
||||
Command\Api\GenerateKeyCommand::class => [ApiKeyService::class, ApiKey\RoleResolver::class],
|
||||
Command\Api\DisableKeyCommand::class => [ApiKeyService::class],
|
||||
Command\Api\DeleteKeyCommand::class => [ApiKeyService::class],
|
||||
Command\Api\ListKeysCommand::class => [ApiKeyService::class],
|
||||
Command\Api\InitialApiKeyCommand::class => [ApiKeyService::class],
|
||||
Command\Api\RenameApiKeyCommand::class => [ApiKeyService::class],
|
||||
|
||||
Command\Tag\ListTagsCommand::class => [TagService::class],
|
||||
Command\Tag\RenameTagCommand::class => [TagService::class],
|
||||
|
||||
@ -5,6 +5,7 @@ declare(strict_types=1);
|
||||
namespace Shlinkio\Shlink\CLI\ApiKey;
|
||||
|
||||
use Shlinkio\Shlink\CLI\Exception\InvalidRoleConfigException;
|
||||
use Shlinkio\Shlink\Core\Config\Options\UrlShortenerOptions;
|
||||
use Shlinkio\Shlink\Core\Domain\DomainServiceInterface;
|
||||
use Shlinkio\Shlink\Rest\ApiKey\Model\RoleDefinition;
|
||||
use Shlinkio\Shlink\Rest\ApiKey\Role;
|
||||
@ -12,11 +13,11 @@ use Symfony\Component\Console\Input\InputInterface;
|
||||
|
||||
use function is_string;
|
||||
|
||||
class RoleResolver implements RoleResolverInterface
|
||||
readonly class RoleResolver implements RoleResolverInterface
|
||||
{
|
||||
public function __construct(
|
||||
private readonly DomainServiceInterface $domainService,
|
||||
private readonly string $defaultDomain,
|
||||
private DomainServiceInterface $domainService,
|
||||
private UrlShortenerOptions $urlShortenerOptions,
|
||||
) {
|
||||
}
|
||||
|
||||
@ -39,7 +40,7 @@ class RoleResolver implements RoleResolverInterface
|
||||
|
||||
private function resolveRoleForAuthority(string $domainAuthority): RoleDefinition
|
||||
{
|
||||
if ($domainAuthority === $this->defaultDomain) {
|
||||
if ($domainAuthority === $this->urlShortenerOptions->defaultDomain) {
|
||||
throw InvalidRoleConfigException::forDomainOnlyWithDefaultDomain();
|
||||
}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user