mirror of
https://github.com/shlinkio/shlink.git
synced 2026-03-05 23:03:11 +08:00
Compare commits
561 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8d706fdabe | ||
|
|
2e91297d85 | ||
|
|
be4c602afe | ||
|
|
38ca22ed21 | ||
|
|
af8ca33d18 | ||
|
|
197cfa8811 | ||
|
|
03e9117f13 | ||
|
|
9edceab3c4 | ||
|
|
1cb93f6154 | ||
|
|
690b5c89b2 | ||
|
|
8a82361d0e | ||
|
|
d746018f52 | ||
|
|
97e7d4a7fe | ||
|
|
98b23a11a9 | ||
|
|
9641c704e2 | ||
|
|
8e10e9138c | ||
|
|
900de9e800 | ||
|
|
0d964f0fde | ||
|
|
248e8032e3 | ||
|
|
a6286c247a | ||
|
|
c0edcd3cfd | ||
|
|
faed7ae60b | ||
|
|
a65c5c3b56 | ||
|
|
6eb94194a3 | ||
|
|
18ac39ad9c | ||
|
|
05b833b399 | ||
|
|
cb8ba5d970 | ||
|
|
983a7f444c | ||
|
|
ce9cbe2add | ||
|
|
fec9d0375d | ||
|
|
0ad777b6fa | ||
|
|
7712f790e5 | ||
|
|
9ae2dce261 | ||
|
|
39585ed87d | ||
|
|
ca183d6e21 | ||
|
|
54dc82cb90 | ||
|
|
dae52fedf4 | ||
|
|
77898d1edc | ||
|
|
a774778822 | ||
|
|
d0ee6e549b | ||
|
|
c6b83a6437 | ||
|
|
e265e55917 | ||
|
|
ce7f334326 | ||
|
|
aecc36a463 | ||
|
|
66d35968f4 | ||
|
|
f9b1f0ebf4 | ||
|
|
5b80ee73bb | ||
|
|
49daf9fbb6 | ||
|
|
83e373e96a | ||
|
|
97c81fc1c8 | ||
|
|
fff070ea87 | ||
|
|
0c0a4ad940 | ||
|
|
e261bd16e4 | ||
|
|
96d122bcbf | ||
|
|
0f3f9d53c9 | ||
|
|
da53c5a206 | ||
|
|
2c8bc6aca0 | ||
|
|
9e4ea80139 | ||
|
|
c30ec261c9 | ||
|
|
d481c06f09 | ||
|
|
36cb760a88 | ||
|
|
cbfcdd42c8 | ||
|
|
635e968bb2 | ||
|
|
965d191ce1 | ||
|
|
b7ae228a95 | ||
|
|
7cdefcb4b6 | ||
|
|
c496b7ac69 | ||
|
|
f0e12b1d06 | ||
|
|
88efe7d962 | ||
|
|
8fb8aea5f8 | ||
|
|
5a390894ea | ||
|
|
5df3abbce9 | ||
|
|
c53f538c79 | ||
|
|
309ef8dc95 | ||
|
|
5600c1cc4f | ||
|
|
a75ee138e1 | ||
|
|
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 | ||
|
|
b2dabf06bf | ||
|
|
67ae05f473 | ||
|
|
fb4fecf411 | ||
|
|
c855f011d1 | ||
|
|
02717eb2fb | ||
|
|
de70ebe769 | ||
|
|
c6109fd396 | ||
|
|
83584a3175 | ||
|
|
f5dcc52b3b | ||
|
|
1901964de1 | ||
|
|
80e9c2452b | ||
|
|
5ad4b39160 | ||
|
|
89b73a9cfa | ||
|
|
e2d8334d69 | ||
|
|
9b16d7acc0 | ||
|
|
6836840746 | ||
|
|
4084d301ca | ||
|
|
added21b18 | ||
|
|
8cd77391cc | ||
|
|
05ebfccc63 | ||
|
|
cb3a690294 | ||
|
|
194a7b0e57 | ||
|
|
98e4d01feb | ||
|
|
c22e3895b5 | ||
|
|
9a76c19615 | ||
|
|
59fa088975 | ||
|
|
163244f40f | ||
|
|
a89b53af4f |
@@ -1,5 +1,6 @@
|
|||||||
bin/rr
|
bin/rr
|
||||||
config/autoload/*local*
|
config/autoload/*local*
|
||||||
|
config/params/shlink_dev_env.*
|
||||||
data/infra
|
data/infra
|
||||||
data/cache/*
|
data/cache/*
|
||||||
data/log/*
|
data/log/*
|
||||||
|
|||||||
1
.gitattributes
vendored
1
.gitattributes
vendored
@@ -13,7 +13,6 @@
|
|||||||
.travis.yml export-ignore
|
.travis.yml export-ignore
|
||||||
build.sh export-ignore
|
build.sh export-ignore
|
||||||
CHANGELOG.md export-ignore
|
CHANGELOG.md export-ignore
|
||||||
docker-compose.override.yml.dist export-ignore
|
|
||||||
docker-compose.yml export-ignore
|
docker-compose.yml export-ignore
|
||||||
indocker export-ignore
|
indocker export-ignore
|
||||||
phpcs.xml 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
|
label: Minimum steps to reproduce
|
||||||
value: |
|
value: |
|
||||||
<!--
|
<!--
|
||||||
Emphasis in MINIMUM: What is the simplest way to reproduce the bug?
|
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 and the bug may be closed as "not reproducible".
|
|
||||||
If you can provide a simple docker compose config, that's even better.
|
* 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:
|
contact_links:
|
||||||
- name: Question - Support
|
- name: Question - Support
|
||||||
about: Do you need help setting up or using Shlink?
|
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 }}
|
php-version: ${{ inputs.php-version }}
|
||||||
tools: composer
|
tools: composer
|
||||||
extensions: ${{ inputs.php-extensions }}
|
extensions: ${{ inputs.php-extensions }}
|
||||||
coverage: pcov
|
coverage: xdebug
|
||||||
ini-values: pcov.directory=module
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
if: ${{ inputs.install-deps == 'yes' }}
|
if: ${{ inputs.install-deps == 'yes' }}
|
||||||
run: composer install --no-interaction --prefer-dist
|
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:
|
jobs:
|
||||||
db-tests:
|
db-tests:
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-24.04
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
php-version: ['8.2', '8.3']
|
php-version: ['8.4', '8.5']
|
||||||
env:
|
env:
|
||||||
LC_ALL: C
|
LC_ALL: C
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v5
|
||||||
- name: Install MSSQL ODBC
|
- name: Install MSSQL ODBC
|
||||||
if: ${{ inputs.platform == 'ms' }}
|
if: ${{ inputs.platform == 'ms' }}
|
||||||
run: sudo ./data/infra/ci/install-ms-odbc.sh
|
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 }}
|
extensions-cache-key: db-tests-extensions-${{ matrix.php-version }}-${{ inputs.platform }}
|
||||||
- name: Create test database
|
- name: Create test database
|
||||||
if: ${{ inputs.platform == 'ms' }}
|
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
|
- name: Run tests
|
||||||
run: composer test:db:${{ inputs.platform }}
|
run: composer test:db:${{ inputs.platform }}
|
||||||
- name: Upload code coverage
|
- name: Upload code coverage
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v5
|
||||||
if: ${{ matrix.php-version == '8.2' && inputs.platform == 'sqlite:ci' }}
|
if: ${{ matrix.php-version == '8.4' && inputs.platform == 'sqlite:ci' }}
|
||||||
with:
|
with:
|
||||||
name: coverage-db
|
name: coverage-db
|
||||||
path: |
|
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:
|
on:
|
||||||
pull_request:
|
pull_request:
|
||||||
@@ -7,8 +7,6 @@ on:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build-docker-image:
|
build-docker-image:
|
||||||
runs-on: ubuntu-22.04
|
uses: shlinkio/github-actions/.github/workflows/docker-image-build-ci.yml@main
|
||||||
steps:
|
with:
|
||||||
- name: Checkout code
|
platforms: 'linux/arm64/v8,linux/amd64'
|
||||||
uses: actions/checkout@v4
|
|
||||||
- run: docker build -t shlink-docker-image:temp .
|
|
||||||
|
|||||||
10
.github/workflows/ci-tests.yml
vendored
10
.github/workflows/ci-tests.yml
vendored
@@ -10,14 +10,14 @@ on:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
tests:
|
tests:
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-24.04
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
php-version: ['8.2', '8.3']
|
php-version: ['8.4', '8.5']
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # rr get-binary picks this env automatically
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # rr get-binary picks this env automatically
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v5
|
||||||
- name: Start postgres database server
|
- name: Start postgres database server
|
||||||
if: ${{ inputs.test-group == 'api' }}
|
if: ${{ inputs.test-group == 'api' }}
|
||||||
run: docker compose -f docker-compose.yml -f docker-compose.ci.yml up -d shlink_db_postgres
|
run: docker compose -f docker-compose.yml -f docker-compose.ci.yml up -d shlink_db_postgres
|
||||||
@@ -32,8 +32,8 @@ jobs:
|
|||||||
if: ${{ inputs.test-group == 'api' }}
|
if: ${{ inputs.test-group == 'api' }}
|
||||||
run: ./vendor/bin/rr get --no-interaction --no-config --location bin/ && chmod +x bin/rr
|
run: ./vendor/bin/rr get --no-interaction --no-config --location bin/ && chmod +x bin/rr
|
||||||
- run: composer test:${{ inputs.test-group }}:ci
|
- run: composer test:${{ inputs.test-group }}:ci
|
||||||
- uses: actions/upload-artifact@v4
|
- uses: actions/upload-artifact@v5
|
||||||
if: ${{ matrix.php-version == '8.2' }}
|
if: ${{ matrix.php-version == '8.4' }}
|
||||||
with:
|
with:
|
||||||
name: coverage-${{ inputs.test-group }}
|
name: coverage-${{ inputs.test-group }}
|
||||||
path: |
|
path: |
|
||||||
|
|||||||
25
.github/workflows/ci.yml
vendored
25
.github/workflows/ci.yml
vendored
@@ -24,13 +24,13 @@ on:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
static-analysis:
|
static-analysis:
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-24.04
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
php-version: ['8.2']
|
php-version: ['8.4']
|
||||||
command: ['cs', 'stan', 'swagger:validate']
|
command: ['cs', 'stan', 'openapi:validate']
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v5
|
||||||
- uses: './.github/actions/ci-setup'
|
- uses: './.github/actions/ci-setup'
|
||||||
with:
|
with:
|
||||||
php-version: ${{ matrix.php-version }}
|
php-version: ${{ matrix.php-version }}
|
||||||
@@ -66,19 +66,18 @@ jobs:
|
|||||||
- api-tests
|
- api-tests
|
||||||
- cli-tests
|
- cli-tests
|
||||||
- db-tests
|
- db-tests
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-24.04
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
php-version: ['8.2']
|
php-version: ['8.4']
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- uses: actions/checkout@v5
|
||||||
uses: actions/checkout@v4
|
|
||||||
- name: Use PHP
|
- name: Use PHP
|
||||||
uses: './.github/actions/ci-setup'
|
uses: './.github/actions/ci-setup'
|
||||||
with:
|
with:
|
||||||
php-version: ${{ matrix.php-version }}
|
php-version: ${{ matrix.php-version }}
|
||||||
extensions-cache-key: tests-extensions-${{ matrix.php-version }}
|
extensions-cache-key: tests-extensions-${{ matrix.php-version }}
|
||||||
- uses: actions/download-artifact@v4
|
- uses: actions/download-artifact@v6
|
||||||
with:
|
with:
|
||||||
path: build
|
path: build
|
||||||
- run: mv build/coverage-unit/coverage-unit.cov build/coverage-unit.cov
|
- 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: mv build/coverage-cli/coverage-cli.cov build/coverage-cli.cov
|
||||||
- run: vendor/bin/phpcov merge build --clover build/clover.xml
|
- run: vendor/bin/phpcov merge build --clover build/clover.xml
|
||||||
- name: Publish coverage
|
- name: Publish coverage
|
||||||
uses: codecov/codecov-action@v4
|
uses: codecov/codecov-action@v5
|
||||||
with:
|
with:
|
||||||
file: ./build/clover.xml
|
files: ./build/clover.xml
|
||||||
|
|
||||||
delete-artifacts:
|
delete-artifacts:
|
||||||
needs:
|
needs:
|
||||||
- upload-coverage
|
- upload-coverage
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-24.04
|
||||||
steps:
|
steps:
|
||||||
- uses: geekyeggo/delete-artifact@v2
|
- uses: geekyeggo/delete-artifact@v5
|
||||||
with:
|
with:
|
||||||
name: |
|
name: |
|
||||||
coverage-*
|
coverage-*
|
||||||
|
|||||||
2
.github/workflows/publish-docker-image.yml
vendored
2
.github/workflows/publish-docker-image.yml
vendored
@@ -15,7 +15,7 @@ jobs:
|
|||||||
- runtime: 'rr'
|
- runtime: 'rr'
|
||||||
tag-suffix: 'roadrunner'
|
tag-suffix: 'roadrunner'
|
||||||
platforms: 'linux/arm64/v8,linux/amd64'
|
platforms: 'linux/arm64/v8,linux/amd64'
|
||||||
uses: shlinkio/github-actions/.github/workflows/docker-build-and-publish.yml@main
|
uses: shlinkio/github-actions/.github/workflows/docker-publish-image.yml@main
|
||||||
secrets: inherit
|
secrets: inherit
|
||||||
with:
|
with:
|
||||||
image-name: shlinkio/shlink
|
image-name: shlinkio/shlink
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
name: Publish swagger spec
|
name: Publish openapi spec
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
@@ -7,12 +7,12 @@ on:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-24.04
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
php-version: ['8.2']
|
php-version: ['8.4']
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v5
|
||||||
- name: Determine version
|
- name: Determine version
|
||||||
id: determine_version
|
id: determine_version
|
||||||
run: echo "version=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT
|
run: echo "version=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT
|
||||||
@@ -20,10 +20,10 @@ jobs:
|
|||||||
- uses: './.github/actions/ci-setup'
|
- uses: './.github/actions/ci-setup'
|
||||||
with:
|
with:
|
||||||
php-version: ${{ matrix.php-version }}
|
php-version: ${{ matrix.php-version }}
|
||||||
extensions-cache-key: publish-swagger-spec-extensions-${{ matrix.php-version }}
|
extensions-cache-key: publish-openapi-spec-extensions-${{ matrix.php-version }}
|
||||||
- run: composer swagger:inline
|
- run: composer openapi:inline
|
||||||
- run: mkdir ${{ steps.determine_version.outputs.version }}
|
- 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
|
- name: Publish spec
|
||||||
uses: JamesIves/github-pages-deploy-action@v4
|
uses: JamesIves/github-pages-deploy-action@v4
|
||||||
with:
|
with:
|
||||||
18
.github/workflows/publish-release.yml
vendored
18
.github/workflows/publish-release.yml
vendored
@@ -7,29 +7,29 @@ on:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-24.04
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
php-version: ['8.2', '8.3']
|
php-version: ['8.4', '8.5']
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v5
|
||||||
- uses: './.github/actions/ci-setup'
|
- uses: './.github/actions/ci-setup'
|
||||||
with:
|
with:
|
||||||
php-version: ${{ matrix.php-version }}
|
php-version: ${{ matrix.php-version }}
|
||||||
extensions-cache-key: publish-swagger-spec-extensions-${{ matrix.php-version }}
|
extensions-cache-key: publish-swagger-spec-extensions-${{ matrix.php-version }}
|
||||||
install-deps: 'no'
|
install-deps: 'no'
|
||||||
- run: ./build.sh ${GITHUB_REF#refs/tags/v}
|
- run: ./build.sh ${GITHUB_REF#refs/tags/v}
|
||||||
- uses: actions/upload-artifact@v4
|
- uses: actions/upload-artifact@v5
|
||||||
with:
|
with:
|
||||||
name: dist-files-${{ matrix.php-version }}
|
name: dist-files-${{ matrix.php-version }}
|
||||||
path: build
|
path: build
|
||||||
|
|
||||||
publish:
|
publish:
|
||||||
needs: ['build']
|
needs: ['build']
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-24.04
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v5
|
||||||
- uses: actions/download-artifact@v4
|
- uses: actions/download-artifact@v6
|
||||||
with:
|
with:
|
||||||
path: build
|
path: build
|
||||||
- name: Publish release with assets
|
- name: Publish release with assets
|
||||||
@@ -43,8 +43,8 @@ jobs:
|
|||||||
|
|
||||||
delete-artifacts:
|
delete-artifacts:
|
||||||
needs: ['publish']
|
needs: ['publish']
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-24.04
|
||||||
steps:
|
steps:
|
||||||
- uses: geekyeggo/delete-artifact@v2
|
- uses: geekyeggo/delete-artifact@v5
|
||||||
with:
|
with:
|
||||||
name: dist-files-*
|
name: dist-files-*
|
||||||
|
|||||||
5
.gitignore
vendored
5
.gitignore
vendored
@@ -10,9 +10,6 @@ data/database.sqlite
|
|||||||
data/shlink-tests.db
|
data/shlink-tests.db
|
||||||
data/GeoLite2-City.*
|
data/GeoLite2-City.*
|
||||||
data/infra/matomo
|
data/infra/matomo
|
||||||
docs/swagger-ui*
|
|
||||||
docs/mercure.html
|
docs/mercure.html
|
||||||
docker-compose.override.yml
|
|
||||||
.phpunit.result.cache
|
.phpunit.result.cache
|
||||||
docs/swagger/swagger-inlined.json
|
docs/swagger/openapi-inlined.json
|
||||||
phpcov*
|
|
||||||
|
|||||||
1229
CHANGELOG.md
1229
CHANGELOG.md
File diff suppressed because it is too large
Load Diff
@@ -16,11 +16,14 @@ The first thing you need to do is fork the repository, and clone it in your loca
|
|||||||
|
|
||||||
Then you will have to follow these steps:
|
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`.
|
* 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.
|
The first time this command is run, it will create several containers that are used during development, so it may take some time.
|
||||||
|
|||||||
29
Dockerfile
29
Dockerfile
@@ -1,26 +1,26 @@
|
|||||||
FROM php:8.3-alpine3.19 as base
|
FROM php:8.4-alpine3.22 AS base
|
||||||
|
|
||||||
ARG SHLINK_VERSION=latest
|
ARG SHLINK_VERSION=latest
|
||||||
ENV SHLINK_VERSION ${SHLINK_VERSION}
|
ENV SHLINK_VERSION=${SHLINK_VERSION}
|
||||||
ARG SHLINK_RUNTIME=rr
|
ARG SHLINK_RUNTIME=rr
|
||||||
ENV SHLINK_RUNTIME ${SHLINK_RUNTIME}
|
ENV SHLINK_RUNTIME=${SHLINK_RUNTIME}
|
||||||
|
|
||||||
ENV USER_ID '1001'
|
ENV USER_ID='1001'
|
||||||
ENV PDO_SQLSRV_VERSION 5.12.0
|
ENV PDO_SQLSRV_VERSION='5.12.0'
|
||||||
ENV MS_ODBC_DOWNLOAD 'b/9/f/b9f3cce4-3925-46d4-9f46-da08869c6486'
|
ENV MS_ODBC_DOWNLOAD='7/6/d/76de322a-d860-4894-9945-f0cc5d6a45f8'
|
||||||
ENV MS_ODBC_SQL_VERSION 18_18.1.1.1
|
ENV MS_ODBC_SQL_VERSION='18_18.4.1.1'
|
||||||
ENV LC_ALL 'C'
|
ENV LC_ALL='C'
|
||||||
|
|
||||||
WORKDIR /etc/shlink
|
WORKDIR /etc/shlink
|
||||||
|
|
||||||
# Install required PHP extensions
|
# Install required PHP extensions
|
||||||
RUN \
|
RUN \
|
||||||
# Temp install dev dependencies needed to compile the extensions
|
# 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 && \
|
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 gd && \
|
docker-php-ext-install -j"$(nproc)" pdo_mysql pdo_pgsql intl calendar sockets bcmath zip && \
|
||||||
apk add --no-cache sqlite-libs && \
|
apk add --no-cache sqlite-libs && \
|
||||||
docker-php-ext-install -j"$(nproc)" pdo_sqlite && \
|
docker-php-ext-install -j"$(nproc)" pdo_sqlite && \
|
||||||
# Remove temp dev extensions, and install prod equivalents that are required at runtime
|
# Remove temp dev extensions, and install prod equivalents that are required at runtime \
|
||||||
apk del .dev-deps && \
|
apk del .dev-deps && \
|
||||||
apk add --no-cache postgresql icu libzip libpng
|
apk add --no-cache postgresql icu libzip libpng
|
||||||
|
|
||||||
@@ -36,14 +36,14 @@ RUN apk add --no-cache --virtual .phpize-deps ${PHPIZE_DEPS} unixodbc-dev && \
|
|||||||
apk del .phpize-deps
|
apk del .phpize-deps
|
||||||
|
|
||||||
# Install shlink
|
# Install shlink
|
||||||
FROM base as builder
|
FROM base AS builder
|
||||||
COPY . .
|
COPY . .
|
||||||
COPY --from=composer:2 /usr/bin/composer ./composer.phar
|
COPY --from=composer:2 /usr/bin/composer ./composer.phar
|
||||||
RUN apk add --no-cache git && \
|
RUN apk add --no-cache git && \
|
||||||
php composer.phar install --no-dev --prefer-dist --optimize-autoloader --no-progress --no-interaction && \
|
php composer.phar install --no-dev --prefer-dist --optimize-autoloader --no-progress --no-interaction && \
|
||||||
php composer.phar clear-cache && \
|
php composer.phar clear-cache && \
|
||||||
rm -r docker composer.* && \
|
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
|
# Prepare final image
|
||||||
@@ -61,7 +61,6 @@ EXPOSE 8080
|
|||||||
|
|
||||||
# Copy config specific for the image
|
# Copy config specific for the image
|
||||||
COPY docker/docker-entrypoint.sh docker-entrypoint.sh
|
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/
|
COPY docker/config/php.ini ${PHP_INI_DIR}/conf.d/
|
||||||
|
|
||||||
USER ${USER_ID}
|
USER ${USER_ID}
|
||||||
|
|||||||
12
README.md
12
README.md
@@ -7,8 +7,7 @@
|
|||||||
[](https://github.com/shlinkio/shlink/blob/main/LICENSE)
|
[](https://github.com/shlinkio/shlink/blob/main/LICENSE)
|
||||||
|
|
||||||
[](https://fosstodon.org/@shlinkio)
|
[](https://fosstodon.org/@shlinkio)
|
||||||
[](https://bsky.app/profile/shlinkio.bsky.social)
|
[](https://bsky.app/profile/shlink.io)
|
||||||
[](https://twitter.com/shlinkio)
|
|
||||||
[](https://slnk.to/donate)
|
[](https://slnk.to/donate)
|
||||||
|
|
||||||
A PHP-based self-hosted URL shortener that can be used to serve shortened URLs under your own domain.
|
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:
|
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.
|
* The next PHP extensions: json, curl, pdo, intl, gd and gmp/bcmath.
|
||||||
* apcu extension is recommended if you don't plan to use RoadRunner.
|
* 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.
|
* sockets and bcmath extensions are required if you want to integrate with a RabbitMQ instance.
|
||||||
* MySQL, MariaDB, PostgreSQL, MicrosoftSQL or SQLite.
|
* 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`.
|
* 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.
|
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)
|
> This product includes GeoLite2 data created by MaxMind, available from [https://www.maxmind.com](https://www.maxmind.com)
|
||||||
|
|||||||
21
UPGRADE.md
21
UPGRADE.md
@@ -1,5 +1,26 @@
|
|||||||
# Upgrading
|
# Upgrading
|
||||||
|
|
||||||
|
## From v4.x to v5.x
|
||||||
|
|
||||||
|
### General
|
||||||
|
|
||||||
|
* Generating QR codes by appending `/qr-code` to a short URL is no longer possible. Use external services to generate QR codes from a short URL, or the logic embedded in Shlink Web Client and Shlink Dashboard.
|
||||||
|
* Shlink no longer tries to detect trusted proxies automatically, when resolving the visitor's IP address.
|
||||||
|
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.
|
||||||
|
* PHP 8.3 is no longer supported. Only 8.4 and 8.5 are officially supported as of Shlink 5.0.0.
|
||||||
|
|
||||||
|
### Changes in CLI
|
||||||
|
|
||||||
|
* 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.
|
||||||
|
* All visits-related commands (`short-url:visits`, `tag:visits`, `domain:visits`, `visit:orphan` and `visit:non-orphan`) now return more information, and columns are arranged slightly differently.
|
||||||
|
* The `short-url:list` command no longer accepts `--including-all-tags` and `--show-api-key-name` options. Use `--tags-all` and `--show-api-key` instead.
|
||||||
|
* The `short-url:list` command no longer allows ordering using the `--order-by=field,dir` format. Use `--order-by=field-dir` instead.
|
||||||
|
* All commands which used to accept the `--tags` flag, no longer accept it. Pass `--tag` multiple times instead, one per tag.
|
||||||
|
|
||||||
|
### Changes in env vars
|
||||||
|
|
||||||
|
* The `REDIRECT_APPEND_EXTRA_PATH` env var is no longer supported. Use `REDIRECT_EXTRA_PATH_MODE=append` to enable the same behavior.
|
||||||
|
|
||||||
## From v3.x to v4.x
|
## From v3.x to v4.x
|
||||||
|
|
||||||
### General
|
### General
|
||||||
|
|||||||
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);
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Shlinkio\Shlink;
|
||||||
|
|
||||||
use Mezzio\Application;
|
use Mezzio\Application;
|
||||||
use Psr\Container\ContainerInterface;
|
use Psr\Container\ContainerInterface;
|
||||||
use Shlinkio\Shlink\Common\Middleware\RequestIdMiddleware;
|
use Shlinkio\Shlink\Common\Middleware\RequestIdMiddleware;
|
||||||
use Shlinkio\Shlink\EventDispatcher\RoadRunner\RoadRunnerTaskConsumerToListener;
|
use Shlinkio\Shlink\EventDispatcher\RoadRunner\RoadRunnerTaskConsumerToListener;
|
||||||
use Spiral\RoadRunner\Http\PSR7Worker;
|
use Spiral\RoadRunner\Http\PSR7Worker;
|
||||||
|
|
||||||
|
use function gc_collect_cycles;
|
||||||
use function Shlinkio\Shlink\Config\env;
|
use function Shlinkio\Shlink\Config\env;
|
||||||
|
|
||||||
(static function (): void {
|
(static function (): void {
|
||||||
/** @var ContainerInterface $container */
|
/** @var ContainerInterface $container */
|
||||||
$container = include __DIR__ . '/../config/container.php';
|
$container = include __DIR__ . '/../config/container.php';
|
||||||
$rrMode = env('RR_MODE');
|
$rrMode = env('RR_MODE');
|
||||||
|
$gcCollectCycles = env('GC_COLLECT_CYCLES', default: false);
|
||||||
|
|
||||||
if ($rrMode === 'http') {
|
if ($rrMode === 'http') {
|
||||||
// This was spin-up as a web worker
|
// This was spin-up as a web worker
|
||||||
@@ -25,6 +29,10 @@ use function Shlinkio\Shlink\Config\env;
|
|||||||
$worker->respond($app->handle($req));
|
$worker->respond($app->handle($req));
|
||||||
} catch (Throwable $e) {
|
} catch (Throwable $e) {
|
||||||
$worker->getWorker()->error((string) $e);
|
$worker->getWorker()->error((string) $e);
|
||||||
|
} finally {
|
||||||
|
if ($gcCollectCycles) {
|
||||||
|
gc_collect_cycles();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} 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 DB_DRIVER="${DB_DRIVER:-"postgres"}"
|
||||||
export GENERATE_COVERAGE="${GENERATE_COVERAGE:-"no"}"
|
export GENERATE_COVERAGE="${GENERATE_COVERAGE:-"no"}"
|
||||||
|
|
||||||
|
[ "$GENERATE_COVERAGE" != 'no' ] && export XDEBUG_MODE=coverage
|
||||||
|
|
||||||
# Reset logs
|
# Reset logs
|
||||||
OUTPUT_LOGS=data/log/api-tests/output.log
|
OUTPUT_LOGS=data/log/api-tests/output.log
|
||||||
rm -rf data/log/api-tests
|
rm -rf data/log/api-tests
|
||||||
@@ -22,7 +24,7 @@ echo 'Starting server...'
|
|||||||
-o=logs.channels.server.output="${PWD}/${OUTPUT_LOGS}" &
|
-o=logs.channels.server.output="${PWD}/${OUTPUT_LOGS}" &
|
||||||
sleep 2 # Let's give the server a couple of seconds to start
|
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=$?
|
TESTS_EXIT_CODE=$?
|
||||||
|
|
||||||
[ "$TEST_RUNTIME" = 'rr' ] && bin/rr stop -w .
|
[ "$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...'
|
echo 'Deleting dev files...'
|
||||||
rm composer.*
|
rm composer.*
|
||||||
|
|
||||||
# Update Shlink version in config
|
# Update Shlink version
|
||||||
sed -i "s/%SHLINK_VERSION%/${version}/g" config/autoload/app_options.global.php
|
sed -i "s/%SHLINK_VERSION%/${version}/g" module/Core/src/Config/Options/AppOptions.php
|
||||||
|
|
||||||
# Compressing file
|
# Compressing file
|
||||||
echo 'Compressing files...'
|
echo 'Compressing files...'
|
||||||
|
|||||||
196
composer.json
196
composer.json
@@ -12,72 +12,66 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"require": {
|
"require": {
|
||||||
"php": "^8.2",
|
"php": "^8.4",
|
||||||
"ext-curl": "*",
|
"ext-curl": "*",
|
||||||
"ext-gd": "*",
|
"ext-mbstring": "*",
|
||||||
"ext-json": "*",
|
|
||||||
"ext-pdo": "*",
|
"ext-pdo": "*",
|
||||||
"akrabat/ip-address-middleware": "^2.1",
|
"akrabat/ip-address-middleware": "^2.6",
|
||||||
"cakephp/chronos": "^3.0.2",
|
"cakephp/chronos": "^3.1",
|
||||||
"doctrine/dbal": "^4.0",
|
"doctrine/dbal": "^4.4",
|
||||||
"doctrine/migrations": "^3.6",
|
"doctrine/migrations": "^3.9",
|
||||||
"doctrine/orm": "^3.0",
|
"doctrine/orm": "^3.6",
|
||||||
"endroid/qr-code": "^5.0",
|
"donatj/phpuseragentparser": "^1.11",
|
||||||
"friendsofphp/proxy-manager-lts": "^1.0",
|
"friendsofphp/proxy-manager-lts": "^1.0",
|
||||||
"geoip2/geoip2": "^3.0",
|
"geoip2/geoip2": "^3.1",
|
||||||
"guzzlehttp/guzzle": "^7.5",
|
"guzzlehttp/guzzle": "^7.9",
|
||||||
"jaybizzle/crawler-detect": "^1.2.116",
|
"hidehalo/nanoid-php": "^2.0",
|
||||||
"laminas/laminas-config": "^3.8",
|
"jaybizzle/crawler-detect": "^1.3",
|
||||||
"laminas/laminas-config-aggregator": "^1.13",
|
"laminas/laminas-config-aggregator": "^1.17",
|
||||||
"laminas/laminas-diactoros": "^3.3",
|
"laminas/laminas-diactoros": "^3.5",
|
||||||
"laminas/laminas-inputfilter": "^2.27",
|
"laminas/laminas-inputfilter": "^2.31",
|
||||||
"laminas/laminas-servicemanager": "^3.21",
|
"laminas/laminas-servicemanager": "^3.23",
|
||||||
"laminas/laminas-stdlib": "^3.17",
|
"laminas/laminas-stdlib": "^3.20",
|
||||||
"matomo/matomo-php-tracker": "^3.2",
|
"league/csv": "^9.28",
|
||||||
"mezzio/mezzio": "^3.17",
|
"matomo/matomo-php-tracker": "^3.3",
|
||||||
"mezzio/mezzio-fastroute": "^3.11",
|
"mezzio/mezzio": "^3.20",
|
||||||
"mezzio/mezzio-problem-details": "^1.13",
|
"mezzio/mezzio-fastroute": "^3.12",
|
||||||
"mlocati/ip-lib": "^1.18",
|
"mezzio/mezzio-problem-details": "^1.15",
|
||||||
"mobiledetect/mobiledetectlib": "^4.8",
|
"mlocati/ip-lib": "^1.18.1",
|
||||||
"pagerfanta/core": "^3.8",
|
"pagerfanta/core": "^3.8",
|
||||||
"pugx/shortid-php": "^1.1",
|
|
||||||
"ramsey/uuid": "^4.7",
|
"ramsey/uuid": "^4.7",
|
||||||
"shlinkio/doctrine-specification": "^2.1.1",
|
"shlinkio/doctrine-specification": "^2.2",
|
||||||
"shlinkio/shlink-common": "^6.1",
|
"shlinkio/shlink-common": "^8.0.0",
|
||||||
"shlinkio/shlink-config": "^3.0",
|
"shlinkio/shlink-config": "^4.1.0",
|
||||||
"shlinkio/shlink-event-dispatcher": "^4.1",
|
"shlinkio/shlink-event-dispatcher": "^4.4.0",
|
||||||
"shlinkio/shlink-importer": "^5.3.2",
|
"shlinkio/shlink-importer": "^5.7.0",
|
||||||
"shlinkio/shlink-installer": "^9.1",
|
"shlinkio/shlink-installer": "^10.0.0",
|
||||||
"shlinkio/shlink-ip-geolocation": "^4.0",
|
"shlinkio/shlink-ip-geolocation": "^5.0.0",
|
||||||
"shlinkio/shlink-json": "^1.1",
|
"shlinkio/shlink-json": "^1.3",
|
||||||
"spiral/roadrunner": "^2023.3",
|
"spiral/roadrunner": "^2025.1",
|
||||||
"spiral/roadrunner-cli": "^2.6",
|
"spiral/roadrunner-cli": "^2.7",
|
||||||
"spiral/roadrunner-http": "^3.3",
|
"spiral/roadrunner-http": "^3.6",
|
||||||
"spiral/roadrunner-jobs": "^4.3",
|
"spiral/roadrunner-jobs": "^4.7",
|
||||||
"symfony/console": "^7.0",
|
"symfony/console": "^8.0",
|
||||||
"symfony/filesystem": "^7.0",
|
"symfony/filesystem": "^8.0",
|
||||||
"symfony/lock": "^7.0",
|
"symfony/lock": "^8.0",
|
||||||
"symfony/process": "^7.0",
|
"symfony/process": "^8.0",
|
||||||
"symfony/string": "^7.0"
|
"symfony/string": "^8.0"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"devizzent/cebe-php-openapi": "^1.0.1",
|
"devizzent/cebe-php-openapi": "^1.1.2",
|
||||||
"devster/ubench": "^2.1",
|
"devster/ubench": "^2.1",
|
||||||
"phpstan/phpstan": "^1.10",
|
"phpstan/phpstan": "^2.1",
|
||||||
"phpstan/phpstan-doctrine": "^1.3",
|
"phpstan/phpstan-doctrine": "^2.0",
|
||||||
"phpstan/phpstan-phpunit": "^1.3",
|
"phpstan/phpstan-phpunit": "^2.0.5",
|
||||||
"phpstan/phpstan-symfony": "^1.3",
|
"phpstan/phpstan-symfony": "^2.0",
|
||||||
"phpunit/php-code-coverage": "^10.1",
|
"phpunit/php-code-coverage": "^13.0",
|
||||||
"phpunit/phpcov": "^9.0",
|
"phpunit/phpcov": "^12.0",
|
||||||
"phpunit/phpunit": "^10.4",
|
"phpunit/phpunit": "^13.0",
|
||||||
"roave/security-advisories": "dev-master",
|
"shlinkio/php-coding-standard": "~2.5.0",
|
||||||
"shlinkio/php-coding-standard": "~2.3.0",
|
"shlinkio/shlink-test-utils": "^4.5",
|
||||||
"shlinkio/shlink-test-utils": "^4.1",
|
"symfony/var-dumper": "^8.0",
|
||||||
"symfony/var-dumper": "^7.0",
|
"veewee/composer-run-parallel": "^1.4"
|
||||||
"veewee/composer-run-parallel": "^1.3"
|
|
||||||
},
|
|
||||||
"conflict": {
|
|
||||||
"symfony/var-exporter": ">=6.3.9,<=6.4.0"
|
|
||||||
},
|
},
|
||||||
"autoload": {
|
"autoload": {
|
||||||
"psr-4": {
|
"psr-4": {
|
||||||
@@ -108,66 +102,56 @@
|
|||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"ci": [
|
"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"
|
"@parallel test:api:ci test:cli:ci"
|
||||||
],
|
],
|
||||||
"cs": "phpcs -s",
|
"cs": "phpcs -s",
|
||||||
"cs:fix": "phpcbf",
|
"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": [
|
"test": [
|
||||||
"@parallel test:unit test:db",
|
"@parallel test:unit test:db",
|
||||||
"@parallel test:api test:cli"
|
"@parallel test:api test:cli"
|
||||||
],
|
],
|
||||||
"test:unit": "COLUMNS=120 vendor/bin/phpunit --order-by=random --colors=always --testdox",
|
"test:unit": ["@putenv COLUMNS=120", "phpunit --order-by=random --testdox --testdox-summary"],
|
||||||
"test:unit:ci": "@test:unit --coverage-php=build/coverage-unit.cov",
|
"test:unit:ci": ["@putenv XDEBUG_MODE=coverage", "@test:unit --coverage-php=build/coverage-unit.cov"],
|
||||||
"test:unit:pretty": "@test:unit --coverage-html build/coverage-unit/coverage-html",
|
"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": "@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": ["@putenv APP_ENV=test", "phpunit --order-by=random --testdox --testdox-summary -c phpunit-db.xml"],
|
||||||
"test:db:sqlite:ci": "@test:db:sqlite --coverage-php build/coverage-db.cov",
|
"test:db:sqlite:ci": ["@putenv XDEBUG_MODE=coverage", "@test:db:sqlite --coverage-php build/coverage-db.cov"],
|
||||||
"test:db:mysql": "DB_DRIVER=mysql composer test:db:sqlite -- $*",
|
"test:db:mysql": ["@putenv DB_DRIVER=mysql", "@test:db:sqlite"],
|
||||||
"test:db:maria": "DB_DRIVER=maria composer test:db:sqlite -- $*",
|
"test:db:maria": ["@putenv DB_DRIVER=maria", "@test:db:sqlite"],
|
||||||
"test:db:postgres": "DB_DRIVER=postgres composer test:db:sqlite -- $*",
|
"test:db:postgres": ["@putenv DB_DRIVER=postgres", "@test:db:sqlite"],
|
||||||
"test:db:ms": "DB_DRIVER=mssql composer test:db:sqlite -- $*",
|
"test:db:ms": ["@putenv DB_DRIVER=mssql", "@test:db:sqlite"],
|
||||||
"test:api": "bin/test/run-api-tests.sh",
|
"test:api": "bin/test/run-api-tests.sh",
|
||||||
"test:api:sqlite": "DB_DRIVER=sqlite composer test:api -- $*",
|
"test:api:sqlite": ["@putenv DB_DRIVER=sqlite", "@test:api"],
|
||||||
"test:api:mysql": "DB_DRIVER=mysql composer test:api -- $*",
|
"test:api:mysql": ["@putenv DB_DRIVER=mysql", "@test:api"],
|
||||||
"test:api:maria": "DB_DRIVER=maria composer test:api -- $*",
|
"test:api:maria": ["@putenv DB_DRIVER=maria", "@test:api"],
|
||||||
"test:api:mssql": "DB_DRIVER=mssql composer test:api -- $*",
|
"test:api:mssql": ["@putenv DB_DRIVER=mssql", "@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:ci": [
|
||||||
"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",
|
"@putenv GENERATE_COVERAGE=yes",
|
||||||
"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:api",
|
||||||
"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",
|
"phpcov merge build/coverage-api --php build/coverage-api.cov && rm build/coverage-api/*.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",
|
"test:api:pretty": [
|
||||||
"swagger:inline": "php-openapi inline docs/swagger/swagger.json docs/swagger/swagger-inlined.json",
|
"@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"
|
"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": {
|
"config": {
|
||||||
"sort-packages": true,
|
"sort-packages": true,
|
||||||
"platform-check": false,
|
"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 {
|
return (static function (): array {
|
||||||
$redisServers = EnvVars::REDIS_SERVERS->loadFromEnv();
|
$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 ? [] : [
|
$cacheRedisBlock = $redisServers === null ? [] : [
|
||||||
'redis' => [
|
'redis' => [
|
||||||
'servers' => $redisServers,
|
'servers' => $redisServers,
|
||||||
'sentinel_service' => EnvVars::REDIS_SENTINEL_SERVICE->loadFromEnv(),
|
'sentinel_service' => EnvVars::REDIS_SENTINEL_SERVICE->loadFromEnv(),
|
||||||
|
'username' => EnvVars::REDIS_SERVERS_USER->loadFromEnv(),
|
||||||
|
'password' => EnvVars::REDIS_SERVERS_PASSWORD->loadFromEnv(),
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
|
|
||||||
return [
|
return [
|
||||||
'cache' => [
|
'cache' => [
|
||||||
'namespace' => EnvVars::CACHE_NAMESPACE->loadFromEnv('Shlink'),
|
'namespace' => EnvVars::CACHE_NAMESPACE->loadFromEnv(),
|
||||||
...$cacheRedisBlock,
|
...$cacheRedisBlock,
|
||||||
],
|
],
|
||||||
'redis' => $redis,
|
'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);
|
declare(strict_types=1);
|
||||||
|
|
||||||
use Laminas\ConfigAggregator\ConfigAggregator;
|
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
|
'debug' => $isDev,
|
||||||
// commands don't generate a cache file that's then used by php-fpm web executions
|
|
||||||
ConfigAggregator::ENABLE_CACHE => PHP_SAPI !== 'cli',
|
|
||||||
|
|
||||||
];
|
// 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\ServerRequestFactoryInterface;
|
||||||
use Psr\Http\Message\StreamFactoryInterface;
|
use Psr\Http\Message\StreamFactoryInterface;
|
||||||
use Psr\Http\Message\UploadedFileFactoryInterface;
|
use Psr\Http\Message\UploadedFileFactoryInterface;
|
||||||
|
use Shlinkio\Shlink\Core\Config\EnvVars;
|
||||||
use Spiral\RoadRunner\Http\PSR7Worker;
|
use Spiral\RoadRunner\Http\PSR7Worker;
|
||||||
use Spiral\RoadRunner\WorkerInterface;
|
use Spiral\RoadRunner\WorkerInterface;
|
||||||
use Symfony\Component\Filesystem\Filesystem;
|
use Symfony\Component\Filesystem\Filesystem;
|
||||||
@@ -36,7 +37,7 @@ return [
|
|||||||
'lazy_services' => [
|
'lazy_services' => [
|
||||||
'proxies_target_dir' => 'data/proxies',
|
'proxies_target_dir' => 'data/proxies',
|
||||||
'proxies_namespace' => 'ShlinkProxy',
|
'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 {
|
return (static function (): array {
|
||||||
$driver = EnvVars::DB_DRIVER->loadFromEnv();
|
$driver = EnvVars::DB_DRIVER->loadFromEnv();
|
||||||
|
$useEncryption = (bool) EnvVars::DB_USE_ENCRYPTION->loadFromEnv();
|
||||||
$isMysqlCompatible = contains($driver, ['maria', 'mysql']);
|
$isMysqlCompatible = contains($driver, ['maria', 'mysql']);
|
||||||
|
|
||||||
$resolveDriver = static fn () => match ($driver) {
|
$doctrineDriver = match ($driver) {
|
||||||
'postgres' => 'pdo_pgsql',
|
'postgres' => 'pdo_pgsql',
|
||||||
'mssql' => 'pdo_sqlsrv',
|
'mssql' => 'pdo_sqlsrv',
|
||||||
default => 'pdo_mysql',
|
default => 'pdo_mysql',
|
||||||
@@ -23,36 +24,40 @@ return (static function (): array {
|
|||||||
$value = $envVar->loadFromEnv();
|
$value = $envVar->loadFromEnv();
|
||||||
return $value === null ? null : (string) $value;
|
return $value === null ? null : (string) $value;
|
||||||
};
|
};
|
||||||
$resolveDefaultPort = static fn () => match ($driver) {
|
$charset = match ($driver) {
|
||||||
'postgres' => '5432',
|
|
||||||
'mssql' => '1433',
|
|
||||||
default => '3306',
|
|
||||||
};
|
|
||||||
$resolveCharset = static fn () => match ($driver) {
|
|
||||||
// This does not determine charsets or collations in tables or columns, but the charset used in the data
|
// 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.
|
// flowing in the connection, so it has to match what has been set in the database.
|
||||||
'maria', 'mysql' => 'utf8mb4',
|
'maria', 'mysql' => 'utf8mb4',
|
||||||
'postgres' => 'utf8',
|
'postgres' => 'utf8',
|
||||||
default => null,
|
default => null,
|
||||||
};
|
};
|
||||||
|
$driverOptions = match ($driver) {
|
||||||
$resolveConnection = static fn () => 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' => [
|
null, 'sqlite' => [
|
||||||
'driver' => 'pdo_sqlite',
|
'driver' => 'pdo_sqlite',
|
||||||
'path' => 'data/database.sqlite',
|
'path' => 'data/database.sqlite',
|
||||||
],
|
],
|
||||||
default => [
|
default => [
|
||||||
'driver' => $resolveDriver(),
|
'driver' => $doctrineDriver,
|
||||||
'dbname' => EnvVars::DB_NAME->loadFromEnv('shlink'),
|
'dbname' => EnvVars::DB_NAME->loadFromEnv(),
|
||||||
'user' => $readCredentialAsString(EnvVars::DB_USER),
|
'user' => $readCredentialAsString(EnvVars::DB_USER),
|
||||||
'password' => $readCredentialAsString(EnvVars::DB_PASSWORD),
|
'password' => $readCredentialAsString(EnvVars::DB_PASSWORD),
|
||||||
'host' => EnvVars::DB_HOST->loadFromEnv(EnvVars::DB_UNIX_SOCKET->loadFromEnv()),
|
'host' => EnvVars::DB_HOST->loadFromEnv(),
|
||||||
'port' => EnvVars::DB_PORT->loadFromEnv($resolveDefaultPort()),
|
'port' => EnvVars::DB_PORT->loadFromEnv(),
|
||||||
'unix_socket' => $isMysqlCompatible ? EnvVars::DB_UNIX_SOCKET->loadFromEnv() : null,
|
'unix_socket' => $isMysqlCompatible ? EnvVars::DB_UNIX_SOCKET->loadFromEnv() : null,
|
||||||
'charset' => $resolveCharset(),
|
'charset' => $charset,
|
||||||
'driverOptions' => $driver !== 'mssql' ? [] : [
|
'driverOptions' => $driverOptions,
|
||||||
'TrustServerCertificate' => 'true',
|
|
||||||
],
|
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -68,7 +73,7 @@ return (static function (): array {
|
|||||||
Events::postFlush => [ShortUrlVisitsCountTracker::class, OrphanVisitsCountTracker::class],
|
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' => [
|
'geolite2' => [
|
||||||
'db_location' => __DIR__ . '/../../data/GeoLite2-City.mmdb',
|
'db_location' => __DIR__ . '/../../data/GeoLite2-City.mmdb',
|
||||||
'temp_dir' => __DIR__ . '/../../data',
|
'temp_dir' => __DIR__ . '/../../data/temp-geolite',
|
||||||
'license_key' => EnvVars::GEOLITE_LICENSE_KEY->loadFromEnv(),
|
'license_key' => EnvVars::GEOLITE_LICENSE_KEY->loadFromEnv(),
|
||||||
],
|
],
|
||||||
|
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ return [
|
|||||||
'enabled_options' => [
|
'enabled_options' => [
|
||||||
Option\Server\RuntimeConfigOption::class,
|
Option\Server\RuntimeConfigOption::class,
|
||||||
Option\Server\MemoryLimitConfigOption::class,
|
Option\Server\MemoryLimitConfigOption::class,
|
||||||
|
Option\Server\LogsFormatConfigOption::class,
|
||||||
Option\Database\DatabaseDriverConfigOption::class,
|
Option\Database\DatabaseDriverConfigOption::class,
|
||||||
Option\Database\DatabaseNameConfigOption::class,
|
Option\Database\DatabaseNameConfigOption::class,
|
||||||
Option\Database\DatabaseHostConfigOption::class,
|
Option\Database\DatabaseHostConfigOption::class,
|
||||||
@@ -20,6 +21,7 @@ return [
|
|||||||
Option\Database\DatabaseUserConfigOption::class,
|
Option\Database\DatabaseUserConfigOption::class,
|
||||||
Option\Database\DatabasePasswordConfigOption::class,
|
Option\Database\DatabasePasswordConfigOption::class,
|
||||||
Option\Database\DatabaseUnixSocketConfigOption::class,
|
Option\Database\DatabaseUnixSocketConfigOption::class,
|
||||||
|
Option\Database\DatabaseUseEncryptionConfigOption::class,
|
||||||
Option\UrlShortener\ShortDomainHostConfigOption::class,
|
Option\UrlShortener\ShortDomainHostConfigOption::class,
|
||||||
Option\UrlShortener\ShortDomainSchemaConfigOption::class,
|
Option\UrlShortener\ShortDomainSchemaConfigOption::class,
|
||||||
Option\Redirect\BaseUrlRedirectConfigOption::class,
|
Option\Redirect\BaseUrlRedirectConfigOption::class,
|
||||||
@@ -31,6 +33,8 @@ return [
|
|||||||
Option\Cache\CacheNamespaceConfigOption::class,
|
Option\Cache\CacheNamespaceConfigOption::class,
|
||||||
Option\Redis\RedisServersConfigOption::class,
|
Option\Redis\RedisServersConfigOption::class,
|
||||||
Option\Redis\RedisSentinelServiceConfigOption::class,
|
Option\Redis\RedisSentinelServiceConfigOption::class,
|
||||||
|
Option\Redis\RedisServersUserConfigOption::class,
|
||||||
|
Option\Redis\RedisServersPasswordConfigOption::class,
|
||||||
Option\Redis\RedisPubSubConfigOption::class,
|
Option\Redis\RedisPubSubConfigOption::class,
|
||||||
Option\UrlShortener\ShortCodeLengthOption::class,
|
Option\UrlShortener\ShortCodeLengthOption::class,
|
||||||
Option\Mercure\EnableMercureConfigOption::class,
|
Option\Mercure\EnableMercureConfigOption::class,
|
||||||
@@ -40,11 +44,14 @@ return [
|
|||||||
Option\UrlShortener\GeoLiteLicenseKeyConfigOption::class,
|
Option\UrlShortener\GeoLiteLicenseKeyConfigOption::class,
|
||||||
Option\UrlShortener\RedirectStatusCodeConfigOption::class,
|
Option\UrlShortener\RedirectStatusCodeConfigOption::class,
|
||||||
Option\UrlShortener\RedirectCacheLifeTimeConfigOption::class,
|
Option\UrlShortener\RedirectCacheLifeTimeConfigOption::class,
|
||||||
|
Option\UrlShortener\RedirectCacheVisibilityConfigOption::class,
|
||||||
Option\UrlShortener\AutoResolveTitlesConfigOption::class,
|
Option\UrlShortener\AutoResolveTitlesConfigOption::class,
|
||||||
Option\UrlShortener\AppendExtraPathConfigOption::class,
|
Option\UrlShortener\ExtraPathModeConfigOption::class,
|
||||||
Option\UrlShortener\EnableMultiSegmentSlugsConfigOption::class,
|
Option\UrlShortener\EnableMultiSegmentSlugsConfigOption::class,
|
||||||
Option\UrlShortener\EnableTrailingSlashConfigOption::class,
|
Option\UrlShortener\EnableTrailingSlashConfigOption::class,
|
||||||
Option\UrlShortener\ShortUrlModeConfigOption::class,
|
Option\UrlShortener\ShortUrlModeConfigOption::class,
|
||||||
|
Option\Robots\RobotsAllowAllShortUrlsConfigOption::class,
|
||||||
|
Option\Robots\RobotsUserAgentsConfigOption::class,
|
||||||
Option\Tracking\IpAnonymizationConfigOption::class,
|
Option\Tracking\IpAnonymizationConfigOption::class,
|
||||||
Option\Tracking\OrphanVisitsTrackingConfigOption::class,
|
Option\Tracking\OrphanVisitsTrackingConfigOption::class,
|
||||||
Option\Tracking\DisableTrackParamConfigOption::class,
|
Option\Tracking\DisableTrackParamConfigOption::class,
|
||||||
@@ -53,15 +60,6 @@ return [
|
|||||||
Option\Tracking\DisableIpTrackingConfigOption::class,
|
Option\Tracking\DisableIpTrackingConfigOption::class,
|
||||||
Option\Tracking\DisableReferrerTrackingConfigOption::class,
|
Option\Tracking\DisableReferrerTrackingConfigOption::class,
|
||||||
Option\Tracking\DisableUaTrackingConfigOption::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\RabbitMqEnabledConfigOption::class,
|
||||||
Option\RabbitMq\RabbitMqHostConfigOption::class,
|
Option\RabbitMq\RabbitMqHostConfigOption::class,
|
||||||
Option\RabbitMq\RabbitMqUseSslConfigOption::class,
|
Option\RabbitMq\RabbitMqUseSslConfigOption::class,
|
||||||
@@ -73,6 +71,11 @@ return [
|
|||||||
Option\Matomo\MatomoBaseUrlConfigOption::class,
|
Option\Matomo\MatomoBaseUrlConfigOption::class,
|
||||||
Option\Matomo\MatomoSiteIdConfigOption::class,
|
Option\Matomo\MatomoSiteIdConfigOption::class,
|
||||||
Option\Matomo\MatomoApiTokenConfigOption::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' => [
|
'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\Logger\LoggerType;
|
||||||
use Shlinkio\Shlink\Common\Middleware\AccessLogMiddleware;
|
use Shlinkio\Shlink\Common\Middleware\AccessLogMiddleware;
|
||||||
use Shlinkio\Shlink\Common\Middleware\RequestIdMiddleware;
|
use Shlinkio\Shlink\Common\Middleware\RequestIdMiddleware;
|
||||||
|
use Shlinkio\Shlink\Core\Config\EnvVars;
|
||||||
use Shlinkio\Shlink\Core\EventDispatcher\Helper\RequestIdProvider;
|
use Shlinkio\Shlink\Core\EventDispatcher\Helper\RequestIdProvider;
|
||||||
use Shlinkio\Shlink\EventDispatcher\Util\RequestIdProviderInterface;
|
use Shlinkio\Shlink\EventDispatcher\Util\RequestIdProviderInterface;
|
||||||
|
|
||||||
|
use function Shlinkio\Shlink\Config\env;
|
||||||
use function Shlinkio\Shlink\Config\runningInRoadRunner;
|
use function Shlinkio\Shlink\Config\runningInRoadRunner;
|
||||||
|
|
||||||
return (static function (): array {
|
return (static function (): array {
|
||||||
$common = [
|
$isDev = EnvVars::isDevEnv();
|
||||||
'level' => Level::Info->value,
|
$format = EnvVars::LOGS_FORMAT->loadFromEnv();
|
||||||
|
$buildCommonConfig = static fn (bool $addNewLine = false) => [
|
||||||
|
'level' => $isDev ? Level::Debug->value : Level::Info->value,
|
||||||
'processors' => [RequestIdMiddleware::class],
|
'processors' => [RequestIdMiddleware::class],
|
||||||
'line_format' =>
|
'formatter' => [
|
||||||
'[%datetime%] [%extra.' . RequestIdMiddleware::ATTRIBUTE . '%] %channel%.%level_name% - %message%',
|
'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 [
|
return [
|
||||||
|
|
||||||
'logger' => [
|
'logger' => [
|
||||||
'Shlink' => [
|
'Shlink' => $useStreamForShlinkLogger ? [
|
||||||
|
'type' => LoggerType::STREAM->value,
|
||||||
|
'destination' => 'php://stderr',
|
||||||
|
...$buildCommonConfig(),
|
||||||
|
] : [
|
||||||
'type' => LoggerType::FILE->value,
|
'type' => LoggerType::FILE->value,
|
||||||
...$common,
|
...$buildCommonConfig(),
|
||||||
],
|
],
|
||||||
'Access' => [
|
'Access' => [
|
||||||
'type' => LoggerType::STREAM->value,
|
'type' => LoggerType::STREAM->value,
|
||||||
'destination' => 'php://stderr',
|
'destination' => 'php://stderr',
|
||||||
'add_new_line' => ! runningInRoadRunner(),
|
...$buildCommonConfig(! runningInRoadRunner()),
|
||||||
...$common,
|
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
|
|
||||||
|
|||||||
@@ -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\Hub;
|
||||||
use Symfony\Component\Mercure\HubInterface;
|
use Symfony\Component\Mercure\HubInterface;
|
||||||
|
|
||||||
return (static function (): array {
|
return [
|
||||||
$publicUrl = EnvVars::MERCURE_PUBLIC_HUB_URL->loadFromEnv();
|
|
||||||
|
|
||||||
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' => [
|
'dependencies' => [
|
||||||
'public_hub_url' => $publicUrl,
|
'delegators' => [
|
||||||
'internal_hub_url' => EnvVars::MERCURE_INTERNAL_HUB_URL->loadFromEnv($publicUrl),
|
LcobucciJwtProvider::class => [
|
||||||
'jwt_secret' => EnvVars::MERCURE_JWT_SECRET->loadFromEnv(),
|
LazyServiceFactory::class,
|
||||||
'jwt_issuer' => 'Shlink',
|
|
||||||
],
|
|
||||||
|
|
||||||
'dependencies' => [
|
|
||||||
'delegators' => [
|
|
||||||
LcobucciJwtProvider::class => [
|
|
||||||
LazyServiceFactory::class,
|
|
||||||
],
|
|
||||||
Hub::class => [
|
|
||||||
LazyServiceFactory::class,
|
|
||||||
],
|
|
||||||
],
|
],
|
||||||
'lazy_services' => [
|
Hub::class => [
|
||||||
'class_map' => [
|
LazyServiceFactory::class,
|
||||||
LcobucciJwtProvider::class => LcobucciJwtProvider::class,
|
|
||||||
Hub::class => HubInterface::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\AccessLogMiddleware;
|
||||||
use Shlinkio\Shlink\Common\Middleware\ContentLengthMiddleware;
|
use Shlinkio\Shlink\Common\Middleware\ContentLengthMiddleware;
|
||||||
use Shlinkio\Shlink\Common\Middleware\RequestIdMiddleware;
|
use Shlinkio\Shlink\Common\Middleware\RequestIdMiddleware;
|
||||||
|
use Shlinkio\Shlink\Core\Geolocation\Middleware\IpGeolocationMiddleware;
|
||||||
|
|
||||||
return [
|
return [
|
||||||
|
|
||||||
@@ -67,8 +68,11 @@ return [
|
|||||||
],
|
],
|
||||||
'not-found' => [
|
'not-found' => [
|
||||||
'middleware' => [
|
'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,
|
IpAddress::class,
|
||||||
|
IpGeolocationMiddleware::class,
|
||||||
|
|
||||||
Core\ErrorHandler\NotFoundTypeResolverMiddleware::class,
|
Core\ErrorHandler\NotFoundTypeResolverMiddleware::class,
|
||||||
Core\ShortUrl\Middleware\ExtraPathRedirectMiddleware::class,
|
Core\ShortUrl\Middleware\ExtraPathRedirectMiddleware::class,
|
||||||
Core\ErrorHandler\NotFoundTrackerMiddleware::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 [
|
return [
|
||||||
|
|
||||||
|
// This config is used by shlink-common. Do not delete
|
||||||
'rabbitmq' => [
|
'rabbitmq' => [
|
||||||
'enabled' => (bool) EnvVars::RABBITMQ_ENABLED->loadFromEnv(false),
|
'enabled' => (bool) EnvVars::RABBITMQ_ENABLED->loadFromEnv(),
|
||||||
'host' => EnvVars::RABBITMQ_HOST->loadFromEnv(),
|
'host' => EnvVars::RABBITMQ_HOST->loadFromEnv(),
|
||||||
'use_ssl' => (bool) EnvVars::RABBITMQ_USE_SSL->loadFromEnv(false),
|
'use_ssl' => (bool) EnvVars::RABBITMQ_USE_SSL->loadFromEnv(),
|
||||||
'port' => (int) EnvVars::RABBITMQ_PORT->loadFromEnv('5672'),
|
'port' => (int) EnvVars::RABBITMQ_PORT->loadFromEnv(),
|
||||||
'user' => EnvVars::RABBITMQ_USER->loadFromEnv(),
|
'user' => EnvVars::RABBITMQ_USER->loadFromEnv(),
|
||||||
'password' => EnvVars::RABBITMQ_PASSWORD->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 [
|
return [
|
||||||
|
|
||||||
'router' => [
|
'router' => [
|
||||||
'base_path' => EnvVars::BASE_PATH->loadFromEnv(''),
|
'base_path' => EnvVars::BASE_PATH->loadFromEnv(),
|
||||||
|
|
||||||
'fastroute' => [
|
'fastroute' => [
|
||||||
// Disabling config cache for cli, ensures it's never used for RoadRunner, and also that console
|
// Disabling config cache for cli, ensures it's never used for RoadRunner, and also that console
|
||||||
// commands don't generate a cache file that's then used by php-fpm web executions
|
// 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',
|
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 RKA\Middleware\IpAddress;
|
||||||
use Shlinkio\Shlink\Core\Action as CoreAction;
|
use Shlinkio\Shlink\Core\Action as CoreAction;
|
||||||
use Shlinkio\Shlink\Core\Config\EnvVars;
|
use Shlinkio\Shlink\Core\Config\EnvVars;
|
||||||
|
use Shlinkio\Shlink\Core\Geolocation\Middleware\IpGeolocationMiddleware;
|
||||||
use Shlinkio\Shlink\Core\ShortUrl\Middleware\TrimTrailingSlashMiddleware;
|
use Shlinkio\Shlink\Core\ShortUrl\Middleware\TrimTrailingSlashMiddleware;
|
||||||
use Shlinkio\Shlink\Rest\Action;
|
use Shlinkio\Shlink\Rest\Action;
|
||||||
use Shlinkio\Shlink\Rest\ConfigProvider;
|
use Shlinkio\Shlink\Rest\ConfigProvider;
|
||||||
@@ -19,9 +20,7 @@ use function sprintf;
|
|||||||
return (static function (): array {
|
return (static function (): array {
|
||||||
$dropDomainMiddleware = Middleware\ShortUrl\DropDefaultDomainFromRequestMiddleware::class;
|
$dropDomainMiddleware = Middleware\ShortUrl\DropDefaultDomainFromRequestMiddleware::class;
|
||||||
$overrideDomainMiddleware = Middleware\ShortUrl\OverrideDomainMiddleware::class;
|
$overrideDomainMiddleware = Middleware\ShortUrl\OverrideDomainMiddleware::class;
|
||||||
|
$shortUrlRouteSuffix = EnvVars::SHORT_URL_TRAILING_SLASH->loadFromEnv() ? '[/]' : '';
|
||||||
// TODO This should be based on config, not the env var
|
|
||||||
$shortUrlRouteSuffix = EnvVars::SHORT_URL_TRAILING_SLASH->loadFromEnv(false) ? '[/]' : '';
|
|
||||||
|
|
||||||
return [
|
return [
|
||||||
|
|
||||||
@@ -90,23 +89,17 @@ return (static function (): array {
|
|||||||
'path' => '/{shortCode}/track',
|
'path' => '/{shortCode}/track',
|
||||||
'middleware' => [
|
'middleware' => [
|
||||||
IpAddress::class,
|
IpAddress::class,
|
||||||
|
IpGeolocationMiddleware::class,
|
||||||
CoreAction\PixelAction::class,
|
CoreAction\PixelAction::class,
|
||||||
],
|
],
|
||||||
'allowed_methods' => [RequestMethodInterface::METHOD_GET],
|
'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,
|
'name' => CoreAction\RedirectAction::class,
|
||||||
'path' => sprintf('/{shortCode}%s', $shortUrlRouteSuffix),
|
'path' => sprintf('/{shortCode}%s', $shortUrlRouteSuffix),
|
||||||
'middleware' => [
|
'middleware' => [
|
||||||
IpAddress::class,
|
IpAddress::class,
|
||||||
|
IpGeolocationMiddleware::class,
|
||||||
TrimTrailingSlashMiddleware::class,
|
TrimTrailingSlashMiddleware::class,
|
||||||
CoreAction\RedirectAction::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 Laminas\Diactoros;
|
||||||
use Mezzio;
|
use Mezzio;
|
||||||
use Mezzio\ProblemDetails;
|
use Mezzio\ProblemDetails;
|
||||||
use Shlinkio\Shlink\Config\ConfigAggregator\EnvVarLoaderProvider;
|
use Shlinkio\Shlink\Core\Config\EnvVars;
|
||||||
|
|
||||||
use function Shlinkio\Shlink\Config\env;
|
return new ConfigAggregator\ConfigAggregator(
|
||||||
use function Shlinkio\Shlink\Core\enumValues;
|
|
||||||
|
|
||||||
$isTestEnv = env('APP_ENV') === 'test';
|
|
||||||
|
|
||||||
return (new ConfigAggregator\ConfigAggregator(
|
|
||||||
providers: [
|
providers: [
|
||||||
! $isTestEnv
|
|
||||||
? new EnvVarLoaderProvider('config/params/generated_config.php', enumValues(Core\Config\EnvVars::class))
|
|
||||||
: new ConfigAggregator\ArrayProvider([]),
|
|
||||||
Mezzio\ConfigProvider::class,
|
Mezzio\ConfigProvider::class,
|
||||||
Mezzio\Router\ConfigProvider::class,
|
Mezzio\Router\ConfigProvider::class,
|
||||||
Mezzio\Router\FastRouteRouter\ConfigProvider::class,
|
Mezzio\Router\FastRouteRouter\ConfigProvider::class,
|
||||||
@@ -34,10 +26,10 @@ return (new ConfigAggregator\ConfigAggregator(
|
|||||||
CLI\ConfigProvider::class,
|
CLI\ConfigProvider::class,
|
||||||
Rest\ConfigProvider::class,
|
Rest\ConfigProvider::class,
|
||||||
new ConfigAggregator\PhpFileProvider('config/autoload/{,*.}global.php'),
|
new ConfigAggregator\PhpFileProvider('config/autoload/{,*.}global.php'),
|
||||||
// Local config should not be loaded during tests, whereas test config should be loaded ONLY during tests
|
// Test config should be loaded ONLY during tests
|
||||||
new ConfigAggregator\PhpFileProvider(
|
EnvVars::isTestEnv()
|
||||||
$isTestEnv ? 'config/test/*.global.php' : 'config/autoload/{,*.}local.php',
|
? new ConfigAggregator\PhpFileProvider('config/test/*.global.php')
|
||||||
),
|
: new ConfigAggregator\ArrayProvider([]),
|
||||||
// Routes have to be loaded last
|
// Routes have to be loaded last
|
||||||
new ConfigAggregator\PhpFileProvider('config/autoload/routes.config.php'),
|
new ConfigAggregator\PhpFileProvider('config/autoload/routes.config.php'),
|
||||||
],
|
],
|
||||||
@@ -47,4 +39,4 @@ return (new ConfigAggregator\ConfigAggregator(
|
|||||||
Core\Config\PostProcessor\MultiSegmentSlugProcessor::class,
|
Core\Config\PostProcessor\MultiSegmentSlugProcessor::class,
|
||||||
Core\Config\PostProcessor\ShortUrlMethodsProcessor::class,
|
Core\Config\PostProcessor\ShortUrlMethodsProcessor::class,
|
||||||
],
|
],
|
||||||
))->getMergedConfig();
|
)->getMergedConfig();
|
||||||
|
|||||||
@@ -11,14 +11,30 @@ const DEFAULT_SHORT_CODES_LENGTH = 5;
|
|||||||
const MIN_SHORT_CODES_LENGTH = 4;
|
const MIN_SHORT_CODES_LENGTH = 4;
|
||||||
const DEFAULT_REDIRECT_STATUS_CODE = RedirectStatus::STATUS_302;
|
const DEFAULT_REDIRECT_STATUS_CODE = RedirectStatus::STATUS_302;
|
||||||
const DEFAULT_REDIRECT_CACHE_LIFETIME = 30;
|
const DEFAULT_REDIRECT_CACHE_LIFETIME = 30;
|
||||||
|
const DEFAULT_REDIRECT_CACHE_VISIBILITY = 'private';
|
||||||
const LOCAL_LOCK_FACTORY = 'Shlinkio\Shlink\LocalLockFactory';
|
const LOCAL_LOCK_FACTORY = 'Shlinkio\Shlink\LocalLockFactory';
|
||||||
const TITLE_TAG_VALUE = '/<title[^>]*>(.*?)<\/title>/i'; // Matches the value inside a html title tag
|
|
||||||
const LOOSE_URI_MATCHER = '/(.+)\:(.+)/i'; // Matches anything starting with a schema.
|
const LOOSE_URI_MATCHER = '/(.+)\:(.+)/i'; // Matches anything starting with a schema.
|
||||||
const DEFAULT_QR_CODE_SIZE = 300;
|
const IP_ADDRESS_REQUEST_ATTRIBUTE = 'remote_address';
|
||||||
const DEFAULT_QR_CODE_MARGIN = 0;
|
const REDIRECT_URL_REQUEST_ATTRIBUTE = 'redirect_url';
|
||||||
const DEFAULT_QR_CODE_FORMAT = 'png';
|
|
||||||
const DEFAULT_QR_CODE_ERROR_CORRECTION = 'l';
|
/**
|
||||||
const DEFAULT_QR_CODE_ROUND_BLOCK_SIZE = true;
|
* List of ISO 3166-1 alpha-2 two-letter country codes https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2
|
||||||
const DEFAULT_QR_CODE_ENABLED_FOR_DISABLED_SHORT_URLS = true;
|
*/
|
||||||
const DEFAULT_QR_CODE_COLOR = '#000000'; // Black
|
const ISO_COUNTRY_CODES = [
|
||||||
const DEFAULT_QR_CODE_BG_COLOR = '#ffffff'; // White
|
'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 Shlinkio\Shlink\Core\Config\EnvVars;
|
||||||
use Symfony\Component\Lock;
|
use Symfony\Component\Lock;
|
||||||
|
|
||||||
|
use function Shlinkio\Shlink\Config\loadEnvVarsFromConfig;
|
||||||
|
use function Shlinkio\Shlink\Core\enumValues;
|
||||||
|
|
||||||
use const Shlinkio\Shlink\LOCAL_LOCK_FACTORY;
|
use const Shlinkio\Shlink\LOCAL_LOCK_FACTORY;
|
||||||
|
|
||||||
|
// Set current directory to the project's root directory
|
||||||
chdir(dirname(__DIR__));
|
chdir(dirname(__DIR__));
|
||||||
|
|
||||||
require 'vendor/autoload.php';
|
require 'vendor/autoload.php';
|
||||||
|
|
||||||
// Set a default memory limit, but allow custom values
|
// Promote env vars from installer, dev config or test config
|
||||||
ini_set('memory_limit', EnvVars::MEMORY_LIMIT->loadFromEnv('512M'));
|
loadEnvVarsFromConfig(
|
||||||
// This is one of the first files loaded. Configure the timezone here
|
EnvVars::isTestEnv() ? 'config/test/shlink_test_env.php' : 'config/params/*.php',
|
||||||
date_default_timezone_set(EnvVars::TIMEZONE->loadFromEnv(date_default_timezone_get()));
|
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
|
// 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
|
// 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
|
!.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
|
prefetch: 10
|
||||||
|
|
||||||
logs:
|
logs:
|
||||||
|
encoding: console
|
||||||
mode: development
|
mode: development
|
||||||
channels:
|
channels:
|
||||||
http:
|
http:
|
||||||
mode: 'off' # Disable logging as Shlink handles it internally
|
mode: 'off' # Disable logging as Shlink handles it internally
|
||||||
server:
|
server:
|
||||||
|
encoding: console
|
||||||
level: info
|
level: info
|
||||||
metrics:
|
metrics:
|
||||||
|
encoding: console
|
||||||
level: debug
|
level: debug
|
||||||
jobs:
|
jobs:
|
||||||
|
encoding: console
|
||||||
level: debug
|
level: debug
|
||||||
|
|||||||
@@ -35,15 +35,16 @@ jobs:
|
|||||||
prefetch: 10
|
prefetch: 10
|
||||||
|
|
||||||
logs:
|
logs:
|
||||||
encoding: json
|
encoding: console
|
||||||
mode: development
|
mode: development
|
||||||
channels:
|
channels:
|
||||||
http:
|
http:
|
||||||
mode: 'off' # Disable logging as Shlink handles it internally
|
mode: 'off' # Disable logging as Shlink handles it internally
|
||||||
server:
|
server:
|
||||||
encoding: json
|
encoding: console
|
||||||
level: info
|
level: info
|
||||||
metrics:
|
metrics:
|
||||||
level: panic
|
level: panic
|
||||||
jobs:
|
jobs:
|
||||||
|
encoding: console
|
||||||
level: panic
|
level: panic
|
||||||
|
|||||||
@@ -7,18 +7,20 @@ server:
|
|||||||
command: 'php -dopcache.enable_cli=1 -dopcache.validate_timestamps=0 ../../bin/roadrunner-worker.php'
|
command: 'php -dopcache.enable_cli=1 -dopcache.validate_timestamps=0 ../../bin/roadrunner-worker.php'
|
||||||
|
|
||||||
http:
|
http:
|
||||||
address: '0.0.0.0:${PORT:-8080}'
|
address: '${ADDRESS:-0.0.0.0}:${PORT:-8080}'
|
||||||
middleware: ['static']
|
middleware: ['static']
|
||||||
static:
|
static:
|
||||||
dir: '../../public'
|
dir: '../../public'
|
||||||
forbid: ['.php', '.htaccess']
|
forbid: ['.php', '.htaccess']
|
||||||
pool:
|
pool:
|
||||||
num_workers: ${WEB_WORKER_NUM:-0}
|
num_workers: ${WEB_WORKER_NUM:-0}
|
||||||
|
max_jobs: 250 # Restart worker after processing this amount of requests to mitigate memory leaks
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
timeout: 300 # 5 minutes
|
timeout: 300 # 5 minutes
|
||||||
pool:
|
pool:
|
||||||
num_workers: ${TASK_WORKER_NUM:-0}
|
num_workers: ${TASK_WORKER_NUM:-0}
|
||||||
|
max_jobs: 250 # Restart worker after processing this amount of jobs to mitigate memory leaks
|
||||||
consume: ['shlink']
|
consume: ['shlink']
|
||||||
pipelines:
|
pipelines:
|
||||||
shlink:
|
shlink:
|
||||||
@@ -28,11 +30,14 @@ jobs:
|
|||||||
prefetch: 10
|
prefetch: 10
|
||||||
|
|
||||||
logs:
|
logs:
|
||||||
|
encoding: ${LOGS_FORMAT:-console}
|
||||||
mode: production
|
mode: production
|
||||||
channels:
|
channels:
|
||||||
http:
|
http:
|
||||||
mode: 'off' # Disable logging as Shlink handles it internally
|
mode: 'off' # Disable logging as Shlink handles it internally
|
||||||
server:
|
server:
|
||||||
|
encoding: ${LOGS_FORMAT:-console}
|
||||||
level: info
|
level: info
|
||||||
jobs:
|
jobs:
|
||||||
|
encoding: ${LOGS_FORMAT:-console}
|
||||||
level: debug
|
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';
|
. '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 '
|
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';
|
. '(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 '
|
const WINDOWS_USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) '
|
||||||
. 'Gecko) Chrome/109.0.0.0 Safari/537.36 Edg/109.0.1518.61';
|
. '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,
|
ConfigAggregator::ENABLE_CACHE => false,
|
||||||
FastRouteRouter::CONFIG_CACHE_ENABLED => false,
|
FastRouteRouter::CONFIG_CACHE_ENABLED => false,
|
||||||
|
|
||||||
'url_shortener' => [
|
|
||||||
'domain' => [
|
|
||||||
'schema' => 'http',
|
|
||||||
'hostname' => 's.test',
|
|
||||||
],
|
|
||||||
],
|
|
||||||
|
|
||||||
'routes' => [
|
'routes' => [
|
||||||
// This route is used to test that title resolution is skipped if the long URL times out
|
// 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' => [
|
'dependencies' => [
|
||||||
'services' => [
|
'services' => [
|
||||||
'shlink_test_api_client' => new Client([
|
'shlink_test_api_client' => new Client([
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
set -ex
|
set -ex
|
||||||
|
|
||||||
curl https://packages.microsoft.com/keys/microsoft.asc | apt-key add -
|
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
|
apt-get update
|
||||||
ACCEPT_EULA=Y apt-get install msodbcsql18
|
ACCEPT_EULA=Y apt-get install msodbcsql18
|
||||||
# apt-get install unixodbc-dev
|
# apt-get install unixodbc-dev
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ server {
|
|||||||
|
|
||||||
location ~ \.php$ {
|
location ~ \.php$ {
|
||||||
fastcgi_split_path_info ^(.+\.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;
|
fastcgi_index index.php;
|
||||||
include fastcgi.conf;
|
include fastcgi.conf;
|
||||||
}
|
}
|
||||||
|
|||||||
53
data/infra/frankenphp.Dockerfile
Normal file
53
data/infra/frankenphp.Dockerfile
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
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 postgresql-dev
|
||||||
|
RUN docker-php-ext-install pdo_pgsql
|
||||||
|
|
||||||
|
COPY --from=ghcr.io/php/pie:bin /pie /usr/bin/pie
|
||||||
|
RUN apk add --no-cache libzip-dev zlib-dev && \
|
||||||
|
apk add --no-cache --virtual .phpize-deps $PHPIZE_DEPS linux-headers && \
|
||||||
|
docker-php-ext-install sockets && \
|
||||||
|
pie install xdebug/xdebug && \
|
||||||
|
pie install pecl/zip && \
|
||||||
|
apk del .phpize-deps
|
||||||
|
RUN docker-php-ext-install bcmath
|
||||||
|
|
||||||
|
# Install 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} && \
|
||||||
|
docker-php-ext-enable pdo_sqlsrv && \
|
||||||
|
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.22
|
||||||
MAINTAINER Alejandro Celaya <alejandro@alejandrocelaya.com>
|
MAINTAINER Alejandro Celaya <alejandro@alejandrocelaya.com>
|
||||||
|
|
||||||
ENV APCU_VERSION 5.1.23
|
ENV APCU_VERSION='5.1.24'
|
||||||
ENV PDO_SQLSRV_VERSION 5.12.0
|
ENV PDO_SQLSRV_VERSION='5.12.0'
|
||||||
ENV MS_ODBC_DOWNLOAD 'b/9/f/b9f3cce4-3925-46d4-9f46-da08869c6486'
|
ENV MS_ODBC_DOWNLOAD='7/6/d/76de322a-d860-4894-9945-f0cc5d6a45f8'
|
||||||
ENV MS_ODBC_SQL_VERSION 18_18.1.1.1
|
ENV MS_ODBC_SQL_VERSION='18_18.4.1.1'
|
||||||
|
|
||||||
RUN apk update
|
RUN apk update
|
||||||
|
|
||||||
@@ -22,36 +22,26 @@ RUN docker-php-ext-install pdo_sqlite
|
|||||||
RUN apk add --no-cache icu-dev
|
RUN apk add --no-cache icu-dev
|
||||||
RUN docker-php-ext-install intl
|
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 apk add --no-cache postgresql-dev
|
||||||
RUN docker-php-ext-install pdo_pgsql
|
RUN docker-php-ext-install pdo_pgsql
|
||||||
|
|
||||||
RUN apk add --no-cache --virtual .phpize-deps $PHPIZE_DEPS linux-headers && \
|
COPY --from=ghcr.io/php/pie:bin /pie /usr/bin/pie
|
||||||
|
RUN apk add --no-cache libzip-dev zlib-dev && \
|
||||||
|
apk add --no-cache --virtual .phpize-deps $PHPIZE_DEPS linux-headers && \
|
||||||
docker-php-ext-install sockets && \
|
docker-php-ext-install sockets && \
|
||||||
|
pie install xdebug/xdebug && \
|
||||||
|
pie install pecl/zip && \
|
||||||
|
pie install apcu/apcu && \
|
||||||
apk del .phpize-deps
|
apk del .phpize-deps
|
||||||
RUN docker-php-ext-install bcmath
|
RUN docker-php-ext-install bcmath
|
||||||
|
|
||||||
# Install APCu extension
|
# Install sqlsrv driver
|
||||||
ADD https://pecl.php.net/get/apcu-$APCU_VERSION.tgz /tmp/apcu.tar.gz
|
RUN apk add --update linux-headers && \
|
||||||
RUN mkdir -p /usr/src/php/ext/apcu \
|
wget https://download.microsoft.com/download/${MS_ODBC_DOWNLOAD}/msodbcsql${MS_ODBC_SQL_VERSION}-1_amd64.apk && \
|
||||||
&& 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 && \
|
|
||||||
apk add --allow-untrusted 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 && \
|
apk add --no-cache --virtual .phpize-deps $PHPIZE_DEPS unixodbc-dev && \
|
||||||
pecl install pdo_sqlsrv-${PDO_SQLSRV_VERSION} pcov && \
|
pecl install pdo_sqlsrv-${PDO_SQLSRV_VERSION} && \
|
||||||
docker-php-ext-enable pdo_sqlsrv pcov && \
|
docker-php-ext-enable pdo_sqlsrv && \
|
||||||
apk del .phpize-deps && \
|
apk del .phpize-deps && \
|
||||||
rm msodbcsql${MS_ODBC_SQL_VERSION}-1_amd64.apk
|
rm msodbcsql${MS_ODBC_SQL_VERSION}-1_amd64.apk
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,4 @@
|
|||||||
display_errors=On
|
display_errors=On
|
||||||
error_reporting=-1
|
|
||||||
log_errors_max_len=0
|
log_errors_max_len=0
|
||||||
zend.assertions=1
|
zend.assertions=1
|
||||||
assert.exception=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.22
|
||||||
MAINTAINER Alejandro Celaya <alejandro@alejandrocelaya.com>
|
MAINTAINER Alejandro Celaya <alejandro@alejandrocelaya.com>
|
||||||
|
|
||||||
ENV APCU_VERSION 5.1.23
|
ENV PDO_SQLSRV_VERSION='5.12.0'
|
||||||
ENV PDO_SQLSRV_VERSION 5.12.0
|
ENV MS_ODBC_DOWNLOAD='7/6/d/76de322a-d860-4894-9945-f0cc5d6a45f8'
|
||||||
ENV MS_ODBC_DOWNLOAD 'b/9/f/b9f3cce4-3925-46d4-9f46-da08869c6486'
|
ENV MS_ODBC_SQL_VERSION='18_18.4.1.1'
|
||||||
ENV MS_ODBC_SQL_VERSION 18_18.1.1.1
|
|
||||||
|
|
||||||
RUN apk update
|
RUN apk update
|
||||||
|
|
||||||
@@ -22,36 +21,25 @@ RUN docker-php-ext-install pdo_sqlite
|
|||||||
RUN apk add --no-cache icu-dev
|
RUN apk add --no-cache icu-dev
|
||||||
RUN docker-php-ext-install intl
|
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 apk add --no-cache postgresql-dev
|
||||||
RUN docker-php-ext-install pdo_pgsql
|
RUN docker-php-ext-install pdo_pgsql
|
||||||
|
|
||||||
RUN apk add --no-cache --virtual .phpize-deps $PHPIZE_DEPS linux-headers && \
|
COPY --from=ghcr.io/php/pie:bin /pie /usr/bin/pie
|
||||||
|
RUN apk add --no-cache libzip-dev zlib-dev && \
|
||||||
|
apk add --no-cache --virtual .phpize-deps $PHPIZE_DEPS linux-headers && \
|
||||||
docker-php-ext-install sockets && \
|
docker-php-ext-install sockets && \
|
||||||
|
pie install xdebug/xdebug && \
|
||||||
|
pie install pecl/zip && \
|
||||||
apk del .phpize-deps
|
apk del .phpize-deps
|
||||||
RUN docker-php-ext-install bcmath
|
RUN docker-php-ext-install bcmath
|
||||||
|
|
||||||
# Install APCu extension
|
# Install sqlsrv driver
|
||||||
ADD https://pecl.php.net/get/apcu-$APCU_VERSION.tgz /tmp/apcu.tar.gz
|
RUN apk add --update linux-headers && \
|
||||||
RUN mkdir -p /usr/src/php/ext/apcu \
|
wget https://download.microsoft.com/download/${MS_ODBC_DOWNLOAD}/msodbcsql${MS_ODBC_SQL_VERSION}-1_amd64.apk && \
|
||||||
&& 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 && \
|
|
||||||
apk add --allow-untrusted 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 && \
|
apk add --no-cache --virtual .phpize-deps $PHPIZE_DEPS unixodbc-dev && \
|
||||||
pecl install pdo_sqlsrv-${PDO_SQLSRV_VERSION} pcov && \
|
pecl install pdo_sqlsrv-${PDO_SQLSRV_VERSION} && \
|
||||||
docker-php-ext-enable pdo_sqlsrv pcov && \
|
docker-php-ext-enable pdo_sqlsrv && \
|
||||||
apk del .phpize-deps && \
|
apk del .phpize-deps && \
|
||||||
rm msodbcsql${MS_ODBC_SQL_VERSION}-1_amd64.apk
|
rm msodbcsql${MS_ODBC_SQL_VERSION}-1_amd64.apk
|
||||||
|
|
||||||
@@ -72,5 +60,7 @@ CMD \
|
|||||||
if [[ ! -d "./vendor" ]]; then /usr/local/bin/composer install ; fi && \
|
if [[ ! -d "./vendor" ]]; then /usr/local/bin/composer install ; fi && \
|
||||||
# Download roadrunner binary
|
# Download roadrunner binary
|
||||||
if [[ ! -f "./bin/rr" ]]; then ./vendor/bin/rr get --no-interaction --no-config --location bin/ && chmod +x bin/rr ; fi && \
|
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
|
# Create env file if it does not exist yet
|
||||||
until ./bin/rr serve -c config/roadrunner/.rr.dev.yml; do sleep 1 ; done
|
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:
|
services:
|
||||||
shlink_db_mysql:
|
shlink_db_mysql:
|
||||||
|
user: '0'
|
||||||
environment:
|
environment:
|
||||||
MYSQL_DATABASE: shlink_test
|
MYSQL_DATABASE: shlink_test
|
||||||
|
|
||||||
shlink_db_postgres:
|
shlink_db_postgres:
|
||||||
|
user: '0'
|
||||||
environment:
|
environment:
|
||||||
POSTGRES_DB: shlink_test
|
POSTGRES_DB: shlink_test
|
||||||
|
|
||||||
shlink_db_maria:
|
shlink_db_maria:
|
||||||
|
user: '0'
|
||||||
environment:
|
environment:
|
||||||
MYSQL_DATABASE: shlink_test
|
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:
|
services:
|
||||||
shlink_nginx:
|
shlink_nginx:
|
||||||
container_name: shlink_nginx
|
container_name: shlink_nginx
|
||||||
@@ -15,6 +13,7 @@ services:
|
|||||||
|
|
||||||
shlink_php:
|
shlink_php:
|
||||||
container_name: shlink_php
|
container_name: shlink_php
|
||||||
|
user: 1000:1000
|
||||||
build:
|
build:
|
||||||
context: .
|
context: .
|
||||||
dockerfile: ./data/infra/php.Dockerfile
|
dockerfile: ./data/infra/php.Dockerfile
|
||||||
@@ -36,11 +35,13 @@ services:
|
|||||||
- shlink_matomo
|
- shlink_matomo
|
||||||
environment:
|
environment:
|
||||||
LC_ALL: C
|
LC_ALL: C
|
||||||
|
DEFAULT_DOMAIN: localhost:8000
|
||||||
extra_hosts:
|
extra_hosts:
|
||||||
- 'host.docker.internal:host-gateway'
|
- 'host.docker.internal:host-gateway'
|
||||||
|
|
||||||
shlink_roadrunner:
|
shlink_roadrunner:
|
||||||
container_name: shlink_roadrunner
|
container_name: shlink_roadrunner
|
||||||
|
user: 1000:1000
|
||||||
build:
|
build:
|
||||||
context: .
|
context: .
|
||||||
dockerfile: ./data/infra/roadrunner.Dockerfile
|
dockerfile: ./data/infra/roadrunner.Dockerfile
|
||||||
@@ -65,8 +66,40 @@ services:
|
|||||||
extra_hosts:
|
extra_hosts:
|
||||||
- 'host.docker.internal:host-gateway'
|
- '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:
|
shlink_db_mysql:
|
||||||
container_name: shlink_db_mysql
|
container_name: shlink_db_mysql
|
||||||
|
user: 1000:1000
|
||||||
image: mysql:8.0
|
image: mysql:8.0
|
||||||
ports:
|
ports:
|
||||||
- "3307:3306"
|
- "3307:3306"
|
||||||
@@ -79,7 +112,8 @@ services:
|
|||||||
|
|
||||||
shlink_db_postgres:
|
shlink_db_postgres:
|
||||||
container_name: shlink_db_postgres
|
container_name: shlink_db_postgres
|
||||||
image: postgres:12.2-alpine
|
user: 1000:1000
|
||||||
|
image: postgres:16.3-alpine
|
||||||
ports:
|
ports:
|
||||||
- "5434:5432"
|
- "5434:5432"
|
||||||
volumes:
|
volumes:
|
||||||
@@ -92,6 +126,7 @@ services:
|
|||||||
|
|
||||||
shlink_db_maria:
|
shlink_db_maria:
|
||||||
container_name: shlink_db_maria
|
container_name: shlink_db_maria
|
||||||
|
user: 1000:1000
|
||||||
image: mariadb:10.7
|
image: mariadb:10.7
|
||||||
ports:
|
ports:
|
||||||
- "3308:3306"
|
- "3308:3306"
|
||||||
@@ -105,7 +140,7 @@ services:
|
|||||||
|
|
||||||
shlink_db_ms:
|
shlink_db_ms:
|
||||||
container_name: shlink_db_ms
|
container_name: shlink_db_ms
|
||||||
image: mcr.microsoft.com/mssql/server:2019-latest
|
image: mcr.microsoft.com/mssql/server:2022-latest
|
||||||
ports:
|
ports:
|
||||||
- "1433:1433"
|
- "1433:1433"
|
||||||
environment:
|
environment:
|
||||||
@@ -114,13 +149,13 @@ services:
|
|||||||
|
|
||||||
shlink_redis:
|
shlink_redis:
|
||||||
container_name: shlink_redis
|
container_name: shlink_redis
|
||||||
image: redis:6.2-alpine
|
image: redis:7.4-alpine
|
||||||
ports:
|
ports:
|
||||||
- "6380:6379"
|
- "6380:6379"
|
||||||
|
|
||||||
shlink_redis_acl:
|
shlink_redis_acl:
|
||||||
container_name: 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"]
|
command: ["redis-server", "/usr/local/etc/redis/redis.conf"]
|
||||||
ports:
|
ports:
|
||||||
- "6382:6379"
|
- "6382:6379"
|
||||||
@@ -140,14 +175,14 @@ services:
|
|||||||
|
|
||||||
shlink_mercure:
|
shlink_mercure:
|
||||||
container_name: shlink_mercure
|
container_name: shlink_mercure
|
||||||
image: dunglas/mercure:v0.15
|
image: dunglas/mercure:v0.18
|
||||||
ports:
|
ports:
|
||||||
- "3080:80"
|
- "3080:80"
|
||||||
environment:
|
environment:
|
||||||
SERVER_NAME: ":80"
|
SERVER_NAME: ":80"
|
||||||
MERCURE_PUBLISHER_JWT_KEY: mercure_jwt_key_long_enough_to_avoid_error
|
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_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:
|
shlink_rabbitmq:
|
||||||
container_name: 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
|
cd /etc/shlink
|
||||||
|
|
||||||
# Create data directories if they do not exist. This allows data dir to be mounted as an empty dir if needed
|
# 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"
|
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
|
# 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"
|
flags="${flags} --skip-download-geolite"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# If INITIAL_API_KEY was provided, create an initial API key
|
# If INITIAL_API_KEY was provided, create an initial API key
|
||||||
if [ -n "${INITIAL_API_KEY}" ]; then
|
if [ -n "${initial_api_key}" ]; then
|
||||||
flags="${flags} --initial-api-key=${INITIAL_API_KEY}"
|
flags="${flags} --initial-api-key=${initial_api_key}"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
php vendor/bin/shlink-installer init ${flags}
|
php vendor/bin/shlink-installer init ${flags}
|
||||||
|
|
||||||
if [ "$SHLINK_RUNTIME" = 'rr' ]; then
|
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
|
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.
|
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)
|
* [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-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)
|
* [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": {
|
"crawlable": {
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"description": "Tells if this URL will be included as 'Allow' in Shlink's robots.txt."
|
"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": {
|
"example": {
|
||||||
@@ -164,7 +172,9 @@
|
|||||||
},
|
},
|
||||||
"domain": "example.com",
|
"domain": "example.com",
|
||||||
"title": "The title",
|
"title": "The title",
|
||||||
"crawlable": false
|
"crawlable": false,
|
||||||
|
"forwardQuery": false,
|
||||||
|
"hasRedirectRules": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"ShortUrlMeta": {
|
"ShortUrlMeta": {
|
||||||
@@ -237,6 +247,11 @@
|
|||||||
"type": "string",
|
"type": "string",
|
||||||
"nullable": true,
|
"nullable": true,
|
||||||
"description": "The originally visited URL that triggered the tracking of this visit"
|
"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": {
|
"example": {
|
||||||
|
|||||||
686
docs/changelog-archive/CHANGELOG-3.x.md
Normal file
686
docs/changelog-archive/CHANGELOG-3.x.md
Normal file
@@ -0,0 +1,686 @@
|
|||||||
|
# CHANGELOG 3.x
|
||||||
|
|
||||||
|
All notable changes to this project will be documented in this file.
|
||||||
|
|
||||||
|
The format is based on [Keep a Changelog](https://keepachangelog.com), and this project adheres to [Semantic Versioning](https://semver.org).
|
||||||
|
|
||||||
|
## [3.7.3] - 2024-01-04
|
||||||
|
### Added
|
||||||
|
* *Nothing*
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
* [#1968](https://github.com/shlinkio/shlink/issues/1968) Move migrations from `data` to `module/Core`.
|
||||||
|
|
||||||
|
### Deprecated
|
||||||
|
* *Nothing*
|
||||||
|
|
||||||
|
### Removed
|
||||||
|
* *Nothing*
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
* [#1967](https://github.com/shlinkio/shlink/issues/1967) Allow an empty dir to be mounted in `data` when using the docker image.
|
||||||
|
|
||||||
|
|
||||||
|
## [3.7.2] - 2023-12-26
|
||||||
|
### Added
|
||||||
|
* *Nothing*
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
* *Nothing*
|
||||||
|
|
||||||
|
### Deprecated
|
||||||
|
* *Nothing*
|
||||||
|
|
||||||
|
### Removed
|
||||||
|
* *Nothing*
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
* [#1960](https://github.com/shlinkio/shlink/issues/1960) Allow QR codes to be optionally resolved even when corresponding short URL is not enabled.
|
||||||
|
|
||||||
|
|
||||||
|
## [3.7.1] - 2023-12-17
|
||||||
|
### Added
|
||||||
|
* *Nothing*
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
* Remove dependency on functional-php library
|
||||||
|
* [#1939](https://github.com/shlinkio/shlink/issues/1939) Fine-tune RoadRunner logs to avoid too many useless info.
|
||||||
|
|
||||||
|
### Deprecated
|
||||||
|
* *Nothing*
|
||||||
|
|
||||||
|
### Removed
|
||||||
|
* *Nothing*
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
* [#1947](https://github.com/shlinkio/shlink/issues/1947) Fix error when importing short URLs while using Postgres.
|
||||||
|
|
||||||
|
|
||||||
|
## [3.7.0] - 2023-11-25
|
||||||
|
### Added
|
||||||
|
* [#1798](https://github.com/shlinkio/shlink/issues/1798) Experimental support to send visits to an external Matomo instance.
|
||||||
|
* [#1780](https://github.com/shlinkio/shlink/issues/1780) Add new `NO_ORPHAN_VISITS` API key role.
|
||||||
|
|
||||||
|
Keys with this role will always get `0` when fetching orphan visits.
|
||||||
|
|
||||||
|
When trying to delete orphan visits the result will also be `0` and no visits will actually get deleted.
|
||||||
|
|
||||||
|
* [#1879](https://github.com/shlinkio/shlink/issues/1879) Cache namespace can now be customized via config option or `CACHE_NAMESPACE` env var.
|
||||||
|
|
||||||
|
This is important if you are running multiple Shlink instance on the same server, or they share the same Redis instance (even more so if they are on different versions).
|
||||||
|
|
||||||
|
* [#1905](https://github.com/shlinkio/shlink/issues/1905) Add support for PHP 8.3.
|
||||||
|
* [#1927](https://github.com/shlinkio/shlink/issues/1927) Allow redis credentials be URL-decoded before passing them to connection.
|
||||||
|
* [#1834](https://github.com/shlinkio/shlink/issues/1834) Add support for redis encrypted connections using SSL/TLS.
|
||||||
|
|
||||||
|
Encryption should work out of the box if servers schema is set tp `tls` or `rediss`, including support for self-signed certificates.
|
||||||
|
|
||||||
|
This has been tested with AWS ElasticCache using in-transit encryption, and with Digital Ocean Redis database.
|
||||||
|
|
||||||
|
* [#1906](https://github.com/shlinkio/shlink/issues/1906) Add support for RabbitMQ encrypted connections using SSL/TLS.
|
||||||
|
|
||||||
|
In order to enable SLL, you need to pass `RABBITMQ_USE_SSL=true` or the corresponding config option.
|
||||||
|
|
||||||
|
Connections using self-signed certificates should work out of the box.
|
||||||
|
|
||||||
|
This has been tested with AWS RabbitMQ using in-transit encryption, and with CloudAMQP.
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
* [#1799](https://github.com/shlinkio/shlink/issues/1799) RoadRunner/openswoole jobs are not run anymore for tasks that are actually disabled.
|
||||||
|
|
||||||
|
For example, if you did not enable RabbitMQ real-time updates, instead of triggering a job that ends immediately, the job will not even be enqueued.
|
||||||
|
|
||||||
|
* [#1835](https://github.com/shlinkio/shlink/issues/1835) Docker image is now built only when a release is tagged, and new tags are included, for minor and major versions.
|
||||||
|
* [#1055](https://github.com/shlinkio/shlink/issues/1055) Update OAS definition to v3.1.
|
||||||
|
* [#1885](https://github.com/shlinkio/shlink/issues/1885) Update to chronos 3.0.
|
||||||
|
* [#1896](https://github.com/shlinkio/shlink/issues/1896) Requests to health endpoint are no longer logged.
|
||||||
|
* [#1877](https://github.com/shlinkio/shlink/issues/1877) Print a warning when manually running `visit:download-db` command and a GeoLite2 license was not provided.
|
||||||
|
|
||||||
|
### Deprecated
|
||||||
|
* [#1783](https://github.com/shlinkio/shlink/issues/1783) Deprecated support for openswoole. RoadRunner is the best replacement, with the same capabilities, but much easier and convenient to install and manage.
|
||||||
|
|
||||||
|
### Removed
|
||||||
|
* [#1790](https://github.com/shlinkio/shlink/issues/1790) Drop support for PHP 8.1.
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
* [#1819](https://github.com/shlinkio/shlink/issues/1819) Fix incorrect timeout when running DB commands during Shlink start-up.
|
||||||
|
* [#1901](https://github.com/shlinkio/shlink/issues/1901) Do not allow short URLs with custom slugs containing URL-reserved characters, as they will not work at all afterward.
|
||||||
|
* [#1900](https://github.com/shlinkio/shlink/issues/1900) Fix short URL visits deletion when multi-segment slugs are enabled.
|
||||||
|
|
||||||
|
|
||||||
|
## [3.6.4] - 2023-09-23
|
||||||
|
### Added
|
||||||
|
* *Nothing*
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
* [#1866](https://github.com/shlinkio/shlink/issues/1866) The `INITIAL_API_KEY` env var is now only relevant for the official docker image.
|
||||||
|
|
||||||
|
Going forward, new non-docker Shlink installations provisioned with env vars that also wish to provide an initial API key, should do it by using the `vendor/bin/shlink-installer init --initial-api-key=%SOME_KEY%` command, instead of using `INITIAL_API_KEY`.
|
||||||
|
|
||||||
|
### Deprecated
|
||||||
|
* *Nothing*
|
||||||
|
|
||||||
|
### Removed
|
||||||
|
* *Nothing*
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
* [#1819](https://github.com/shlinkio/shlink/issues/1819) Fix incorrect timeout when running DB commands during Shlink start-up.
|
||||||
|
* [#1870](https://github.com/shlinkio/shlink/issues/1870) Make sure shared locks include the cache prefix when using Redis.
|
||||||
|
* [#1866](https://github.com/shlinkio/shlink/issues/1866) Fix error when starting docker image with `INITIAL_API_KEY` env var.
|
||||||
|
|
||||||
|
|
||||||
|
## [3.6.3] - 2023-06-14
|
||||||
|
### Added
|
||||||
|
* *Nothing*
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
* *Nothing*
|
||||||
|
|
||||||
|
### Deprecated
|
||||||
|
* *Nothing*
|
||||||
|
|
||||||
|
### Removed
|
||||||
|
* *Nothing*
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
* [#1817](https://github.com/shlinkio/shlink/issues/1817) Fix Shlink trying to create SQLite database tables even if they already exist.
|
||||||
|
|
||||||
|
|
||||||
|
## [3.6.2] - 2023-06-08
|
||||||
|
### Added
|
||||||
|
* *Nothing*
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
* *Nothing*
|
||||||
|
|
||||||
|
### Deprecated
|
||||||
|
* *Nothing*
|
||||||
|
|
||||||
|
### Removed
|
||||||
|
* *Nothing*
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
* [#1808](https://github.com/shlinkio/shlink/issues/1808) Fix `rr` binary downloading during Shlink update.
|
||||||
|
|
||||||
|
|
||||||
|
## [3.6.1] - 2023-06-04
|
||||||
|
### Added
|
||||||
|
* *Nothing*
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
* *Nothing*
|
||||||
|
|
||||||
|
### Deprecated
|
||||||
|
* *Nothing*
|
||||||
|
|
||||||
|
### Removed
|
||||||
|
* *Nothing*
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
* [#1413](https://github.com/shlinkio/shlink/issues/1413) Fix error when creating initial DB in Postgres in a cluster where a default `postgres` db does not exist or the credentials do not grant permissions to connect.
|
||||||
|
* [#1803](https://github.com/shlinkio/shlink/issues/1803) Fix default RoadRunner port when not using docker image.
|
||||||
|
|
||||||
|
|
||||||
|
## [3.6.0] - 2023-05-24
|
||||||
|
### Added
|
||||||
|
* [#1148](https://github.com/shlinkio/shlink/issues/1148) Add support to delete short URL visits.
|
||||||
|
|
||||||
|
This can be done via `DELETE /short-urls/{shortCode}/visits` REST endpoint or via `short-url:visits-delete` console command.
|
||||||
|
|
||||||
|
The CLI command includes a warning and requires the user to confirm before proceeding.
|
||||||
|
|
||||||
|
* [#1681](https://github.com/shlinkio/shlink/issues/1681) Add support to delete orphan visits.
|
||||||
|
|
||||||
|
This can be done via `DELETE /visits/orphan` REST endpoint or via `visit:orphan-delete` console command.
|
||||||
|
|
||||||
|
The CLI command includes a warning and requires the user to confirm before proceeding.
|
||||||
|
|
||||||
|
* [#1753](https://github.com/shlinkio/shlink/issues/1753) Add a new `vendor/bin/shlink-installer init` command that can be used to automate Shlink installations.
|
||||||
|
|
||||||
|
This command can create the initial database, update it, create proxies, clean cache, download initial GeoLite db files, etc
|
||||||
|
|
||||||
|
The official docker image also uses it on its entry point script.
|
||||||
|
|
||||||
|
* [#1656](https://github.com/shlinkio/shlink/issues/1656) Add support for openswoole 22
|
||||||
|
* [#1784](https://github.com/shlinkio/shlink/issues/1784) Add new docker tag where the container runs as a non-root user.
|
||||||
|
* [#953](https://github.com/shlinkio/shlink/issues/953) Add locks that prevent errors on duplicated keys when creating short URLs in parallel that depend on the same new tag or domain.
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
* [#1755](https://github.com/shlinkio/shlink/issues/1755) Update to roadrunner 2023
|
||||||
|
* [#1745](https://github.com/shlinkio/shlink/issues/1745) Roadrunner is now the default docker runtime.
|
||||||
|
|
||||||
|
There are now three different docker images published:
|
||||||
|
|
||||||
|
* Versions without suffix (like `3.6.0`) will contain the default runtime, whichever it is.
|
||||||
|
* Versions with `-roadrunner` suffix (like `3.6.0-roadrunner`) will always use roadrunner as the runtime, even if default one changes in the future.
|
||||||
|
* Versions with `-openswoole` suffix (like `3.6.0-openswoole`) will always use openswoole as the runtime, even if default one changes in the future.
|
||||||
|
|
||||||
|
### Deprecated
|
||||||
|
* Deprecated `ENABLE_PERIODIC_VISIT_LOCATE` env var. Use an external mechanism to automate visit locations.
|
||||||
|
|
||||||
|
### Removed
|
||||||
|
* *Nothing*
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
* [#1760](https://github.com/shlinkio/shlink/issues/1760) Fix domain not being set to null when importing short URLs with default domain.
|
||||||
|
* [#953](https://github.com/shlinkio/shlink/issues/953) Fix duplicated key errors and short URL creation failing when creating short URLs in parallel that depend on the same new tag or domain.
|
||||||
|
* [#1741](https://github.com/shlinkio/shlink/issues/1741) Fix randomly using 100% CPU in task workers when trying to download GeoLite DB files.
|
||||||
|
* Fix Shlink trying to connect to RabbitMQ even if configuration set to not connect.
|
||||||
|
|
||||||
|
|
||||||
|
## [3.5.4] - 2023-04-12
|
||||||
|
### Added
|
||||||
|
* *Nothing*
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
* *Nothing*
|
||||||
|
|
||||||
|
### Deprecated
|
||||||
|
* *Nothing*
|
||||||
|
|
||||||
|
### Removed
|
||||||
|
* *Nothing*
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
* [#1742](https://github.com/shlinkio/shlink/issues/1742) Fix URLs using schemas which do not contain `//`, like `mailto:`, to no longer be considered valid.
|
||||||
|
* [#1743](https://github.com/shlinkio/shlink/issues/1743) Fix Error when trying to create short URLs from CLI on an openswoole context.
|
||||||
|
|
||||||
|
Unfortunately the reason are real-time updates do not work with openswoole when outside an openswoole request, so the feature has been disabled for that context.
|
||||||
|
|
||||||
|
|
||||||
|
## [3.5.3] - 2023-03-31
|
||||||
|
### Added
|
||||||
|
* *Nothing*
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
* *Nothing*
|
||||||
|
|
||||||
|
### Deprecated
|
||||||
|
* *Nothing*
|
||||||
|
|
||||||
|
### Removed
|
||||||
|
* *Nothing*
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
* [#1715](https://github.com/shlinkio/shlink/issues/1715) Fix short URL creation/edition allowing long URLs without schema. Now a validation error is thrown.
|
||||||
|
* [#1537](https://github.com/shlinkio/shlink/issues/1537) Fix incorrect list of tags being returned for some author-only API keys.
|
||||||
|
* [#1738](https://github.com/shlinkio/shlink/issues/1738) Fix memory leak when importing short URLs with many visits.
|
||||||
|
|
||||||
|
|
||||||
|
## [3.5.2] - 2023-02-16
|
||||||
|
### Added
|
||||||
|
* *Nothing*
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
* [#1696](https://github.com/shlinkio/shlink/issues/1696) Migrated to PHPUnit 10.
|
||||||
|
|
||||||
|
### Deprecated
|
||||||
|
* *Nothing*
|
||||||
|
|
||||||
|
### Removed
|
||||||
|
* *Nothing*
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
* [#1698](https://github.com/shlinkio/shlink/issues/1698) Fixed error 500 in `robots.txt`.
|
||||||
|
* [#1688](https://github.com/shlinkio/shlink/issues/1688) Fixed huge performance degradation on `/tags/stats` endpoint.
|
||||||
|
* [#1693](https://github.com/shlinkio/shlink/issues/1693) Fixed Shlink thinking database already exists if it finds foreign tables.
|
||||||
|
|
||||||
|
|
||||||
|
## [3.5.1] - 2023-02-04
|
||||||
|
### Added
|
||||||
|
* *Nothing*
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
* [#1685](https://github.com/shlinkio/shlink/issues/1685) Changed `loosely` mode to `loose`, as it was a typo. The old one keeps working and maps to the new one, but it's considered deprecated.
|
||||||
|
|
||||||
|
### Deprecated
|
||||||
|
* *Nothing*
|
||||||
|
|
||||||
|
### Removed
|
||||||
|
* *Nothing*
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
* [#1682](https://github.com/shlinkio/shlink/issues/1682) Fixed incorrect case-insensitive checks in short URLs when using Microsoft SQL server.
|
||||||
|
* [#1684](https://github.com/shlinkio/shlink/issues/1684) Fixed entities metadata cache not being cleared at docker container start-up when using redis with replication.
|
||||||
|
|
||||||
|
|
||||||
|
## [3.5.0] - 2023-01-28
|
||||||
|
### Added
|
||||||
|
* [#1557](https://github.com/shlinkio/shlink/issues/1557) Added support to dynamically redirect to different long URLs based on the visitor's device type.
|
||||||
|
|
||||||
|
For the moment, only `android`, `ios` and `desktop` can have their own specific long URL, and when the visitor cannot be matched against any of them, the regular long URL will be used.
|
||||||
|
|
||||||
|
In the future, more granular device types could be added if appropriate (iOS tablet, android table, tablet, mobile phone, Linux, Mac, Windows, etc).
|
||||||
|
|
||||||
|
In order to match the visitor's device, the `User-Agent` header is used.
|
||||||
|
|
||||||
|
* [#1632](https://github.com/shlinkio/shlink/issues/1632) Added amount of bots, non-bots and total visits to the visits summary endpoint.
|
||||||
|
* [#1633](https://github.com/shlinkio/shlink/issues/1633) Added amount of bots, non-bots and total visits to the tag stats endpoint.
|
||||||
|
* [#1653](https://github.com/shlinkio/shlink/issues/1653) Added support for all HTTP methods in short URLs, together with two new redirect status codes, 307 and 308.
|
||||||
|
|
||||||
|
Existing Shlink instances will continue to work the same. However, if you decide to set the redirect status codes as 307 or 308, Shlink will also return a redirect for short URLs even when the request method is different from `GET`.
|
||||||
|
|
||||||
|
The status 308 is equivalent to 301, and 307 is equivalent to 302. The difference is that the spec requires the client to respect the original HTTP method when performing the redirect. With 301 and 302, some old clients might perform a `GET` request during the redirect, regardless the original request method.
|
||||||
|
|
||||||
|
* [#1662](https://github.com/shlinkio/shlink/issues/1662) Added support to provide openswoole-specific config options via env vars prefixed with `OPENSWOOLE_`.
|
||||||
|
* [#1389](https://github.com/shlinkio/shlink/issues/1389) and [#706](https://github.com/shlinkio/shlink/issues/706) Added support for case-insensitive short URLs.
|
||||||
|
|
||||||
|
In order to achieve this, a new env var/config option has been implemented (`SHORT_URL_MODE`), which allows either `strict` or ~~`loosely`~~ `loose`.
|
||||||
|
|
||||||
|
Default value is `strict`, but if `loose` is provided, then short URLs will be matched in a case-insensitive way, and new short URLs will be generated with short-codes in lowercase only.
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
* *Nothing*
|
||||||
|
|
||||||
|
### Deprecated
|
||||||
|
* [#1676](https://github.com/shlinkio/shlink/issues/1676) Deprecated `GET /short-urls/shorten` endpoint. Use `POST /short-urls` to create short URLs instead.
|
||||||
|
* [#1678](https://github.com/shlinkio/shlink/issues/1678) Deprecated `validateUrl` option on URL creation/edition.
|
||||||
|
|
||||||
|
### Removed
|
||||||
|
* *Nothing*
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
* [#1639](https://github.com/shlinkio/shlink/issues/1639) Fixed 500 error returned when request body is not valid JSON, instead of a proper descriptive error.
|
||||||
|
|
||||||
|
|
||||||
|
## [3.4.0] - 2022-12-16
|
||||||
|
### Added
|
||||||
|
* [#1612](https://github.com/shlinkio/shlink/issues/1612) Allowed to filter short URLs out of lists, when `validUntil` date is in the past or have reached their maximum amount of visits.
|
||||||
|
|
||||||
|
This can be done by:
|
||||||
|
|
||||||
|
* Providing `excludeMaxVisitsReached=true` and/or `excludePastValidUntil=true` to the `GET /short-urls` endpoint.
|
||||||
|
* Providing `--exclude-max-visits-reached` and/or `--exclude-past-valid-until` to the `short-urls:list` command.
|
||||||
|
|
||||||
|
* [#1613](https://github.com/shlinkio/shlink/issues/1613) Added amount of visits coming from bots, non-bots and total to every short URL in the short URLs list.
|
||||||
|
|
||||||
|
Additionally, added option to order by non-bot visits, by passing `nonBotVisits-DESC` or `nonBotVisits-ASC`.
|
||||||
|
|
||||||
|
* [#1599](https://github.com/shlinkio/shlink/issues/1599) Added support for credentials on redis DSNs, either only password, or both username and password.
|
||||||
|
* [#1616](https://github.com/shlinkio/shlink/issues/1616) Added support to import orphan visits when importing short URLs from another Shlink instance.
|
||||||
|
* [#1519](https://github.com/shlinkio/shlink/issues/1519) Allowing to search short URLs by default domain.
|
||||||
|
* [#1555](https://github.com/shlinkio/shlink/issues/1555) and [#1625](https://github.com/shlinkio/shlink/issues/1625) Added full support for PHP 8.2, updating the docker image to this version.
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
* [#1563](https://github.com/shlinkio/shlink/issues/1563) Moved logic to reuse command options to option classes instead of base abstract command classes.
|
||||||
|
* [#1569](https://github.com/shlinkio/shlink/issues/1569) Migrated test doubles from phpspec/prophecy to PHPUnit mocks.
|
||||||
|
* [#1329](https://github.com/shlinkio/shlink/issues/1329) Split some logic from `VisitRepository` and `ShortUrlRepository` into separated repository classes.
|
||||||
|
|
||||||
|
### Deprecated
|
||||||
|
* *Nothing*
|
||||||
|
|
||||||
|
### Removed
|
||||||
|
* *Nothing*
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
* [#1618](https://github.com/shlinkio/shlink/issues/1618) Fixed imported short URLs and visits dates not being set to the target server timezone.
|
||||||
|
* [#1578](https://github.com/shlinkio/shlink/issues/1578) Fixed short URL allowing an empty string as the domain during creation.
|
||||||
|
* [#1580](https://github.com/shlinkio/shlink/issues/1580) Fixed `FLUSHDB` being run on Shlink docker start-up when using redis, causing full cache to be flushed.
|
||||||
|
|
||||||
|
|
||||||
|
## [3.3.2] - 2022-10-18
|
||||||
|
### Added
|
||||||
|
* *Nothing*
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
* *Nothing*
|
||||||
|
|
||||||
|
### Deprecated
|
||||||
|
* *Nothing*
|
||||||
|
|
||||||
|
### Removed
|
||||||
|
* *Nothing*
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
* [#1576](https://github.com/shlinkio/shlink/issues/1576) Fixed error when trying to retry visits location from CLI.
|
||||||
|
|
||||||
|
|
||||||
|
## [3.3.1] - 2022-09-30
|
||||||
|
### Added
|
||||||
|
* *Nothing*
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
* [#1474](https://github.com/shlinkio/shlink/issues/1474) Added preliminary support for PHP 8.2 during CI workflow.
|
||||||
|
* [#1551](https://github.com/shlinkio/shlink/issues/1551) Moved services related to geolocating visits to the `Visit\Geolocation` namespace.
|
||||||
|
* [#1550](https://github.com/shlinkio/shlink/issues/1550) Reorganized main namespaces from Core module.
|
||||||
|
|
||||||
|
### Deprecated
|
||||||
|
* *Nothing*
|
||||||
|
|
||||||
|
### Removed
|
||||||
|
* *Nothing*
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
* [#1556](https://github.com/shlinkio/shlink/issues/1556) Fixed trailing slash not working when enabling multi-segment slashes.
|
||||||
|
|
||||||
|
|
||||||
|
## [3.3.0] - 2022-09-18
|
||||||
|
### Added
|
||||||
|
* [#1221](https://github.com/shlinkio/shlink/issues/1221) Added experimental support to run Shlink with [RoadRunner](https://roadrunner.dev) instead of openswoole.
|
||||||
|
* [#1531](https://github.com/shlinkio/shlink/issues/1531) and [#1090](https://github.com/shlinkio/shlink/issues/1090) Added support for trailing slashes in short URLs.
|
||||||
|
* [#1406](https://github.com/shlinkio/shlink/issues/1406) Added new REST API version 3.
|
||||||
|
|
||||||
|
When making requests to the REST API with `/rest/v3/...` and an error occurs, all error types will be different, with the next correlation:
|
||||||
|
|
||||||
|
* `INVALID_ARGUMENT` -> `https://shlink.io/api/error/invalid-data`
|
||||||
|
* `INVALID_SHORT_URL_DELETION` -> `https://shlink.io/api/error/invalid-short-url-deletion`
|
||||||
|
* `DOMAIN_NOT_FOUND` -> `https://shlink.io/api/error/domain-not-found`
|
||||||
|
* `FORBIDDEN_OPERATION` -> `https://shlink.io/api/error/forbidden-tag-operation`
|
||||||
|
* `INVALID_URL` -> `https://shlink.io/api/error/invalid-url`
|
||||||
|
* `INVALID_SLUG` -> `https://shlink.io/api/error/non-unique-slug`
|
||||||
|
* `INVALID_SHORTCODE` -> `https://shlink.io/api/error/short-url-not-found`
|
||||||
|
* `TAG_CONFLICT` -> `https://shlink.io/api/error/tag-conflict`
|
||||||
|
* `TAG_NOT_FOUND` -> `https://shlink.io/api/error/tag-not-found`
|
||||||
|
* `MERCURE_NOT_CONFIGURED` -> `https://shlink.io/api/error/mercure-not-configured`
|
||||||
|
* `INVALID_AUTHORIZATION` -> `https://shlink.io/api/error/missing-authentication`
|
||||||
|
* `INVALID_API_KEY` -> `https://shlink.io/api/error/invalid-api-key`
|
||||||
|
|
||||||
|
If you make a request to the API with v2 or v1, the old error types will be returned, until Shlink 4 is released, when only the new ones will be used.
|
||||||
|
|
||||||
|
Non-error responses are not affected.
|
||||||
|
|
||||||
|
* [#1513](https://github.com/shlinkio/shlink/issues/1513) Added publishing of the docker image in GHCR.
|
||||||
|
* [#1114](https://github.com/shlinkio/shlink/issues/1114) Added support to provide an initial API key via `INITIAL_API_KEY` env var, when running Shlink with openswoole or RoadRunner.
|
||||||
|
|
||||||
|
Also, the installer tool now allows to generate an initial API key that can be copy-pasted (this tool is run interactively), in case you use php-fpm or you don't want to use env vars.
|
||||||
|
|
||||||
|
* [#1528](https://github.com/shlinkio/shlink/issues/1528) Added support to delay when the GeoLite2 DB file is downloaded in docker images, speeding up its startup time.
|
||||||
|
|
||||||
|
In order to do it, pass `SKIP_INITIAL_GEOLITE_DOWNLOAD=true` when creating the container.
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
* [#1339](https://github.com/shlinkio/shlink/issues/1339) Added new test suite for CLI E2E tests.
|
||||||
|
* [#1503](https://github.com/shlinkio/shlink/issues/1503) Drastically improved build time in GitHub Actions, by optimizing parallelization and adding php extensions cache.
|
||||||
|
* [#1525](https://github.com/shlinkio/shlink/issues/1525) Migrated to custom doctrine CLI entry point.
|
||||||
|
* [#1492](https://github.com/shlinkio/shlink/issues/1492) Migrated to immutable options objects, mapped with [cuyz/valinor](https://github.com/CuyZ/Valinor).
|
||||||
|
|
||||||
|
### Deprecated
|
||||||
|
* *Nothing*
|
||||||
|
|
||||||
|
### Removed
|
||||||
|
* *Nothing*
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
* *Nothing*
|
||||||
|
|
||||||
|
|
||||||
|
## [3.2.1] - 2022-08-08
|
||||||
|
### Added
|
||||||
|
* *Nothing*
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
* [#1495](https://github.com/shlinkio/shlink/issues/1495) Centralized how routes are configured to support multi-segment slugs.
|
||||||
|
* [#1497](https://github.com/shlinkio/shlink/issues/1497) Updated to latest shlink dependencies with support for PHP 8.1 only.
|
||||||
|
|
||||||
|
### Deprecated
|
||||||
|
* *Nothing*
|
||||||
|
|
||||||
|
### Removed
|
||||||
|
* *Nothing*
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
* [#1499](https://github.com/shlinkio/shlink/issues/1499) Fixed loading of config options as env vars, which was making all default configurations to be loaded unless env vars were explicitly provided.
|
||||||
|
|
||||||
|
|
||||||
|
## [3.2.0] - 2022-08-05
|
||||||
|
### Added
|
||||||
|
* [#854](https://github.com/shlinkio/shlink/issues/854) Added support for multi-segment custom slugs.
|
||||||
|
|
||||||
|
The feature is disabled by default, but you can optionally opt in. If you do, you will be able to create short URLs with multiple segments in the custom slug, like `https://example.com/foo/bar/baz`.
|
||||||
|
|
||||||
|
* [#1280](https://github.com/shlinkio/shlink/issues/1280) Added missing visit-related commands.
|
||||||
|
|
||||||
|
Now you can run `tag:visits`, `domain:visits`, `visit:orphan` or `visit:non-orphan` to get the corresponding list of visits from the command line.
|
||||||
|
|
||||||
|
* [#962](https://github.com/shlinkio/shlink/issues/962) Added new real-time update for new short URLs.
|
||||||
|
|
||||||
|
You can now subscribe to the `https://shlink.io/new-short-url` topic on any of the supported async updates technologies in order to get notified when a short URL is created.
|
||||||
|
|
||||||
|
* [#1367](https://github.com/shlinkio/shlink/issues/1367) Added support to publish real-time updates in redis pub/sub.
|
||||||
|
|
||||||
|
The publishing will happen in the same redis instance/cluster configured for caching.
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
* [#1452](https://github.com/shlinkio/shlink/issues/1452) Updated to monolog 3
|
||||||
|
* [#1485](https://github.com/shlinkio/shlink/issues/1485) Changed payload published in RabbitMQ for all visits events, in order to conform with the Async API spec.
|
||||||
|
|
||||||
|
Since this is a breaking change, also provided a new `RABBITMQ_LEGACY_VISITS_PUBLISHING=true` env var that can be provided in order to keep the old payload.
|
||||||
|
|
||||||
|
This env var is considered deprecated and will be removed in Shlink 4, when the legacy format will no longer be supported.
|
||||||
|
|
||||||
|
### Deprecated
|
||||||
|
* *Nothing*
|
||||||
|
|
||||||
|
### Removed
|
||||||
|
* [#1280](https://github.com/shlinkio/shlink/issues/1280) Dropped support for PHP 8.0
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
* [#1471](https://github.com/shlinkio/shlink/issues/1471) Fixed error when running `visit:locate` command with any extra parameter (like `--retry`).
|
||||||
|
|
||||||
|
|
||||||
|
## [3.1.2] - 2022-06-04
|
||||||
|
### Added
|
||||||
|
* *Nothing*
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
* *Nothing*
|
||||||
|
|
||||||
|
### Deprecated
|
||||||
|
* *Nothing*
|
||||||
|
|
||||||
|
### Removed
|
||||||
|
* *Nothing*
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
* [#1448](https://github.com/shlinkio/shlink/issues/1448) Fixed HTML entities not being properly parsed when auto-resolving page titles.
|
||||||
|
* [#1458](https://github.com/shlinkio/shlink/issues/1458) Fixed 500 error when filtering short URLs by ALL tags and search term.
|
||||||
|
|
||||||
|
|
||||||
|
## [3.1.1] - 2022-05-09
|
||||||
|
### Added
|
||||||
|
* *Nothing*
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
* [#1444](https://github.com/shlinkio/shlink/issues/1444) Updated docker image to openswoole 4.11.1, in an attempt to fix error.
|
||||||
|
|
||||||
|
### Deprecated
|
||||||
|
* *Nothing*
|
||||||
|
|
||||||
|
### Removed
|
||||||
|
* *Nothing*
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
* [#1439](https://github.com/shlinkio/shlink/issues/1439) Fixed crash when trying to auto-resolve titles for URLs which serve large binary files.
|
||||||
|
|
||||||
|
|
||||||
|
## [3.1.0] - 2022-04-23
|
||||||
|
### Added
|
||||||
|
* [#1294](https://github.com/shlinkio/shlink/issues/1294) Allowed to provide a specific domain when importing URLs from YOURLS.
|
||||||
|
* [#1416](https://github.com/shlinkio/shlink/issues/1416) Added support to import URLs from Kutt.it.
|
||||||
|
* [#1418](https://github.com/shlinkio/shlink/issues/1418) Added support to customize the timezone used by Shlink, falling back to the default one set in PHP config.
|
||||||
|
|
||||||
|
The timezone can be set via the `TIMEZONE` env var, or using the installer tool.
|
||||||
|
|
||||||
|
* [#1309](https://github.com/shlinkio/shlink/issues/1309) Improved URL importing, ensuring individual errors do not make the whole process fail, and instead, failing URLs are skipped.
|
||||||
|
* [#1162](https://github.com/shlinkio/shlink/issues/1162) Added new endpoint to get visits by domain.
|
||||||
|
|
||||||
|
The endpoint is `GET /domains/{domain}/visits`, and it has the same capabilities as any other visits endpoint, allowing pagination and filtering.
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
* [#1359](https://github.com/shlinkio/shlink/issues/1359) Hidden database commands.
|
||||||
|
* [#1385](https://github.com/shlinkio/shlink/issues/1385) Prevented a big error message from being logged when using Shlink without mercure.
|
||||||
|
* [#1398](https://github.com/shlinkio/shlink/issues/1398) Increased required mutation score for unit tests to 85%.
|
||||||
|
* [#1419](https://github.com/shlinkio/shlink/issues/1419) Input dates are now parsed to Shlink's configured timezone or default timezone before using them for database queries.
|
||||||
|
* [#1428](https://github.com/shlinkio/shlink/issues/1428) Updated native dependencies in docker image and base image to PHP v8.1.5.
|
||||||
|
|
||||||
|
### Deprecated
|
||||||
|
* [#1340](https://github.com/shlinkio/shlink/issues/1340) Deprecated webhooks. New events will only be added to other real-time updates approaches, and webhooks will be completely removed in Shlink 4.0.0.
|
||||||
|
|
||||||
|
### Removed
|
||||||
|
* *Nothing*
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
* [#1397](https://github.com/shlinkio/shlink/issues/1397) Fixed `db:create` command always reporting the schema exists if the `db:migrate` command has been run before by mistake.
|
||||||
|
* [#1402](https://github.com/shlinkio/shlink/issues/1402) Fixed the base path getting appended with the default domain by mistake, causing multiple side effects in several places.
|
||||||
|
|
||||||
|
|
||||||
|
## [3.0.3] - 2022-02-19
|
||||||
|
### Added
|
||||||
|
* *Nothing*
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
* [#1382](https://github.com/shlinkio/shlink/issues/1382) Updated docker image to PHP 8.1.3.
|
||||||
|
|
||||||
|
### Deprecated
|
||||||
|
* *Nothing*
|
||||||
|
|
||||||
|
### Removed
|
||||||
|
* *Nothing*
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
* [#1377](https://github.com/shlinkio/shlink/issues/1377) Fixed installer always setting delete threshold with value 1.
|
||||||
|
* [#1379](https://github.com/shlinkio/shlink/issues/1379) Ensured API keys cannot be created with a domain-only role linked to default domain.
|
||||||
|
|
||||||
|
|
||||||
|
## [3.0.2] - 2022-02-10
|
||||||
|
### Added
|
||||||
|
* *Nothing*
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
* *Nothing*
|
||||||
|
|
||||||
|
### Deprecated
|
||||||
|
* *Nothing*
|
||||||
|
|
||||||
|
### Removed
|
||||||
|
* *Nothing*
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
* [#1373](https://github.com/shlinkio/shlink/issues/1373) Fixed incorrect config import when updating from Shlink 2.x using SQLite.
|
||||||
|
* [#1369](https://github.com/shlinkio/shlink/issues/1369) Fixed slow regexps in `.htaccess` file.
|
||||||
|
|
||||||
|
|
||||||
|
## [3.0.1] - 2022-02-04
|
||||||
|
### Added
|
||||||
|
* *Nothing*
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
* *Nothing*
|
||||||
|
|
||||||
|
### Deprecated
|
||||||
|
* *Nothing*
|
||||||
|
|
||||||
|
### Removed
|
||||||
|
* *Nothing*
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
* [#1363](https://github.com/shlinkio/shlink/issues/1363) Fixed titles being resolved no matter what when `validateUrl` is not set or is explicitly set to true.
|
||||||
|
* [#1352](https://github.com/shlinkio/shlink/issues/1352) Updated to stable pdo_sqlsrv in docker image.
|
||||||
|
|
||||||
|
|
||||||
|
## [3.0.0] - 2022-01-28
|
||||||
|
### Added
|
||||||
|
* [#767](https://github.com/shlinkio/shlink/issues/767) Added full support to use emojis everywhere, whether it is custom slugs, titles, referrers, etc.
|
||||||
|
* [#1274](https://github.com/shlinkio/shlink/issues/1274) Added support to filter short URLs lists by all provided tags.
|
||||||
|
|
||||||
|
The `GET /short-urls` endpoint now accepts a `tagsMode=all` param which will make only short URLs matching **all** the tags in the `tags[]` query param, to be returned.
|
||||||
|
|
||||||
|
The `short-urls:list` command now accepts a `-i`/`--including-all-tags` flag which behaves the same.
|
||||||
|
|
||||||
|
* [#1273](https://github.com/shlinkio/shlink/issues/1273) Added support for pagination in tags lists, allowing to improve performance by loading subsets of tags.
|
||||||
|
|
||||||
|
For backwards compatibility, lists continue returning all items by default, but the `GET /tags` endpoint now supports `page` and `itemsPerPage` query params, to make sure only a subset of the tags is returned.
|
||||||
|
|
||||||
|
This is supported both when invoking the endpoint with and without `withStats=true` query param.
|
||||||
|
|
||||||
|
Additionally, the endpoint also supports filtering by `searchTerm` query param. When provided, only tags matching it will be returned.
|
||||||
|
|
||||||
|
* [#1063](https://github.com/shlinkio/shlink/issues/1063) Added new endpoint that allows fetching all existing non-orphan visits, in case you need a global view of all visits received by your Shlink instance.
|
||||||
|
|
||||||
|
This can be achieved using the `GET /visits/non-orphan` endpoint.
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
* [#1277](https://github.com/shlinkio/shlink/issues/1277) Reduced docker image size to 45% of the original size.
|
||||||
|
* [#1268](https://github.com/shlinkio/shlink/issues/1268) Updated dependencies, including symfony/console 6 and mezzio/mezzio-swoole 4.
|
||||||
|
* [#1283](https://github.com/shlinkio/shlink/issues/1283) Changed behavior of `DELETE_SHORT_URL_THRESHOLD` env var, disabling the feature if a value was not provided.
|
||||||
|
* [#1300](https://github.com/shlinkio/shlink/issues/1300) Changed default ordering for short URLs list, returning always from newest to oldest.
|
||||||
|
* [#1299](https://github.com/shlinkio/shlink/issues/1299) Updated to the latest base docker images, based in PHP 8.1.1, and bumped openswoole to v4.9.1.
|
||||||
|
* [#1282](https://github.com/shlinkio/shlink/issues/1282) Env vars now have precedence over installer options.
|
||||||
|
* [#1328](https://github.com/shlinkio/shlink/issues/1328) Refactored ShortUrlsRepository to use DTOs in methods with too many arguments.
|
||||||
|
|
||||||
|
### Deprecated
|
||||||
|
* [#1315](https://github.com/shlinkio/shlink/issues/1315) Deprecated `GET /tags?withStats=true` endpoint. Use `GET /tags/stats` instead.
|
||||||
|
|
||||||
|
### Removed
|
||||||
|
* [#1275](https://github.com/shlinkio/shlink/issues/1275) Removed everything that was deprecated in Shlink 2.x.
|
||||||
|
|
||||||
|
See [UPGRADE](UPGRADE.md#from-v2x-to-v3x) doc in order to get details on how to migrate to this version.
|
||||||
|
|
||||||
|
* [#1347](https://github.com/shlinkio/shlink/issues/1347) Dropped support for regular swoole in favor of openswoole.
|
||||||
|
|
||||||
|
Since openswoole support was introduced in the previous release version, Shlink will still consider the swoole extension as openswoole, as at the moment, functionality hasn't deviated too much, and will simplify migrating to Shlink 3.0.0
|
||||||
|
|
||||||
|
However, there's no longer active testing with regular swoole, and it is considered no longer supported. If some incompatibility arises, the only supported solution will be to migrate to openswoole.
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
* *Nothing*
|
||||||
@@ -15,14 +15,25 @@
|
|||||||
"properties": {
|
"properties": {
|
||||||
"type": {
|
"type": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": ["device", "language", "query-param"],
|
"enum": [
|
||||||
"description": "The type of the condition, which will condition the logic used to match it"
|
"device",
|
||||||
|
"language",
|
||||||
|
"query-param",
|
||||||
|
"any-value-query-param",
|
||||||
|
"valueless-query-param",
|
||||||
|
"ip-address",
|
||||||
|
"geolocation-country-code",
|
||||||
|
"geolocation-city-name",
|
||||||
|
"before-date",
|
||||||
|
"after-date"
|
||||||
|
],
|
||||||
|
"description": "The type of the condition, which will determine the logic used to match it"
|
||||||
},
|
},
|
||||||
"matchKey": {
|
"matchKey": {
|
||||||
"type": ["string", "null"]
|
"type": ["string", "null"]
|
||||||
},
|
},
|
||||||
"matchValue": {
|
"matchValue": {
|
||||||
"type": "string"
|
"type": ["string", "null"]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,8 @@
|
|||||||
"domain",
|
"domain",
|
||||||
"title",
|
"title",
|
||||||
"crawlable",
|
"crawlable",
|
||||||
"forwardQuery"
|
"forwardQuery",
|
||||||
|
"hasRedirectRules"
|
||||||
],
|
],
|
||||||
"properties": {
|
"properties": {
|
||||||
"shortCode": {
|
"shortCode": {
|
||||||
@@ -59,6 +60,10 @@
|
|||||||
"forwardQuery": {
|
"forwardQuery": {
|
||||||
"type": "boolean",
|
"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)."
|
"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": {
|
"visitedUrl": {
|
||||||
"type": ["string", "null"],
|
"type": ["string", "null"],
|
||||||
"description": "The originally visited URL that triggered the tracking of this visit"
|
"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",
|
"name": "searchTerm",
|
||||||
"in": "query",
|
"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,
|
"required": false,
|
||||||
"schema": {
|
"schema": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
@@ -40,7 +40,7 @@
|
|||||||
{
|
{
|
||||||
"name": "tags[]",
|
"name": "tags[]",
|
||||||
"in": "query",
|
"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,
|
"required": false,
|
||||||
"schema": {
|
"schema": {
|
||||||
"type": "array",
|
"type": "array",
|
||||||
@@ -52,7 +52,29 @@
|
|||||||
{
|
{
|
||||||
"name": "tagsMode",
|
"name": "tagsMode",
|
||||||
"in": "query",
|
"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,
|
"required": false,
|
||||||
"schema": {
|
"schema": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
@@ -125,6 +147,24 @@
|
|||||||
"false"
|
"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": [
|
"security": [
|
||||||
@@ -180,7 +220,9 @@
|
|||||||
},
|
},
|
||||||
"domain": null,
|
"domain": null,
|
||||||
"title": "Welcome to Steam",
|
"title": "Welcome to Steam",
|
||||||
"crawlable": false
|
"crawlable": false,
|
||||||
|
"forwardQuery": true,
|
||||||
|
"hasRedirectRules": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"shortCode": "12Kb3",
|
"shortCode": "12Kb3",
|
||||||
@@ -202,7 +244,9 @@
|
|||||||
},
|
},
|
||||||
"domain": null,
|
"domain": null,
|
||||||
"title": null,
|
"title": null,
|
||||||
"crawlable": false
|
"crawlable": false,
|
||||||
|
"forwardQuery": true,
|
||||||
|
"hasRedirectRules": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"shortCode": "123bA",
|
"shortCode": "123bA",
|
||||||
@@ -222,7 +266,9 @@
|
|||||||
},
|
},
|
||||||
"domain": "example.com",
|
"domain": "example.com",
|
||||||
"title": null,
|
"title": null,
|
||||||
"crawlable": false
|
"crawlable": false,
|
||||||
|
"forwardQuery": false,
|
||||||
|
"hasRedirectRules": true
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"pagination": {
|
"pagination": {
|
||||||
@@ -337,7 +383,9 @@
|
|||||||
},
|
},
|
||||||
"domain": null,
|
"domain": null,
|
||||||
"title": null,
|
"title": null,
|
||||||
"crawlable": false
|
"crawlable": false,
|
||||||
|
"forwardQuery": true,
|
||||||
|
"hasRedirectRules": false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -72,7 +72,9 @@
|
|||||||
},
|
},
|
||||||
"domain": null,
|
"domain": null,
|
||||||
"title": null,
|
"title": null,
|
||||||
"crawlable": false
|
"crawlable": false,
|
||||||
|
"forwardQuery": true,
|
||||||
|
"hasRedirectRules": false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"text/plain": {
|
"text/plain": {
|
||||||
|
|||||||
@@ -50,7 +50,9 @@
|
|||||||
},
|
},
|
||||||
"domain": null,
|
"domain": null,
|
||||||
"title": null,
|
"title": null,
|
||||||
"crawlable": false
|
"crawlable": false,
|
||||||
|
"forwardQuery": true,
|
||||||
|
"hasRedirectRules": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -163,7 +165,9 @@
|
|||||||
},
|
},
|
||||||
"domain": null,
|
"domain": null,
|
||||||
"title": "Shlink - The URL shortener",
|
"title": "Shlink - The URL shortener",
|
||||||
"crawlable": false
|
"crawlable": false,
|
||||||
|
"forwardQuery": false,
|
||||||
|
"hasRedirectRules": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -64,6 +64,10 @@
|
|||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": ["true"]
|
"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": [
|
"security": [
|
||||||
|
|||||||
@@ -55,6 +55,10 @@
|
|||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": ["true"]
|
"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": [
|
"security": [
|
||||||
|
|||||||
@@ -65,6 +65,10 @@
|
|||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": ["invalid_short_url", "base_url", "regular_404"]
|
"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": [
|
"security": [
|
||||||
|
|||||||
@@ -77,12 +77,12 @@
|
|||||||
"priority": 3,
|
"priority": 3,
|
||||||
"conditions": [
|
"conditions": [
|
||||||
{
|
{
|
||||||
"type": "query",
|
"type": "query-param",
|
||||||
"matchKey": "foo",
|
"matchKey": "foo",
|
||||||
"matchValue": "bar"
|
"matchValue": "bar"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "query",
|
"type": "query-param",
|
||||||
"matchKey": "hello",
|
"matchKey": "hello",
|
||||||
"matchValue": "world"
|
"matchValue": "world"
|
||||||
}
|
}
|
||||||
@@ -209,12 +209,12 @@
|
|||||||
"longUrl": "https://example.com/query-foo-bar-hello-world",
|
"longUrl": "https://example.com/query-foo-bar-hello-world",
|
||||||
"conditions": [
|
"conditions": [
|
||||||
{
|
{
|
||||||
"type": "query",
|
"type": "query-param",
|
||||||
"matchKey": "foo",
|
"matchKey": "foo",
|
||||||
"matchValue": "bar"
|
"matchValue": "bar"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "query",
|
"type": "query-param",
|
||||||
"matchKey": "hello",
|
"matchKey": "hello",
|
||||||
"matchValue": "world"
|
"matchValue": "world"
|
||||||
}
|
}
|
||||||
@@ -280,12 +280,12 @@
|
|||||||
"priority": 3,
|
"priority": 3,
|
||||||
"conditions": [
|
"conditions": [
|
||||||
{
|
{
|
||||||
"type": "query",
|
"type": "query-param",
|
||||||
"matchKey": "foo",
|
"matchKey": "foo",
|
||||||
"matchValue": "bar"
|
"matchValue": "bar"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "query",
|
"type": "query-param",
|
||||||
"matchKey": "hello",
|
"matchKey": "hello",
|
||||||
"matchValue": "world"
|
"matchValue": "world"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user