mirror of
https://github.com/shlinkio/shlink.git
synced 2026-03-01 04:33:12 +08:00
Compare commits
696 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c12db7567e | ||
|
|
e8069a10ba | ||
|
|
9742bf13e4 | ||
|
|
5b9a1e1978 | ||
|
|
4ba3522e79 | ||
|
|
d3faa22b78 | ||
|
|
1daad334a5 | ||
|
|
3dda49dab4 | ||
|
|
c6c4e5580b | ||
|
|
3f808e3813 | ||
|
|
e5107c40f9 | ||
|
|
0871ca884e | ||
|
|
62ce9311bf | ||
|
|
70b15a7ab0 | ||
|
|
708bff20f0 | ||
|
|
369628ee95 | ||
|
|
0c6f8f1136 | ||
|
|
9f9d011d46 | ||
|
|
e28b73c130 | ||
|
|
56f953ab2f | ||
|
|
3ad8be175c | ||
|
|
f5f990511c | ||
|
|
1e3ccba503 | ||
|
|
a842b5b7cd | ||
|
|
909e42b0be | ||
|
|
c8acb5de68 | ||
|
|
53b9e3ddc1 | ||
|
|
68e1c61e7f | ||
|
|
8605b35b57 | ||
|
|
36680e82aa | ||
|
|
83b7d5a5f1 | ||
|
|
fe41e9d573 | ||
|
|
d76e6647d2 | ||
|
|
6f17f70137 | ||
|
|
ef01754ad5 | ||
|
|
eab9347522 | ||
|
|
59bcd62717 | ||
|
|
3f01fad12f | ||
|
|
c7f0d14c1b | ||
|
|
2408829627 | ||
|
|
8d244c8d34 | ||
|
|
42af057316 | ||
|
|
8f68078835 | ||
|
|
0c34032fd3 | ||
|
|
20f457a3e9 | ||
|
|
39693ca1fe | ||
|
|
784908420e | ||
|
|
9685929824 | ||
|
|
fe4b2c4ae4 | ||
|
|
5f87bb13f8 | ||
|
|
a87f6c6709 | ||
|
|
da3ee6b65e | ||
|
|
c5eda37bda | ||
|
|
1966367caf | ||
|
|
eed7b6e565 | ||
|
|
0e54ed691d | ||
|
|
997289da02 | ||
|
|
c841e57db5 | ||
|
|
f5138385be | ||
|
|
63ceba199d | ||
|
|
e6ee4ceae2 | ||
|
|
19a9d815eb | ||
|
|
5b78b363f0 | ||
|
|
b078c00492 | ||
|
|
e712efd008 | ||
|
|
ab27c0ce53 | ||
|
|
d97cabbe79 | ||
|
|
c3c7ffad25 | ||
|
|
fe4329d730 | ||
|
|
c53ba7b119 | ||
|
|
025eec6c70 | ||
|
|
40e1670314 | ||
|
|
2bca260627 | ||
|
|
463d8e8950 | ||
|
|
e2eed8a728 | ||
|
|
f97effcfe0 | ||
|
|
2cf21ab3bd | ||
|
|
7daa602630 | ||
|
|
7b637d6a61 | ||
|
|
a4f979be08 | ||
|
|
8852739111 | ||
|
|
2099ea16ec | ||
|
|
a739eb6d60 | ||
|
|
529ddacafe | ||
|
|
f71c95b74a | ||
|
|
8260a0843b | ||
|
|
bfbeb7b1fb | ||
|
|
df70810aa6 | ||
|
|
aca5804f98 | ||
|
|
b7f7288a4b | ||
|
|
d54a2bde0f | ||
|
|
679bb8d357 | ||
|
|
ca515998e4 | ||
|
|
c5b6d203f5 | ||
|
|
86159c5d86 | ||
|
|
846802c003 | ||
|
|
e9ec32b3c3 | ||
|
|
4882bec118 | ||
|
|
89ff259be0 | ||
|
|
60ece7fbf7 | ||
|
|
0c110f574a | ||
|
|
dbca5b2a7e | ||
|
|
3088298e6b | ||
|
|
a9c6a12182 | ||
|
|
fa5b512629 | ||
|
|
5c2061a6e6 | ||
|
|
cf0fc956c9 | ||
|
|
a0517dfbeb | ||
|
|
39c71638e6 | ||
|
|
672b728379 | ||
|
|
750a546faf | ||
|
|
a41835573b | ||
|
|
2650cb89b5 | ||
|
|
4a122e0209 | ||
|
|
ce4bf62d75 | ||
|
|
40bbcb3250 | ||
|
|
905f51fbd0 | ||
|
|
cd4fe4362b | ||
|
|
ed7be6eb99 | ||
|
|
555007ab16 | ||
|
|
bd31b99324 | ||
|
|
60237c3c0b | ||
|
|
eb21833d94 | ||
|
|
763002ae14 | ||
|
|
ae2dc39a78 | ||
|
|
fe4ced2709 | ||
|
|
9075d68b7c | ||
|
|
759c0ea957 | ||
|
|
67b393d4a3 | ||
|
|
de71821759 | ||
|
|
0c2bcaee34 | ||
|
|
1613975e0e | ||
|
|
be82204df2 | ||
|
|
14c2ff5545 | ||
|
|
d7d0e11f2c | ||
|
|
6654f45cb8 | ||
|
|
23f92179ad | ||
|
|
7377917642 | ||
|
|
0f796859f2 | ||
|
|
6383230678 | ||
|
|
51536f8746 | ||
|
|
e3b6c061c4 | ||
|
|
4bd3fa74d1 | ||
|
|
71553988d5 | ||
|
|
761b24e614 | ||
|
|
10974902b5 | ||
|
|
474407dbc2 | ||
|
|
95d84f354d | ||
|
|
db47a9a253 | ||
|
|
709a4639b3 | ||
|
|
28b9cd02ef | ||
|
|
af9ea13933 | ||
|
|
bd2cd18916 | ||
|
|
23138dc0b4 | ||
|
|
a2f9742cfc | ||
|
|
6378e614b0 | ||
|
|
b116a57aa7 | ||
|
|
a03f32f521 | ||
|
|
b9180be685 | ||
|
|
334aee64ad | ||
|
|
16bd368a58 | ||
|
|
3266a0f85c | ||
|
|
4629f1b03f | ||
|
|
fbd0c6cbea | ||
|
|
8260051c30 | ||
|
|
c061c9c3ff | ||
|
|
8961191b2e | ||
|
|
fc0d99be41 | ||
|
|
6834e72c4a | ||
|
|
efe655f880 | ||
|
|
3d5ddce621 | ||
|
|
a3de3e15cb | ||
|
|
619999d4f8 | ||
|
|
7acf27dd38 | ||
|
|
ba517eeeb5 | ||
|
|
fdd3e24967 | ||
|
|
a570ce202a | ||
|
|
0a220bbc7a | ||
|
|
e0e511f56d | ||
|
|
d375dece0e | ||
|
|
f801f265ed | ||
|
|
1b4fc89b07 | ||
|
|
3ac2b77bf0 | ||
|
|
b2ca4ad66b | ||
|
|
25a7c7bc7f | ||
|
|
6b009a4de4 | ||
|
|
0b80a86e88 | ||
|
|
b03f24d59a | ||
|
|
78ea13d366 | ||
|
|
8c2bdfba1c | ||
|
|
3289968a93 | ||
|
|
73ae754aa7 | ||
|
|
20a6e7e210 | ||
|
|
4cf433a994 | ||
|
|
e36c4d397c | ||
|
|
26037327f9 | ||
|
|
da6aa1d697 | ||
|
|
dada6aa3d1 | ||
|
|
fa5ebb1677 | ||
|
|
f071df325d | ||
|
|
3c042c4011 | ||
|
|
7e8109caa3 | ||
|
|
d3add6d8e4 | ||
|
|
1b089749c0 | ||
|
|
791d6b7e57 | ||
|
|
233bb603cf | ||
|
|
db8a816524 | ||
|
|
eff50ca202 | ||
|
|
ceabb5ab2c | ||
|
|
122c2fd5e6 | ||
|
|
cd27a72982 | ||
|
|
19b0f0d7dc | ||
|
|
6ce2049935 | ||
|
|
53b937be63 | ||
|
|
71c8f99dab | ||
|
|
9eb3fca726 | ||
|
|
019bd4dec8 | ||
|
|
be1ce06c00 | ||
|
|
074bfe3db2 | ||
|
|
34e72b42dc | ||
|
|
97d24d76d8 | ||
|
|
4d1af867a4 | ||
|
|
fc6b4c12b2 | ||
|
|
405c6de591 | ||
|
|
47bfa5fcc0 | ||
|
|
67d91d5fc5 | ||
|
|
f832c56adb | ||
|
|
1aa9ae680e | ||
|
|
c4b30db82d | ||
|
|
abd9f3c6be | ||
|
|
3de3594282 | ||
|
|
ed5816d464 | ||
|
|
3d43bdbb49 | ||
|
|
1ab492ce5b | ||
|
|
de30c6ad79 | ||
|
|
f5a48ff98d | ||
|
|
8493ee5b83 | ||
|
|
52a6d55e5d | ||
|
|
7142295aa5 | ||
|
|
8b65be26a6 | ||
|
|
60f5deb494 | ||
|
|
0fc09e6dd3 | ||
|
|
0c4ccf4e3e | ||
|
|
a0e79bf446 | ||
|
|
aa356ad7c7 | ||
|
|
9e0e384d46 | ||
|
|
a20b99e643 | ||
|
|
fe4237b2b1 | ||
|
|
4146835f6f | ||
|
|
5201ea4516 | ||
|
|
fba7b36245 | ||
|
|
353ac0fc0c | ||
|
|
00002b1e24 | ||
|
|
aa32830671 | ||
|
|
12b8100d89 | ||
|
|
72e56d271d | ||
|
|
2b69f5eff4 | ||
|
|
f224bb98c4 | ||
|
|
ec17eb3fbc | ||
|
|
358b600713 | ||
|
|
aacb5c39ba | ||
|
|
9ae8804095 | ||
|
|
2d51bd895d | ||
|
|
18f656fed2 | ||
|
|
eea76999b2 | ||
|
|
bd495adf22 | ||
|
|
e8c7bee924 | ||
|
|
24b06c24dc | ||
|
|
6fdd764a35 | ||
|
|
2400d1f265 | ||
|
|
cdef430b0b | ||
|
|
6074e4ae2c | ||
|
|
6ada704bc3 | ||
|
|
e8f7daac6f | ||
|
|
404455928e | ||
|
|
bca3e62ced | ||
|
|
e79391907a | ||
|
|
54a23cc7fa | ||
|
|
e8ebe77923 | ||
|
|
68e0aa1ea9 | ||
|
|
ceaf64c9b3 | ||
|
|
e015b1bec5 | ||
|
|
ca9726c997 | ||
|
|
85c79abd30 | ||
|
|
54c1c7ad84 | ||
|
|
af15e31b42 | ||
|
|
99b4f9f4dd | ||
|
|
9a0e5ea626 | ||
|
|
984205e02c | ||
|
|
e11bf6ac67 | ||
|
|
e029d91544 | ||
|
|
011856cbfa | ||
|
|
9ce8164013 | ||
|
|
dca6b7bbf5 | ||
|
|
2511ec3395 | ||
|
|
9f6ffc7186 | ||
|
|
622f0217fa | ||
|
|
09eba49bab | ||
|
|
c20c3801a8 | ||
|
|
f8208b7288 | ||
|
|
3db8a65ddb | ||
|
|
0495b6f298 | ||
|
|
52c55f385d | ||
|
|
fe28d6fba0 | ||
|
|
0294e49d4a | ||
|
|
cbaf51d3ef | ||
|
|
efb604a381 | ||
|
|
da87f05126 | ||
|
|
21534b78cb | ||
|
|
ab65593f7d | ||
|
|
3a82691503 | ||
|
|
430e2ff0b5 | ||
|
|
7d572e7988 | ||
|
|
1449e24b66 | ||
|
|
6a671760da | ||
|
|
613bdd82b0 | ||
|
|
01bae358f9 | ||
|
|
3a8e560dc5 | ||
|
|
a0c538d9ee | ||
|
|
07c30f86e9 | ||
|
|
c22e38f9a0 | ||
|
|
4d082a87a1 | ||
|
|
1b6512fc8d | ||
|
|
7502e8a1e4 | ||
|
|
5a25211371 | ||
|
|
6983f9b2bf | ||
|
|
5affe64b61 | ||
|
|
c52f3c396b | ||
|
|
e1ebbaa52f | ||
|
|
7abe6af5ec | ||
|
|
c98ea6055b | ||
|
|
3e3d255edf | ||
|
|
816d4851e7 | ||
|
|
9e32886f60 | ||
|
|
79af315b9f | ||
|
|
4110c702c0 | ||
|
|
57eb29c3c8 | ||
|
|
5267c4eee6 | ||
|
|
1453ebe8ca | ||
|
|
3b5cea5768 | ||
|
|
a89f67348d | ||
|
|
af1ae0399c | ||
|
|
5bf84144e7 | ||
|
|
d9adff5749 | ||
|
|
a1cd8baf3e | ||
|
|
87cadce0ac | ||
|
|
f22f50afa2 | ||
|
|
d0fa6f7e03 | ||
|
|
d29c58dce5 | ||
|
|
9ea8f3b590 | ||
|
|
ffffc68144 | ||
|
|
086de9f2a0 | ||
|
|
1b731aa4a3 | ||
|
|
12913f6b90 | ||
|
|
1d4186392c | ||
|
|
48d3ab0cb4 | ||
|
|
a9d04729eb | ||
|
|
7adf2292bd | ||
|
|
c8f55f9c05 | ||
|
|
93de62f81d | ||
|
|
9766231d41 | ||
|
|
9df80e5bec | ||
|
|
81b00e4302 | ||
|
|
25ac7c31c4 | ||
|
|
11c6c9a2b8 | ||
|
|
066268765a | ||
|
|
356b33ced0 | ||
|
|
77088d55f9 | ||
|
|
8d965655a8 | ||
|
|
3ace4952e6 | ||
|
|
299f9f3a10 | ||
|
|
0e6790cdab | ||
|
|
1f90af3aec | ||
|
|
cdb18a5baf | ||
|
|
8adb6596fb | ||
|
|
dd6bcd68cc | ||
|
|
1c9ce0ede0 | ||
|
|
6b409b06cc | ||
|
|
361e864415 | ||
|
|
d5606114cd | ||
|
|
afca66d655 | ||
|
|
33a6c9fda7 | ||
|
|
a198484ab6 | ||
|
|
dd5bce9694 | ||
|
|
bef17ff76d | ||
|
|
7202605fc8 | ||
|
|
747dac531d | ||
|
|
07d24f70e1 | ||
|
|
d0546a2ea2 | ||
|
|
9e9621e7b2 | ||
|
|
d39f3b4265 | ||
|
|
223339cd61 | ||
|
|
dc430bae10 | ||
|
|
661b07e12f | ||
|
|
0727c7bdfb | ||
|
|
b4c52116b4 | ||
|
|
89dc6108b7 | ||
|
|
492eba3a8b | ||
|
|
77fee1390f | ||
|
|
bfb54189b8 | ||
|
|
fb43885d85 | ||
|
|
7c1f705e64 | ||
|
|
fe1fa7689a | ||
|
|
8b79eee081 | ||
|
|
4a3e04ced9 | ||
|
|
61618250ec | ||
|
|
60c0ca3ae5 | ||
|
|
3436405c55 | ||
|
|
d43c3ec865 | ||
|
|
545da96d15 | ||
|
|
f53305c404 | ||
|
|
199d976e3d | ||
|
|
a1366f0ef1 | ||
|
|
91192a8a8f | ||
|
|
c6f16b0558 | ||
|
|
0d37eb65c9 | ||
|
|
f7e3a74794 | ||
|
|
976b07cd61 | ||
|
|
cff9cd5fb8 | ||
|
|
f0fd947046 | ||
|
|
7f4ada9c4b | ||
|
|
db4ef328b1 | ||
|
|
b438802e71 | ||
|
|
632a19ceeb | ||
|
|
629f8ece7a | ||
|
|
9215f9beb5 | ||
|
|
154431e86c | ||
|
|
8cfb14198b | ||
|
|
2ed475fc76 | ||
|
|
34512da2fb | ||
|
|
5b3c6f7752 | ||
|
|
f4dd27ca3f | ||
|
|
ce47d8c591 | ||
|
|
b941ee9aa9 | ||
|
|
45de3f0128 | ||
|
|
41d3826c1a | ||
|
|
f2ff6e6a70 | ||
|
|
e47c90c645 | ||
|
|
d2fef20239 | ||
|
|
3b359cfc4f | ||
|
|
acfc5a4676 | ||
|
|
a6b1647f27 | ||
|
|
d5851bbb6a | ||
|
|
397bbe2655 | ||
|
|
95d8d3ef72 | ||
|
|
1b51a1aedd | ||
|
|
ff75b3cd1f | ||
|
|
2abcaf02e2 | ||
|
|
d0c9f5a776 | ||
|
|
a46d510e2b | ||
|
|
2d861b4077 | ||
|
|
470c62d993 | ||
|
|
364734094b | ||
|
|
a667c957ee | ||
|
|
dc648b0142 | ||
|
|
1d14140986 | ||
|
|
2b693dc492 | ||
|
|
38bea6c086 | ||
|
|
cbdc5f121e | ||
|
|
562763199a | ||
|
|
107c09604a | ||
|
|
2b0567b368 | ||
|
|
d00a56bec0 | ||
|
|
ead8cc6cec | ||
|
|
806ff9daaf | ||
|
|
b3863a3e10 | ||
|
|
5559107776 | ||
|
|
af1cf806f0 | ||
|
|
0cf33c6119 | ||
|
|
b38b8a3365 | ||
|
|
e998c8434d | ||
|
|
4b90cf93d3 | ||
|
|
3dd4e33758 | ||
|
|
6caeb11598 | ||
|
|
11a383b7e5 | ||
|
|
fd2a2530b1 | ||
|
|
2f42b2d072 | ||
|
|
775f58f972 | ||
|
|
5c0abb3d96 | ||
|
|
3dc46bc5a3 | ||
|
|
e2871fc048 | ||
|
|
44e3f9b49f | ||
|
|
d3f4263639 | ||
|
|
9dec05f62d | ||
|
|
0447aa07fa | ||
|
|
0e25af790d | ||
|
|
d8484e777f | ||
|
|
665a3dbcbf | ||
|
|
103af2e2c1 | ||
|
|
d0daeb0078 | ||
|
|
a9aa49c2e5 | ||
|
|
aad24389a7 | ||
|
|
4b4f6f3201 | ||
|
|
81f82d3b73 | ||
|
|
4103ccf791 | ||
|
|
e2ed11f960 | ||
|
|
8e1cd67a3d | ||
|
|
18b4caa55e | ||
|
|
30207ce0c2 | ||
|
|
0f37f1cb23 | ||
|
|
99a905cdee | ||
|
|
6eac079440 | ||
|
|
4a1e7b8d5a | ||
|
|
5e781a9010 | ||
|
|
277d817429 | ||
|
|
970f202757 | ||
|
|
2c6b2b47a4 | ||
|
|
5c8be4b21f | ||
|
|
558a4a2b30 | ||
|
|
203ad7d594 | ||
|
|
04cf1aed9c | ||
|
|
8c14526f85 | ||
|
|
1ff241411b | ||
|
|
351e36b273 | ||
|
|
ca06040efc | ||
|
|
2102cc4e9a | ||
|
|
14d3493db8 | ||
|
|
d082d208e1 | ||
|
|
959efd17c8 | ||
|
|
30a7c55e84 | ||
|
|
2aec759857 | ||
|
|
54dcaaac0c | ||
|
|
8e5730f374 | ||
|
|
cb1705b6e8 | ||
|
|
0bcefda60d | ||
|
|
966620f840 | ||
|
|
bd3bb67949 | ||
|
|
69f4daa9d2 | ||
|
|
ec11155c9c | ||
|
|
c48a3a24f7 | ||
|
|
1b8bc9f0ff | ||
|
|
5bf25c7eca | ||
|
|
5a7f0ad340 | ||
|
|
8a93922da0 | ||
|
|
295de5be8e | ||
|
|
5c114b584d | ||
|
|
dad58b7610 | ||
|
|
23c1dadb4c | ||
|
|
05332e0606 | ||
|
|
453842246f | ||
|
|
38280b9027 | ||
|
|
7d7c0011bb | ||
|
|
de2d87a6d9 | ||
|
|
537152450f | ||
|
|
87f6b19207 | ||
|
|
064fef5d8a | ||
|
|
6aebaa94af | ||
|
|
a1a6ac9c08 | ||
|
|
0d936425c2 | ||
|
|
00f867c6ee | ||
|
|
bfea3f35f0 | ||
|
|
3f3cf5e20e | ||
|
|
0786a962e7 | ||
|
|
f7c0486101 | ||
|
|
2e3798b282 | ||
|
|
181740c3e9 | ||
|
|
23c51a1d5f | ||
|
|
15ce529c09 | ||
|
|
0fd941401b | ||
|
|
808ae6a442 | ||
|
|
ada8d18fa1 | ||
|
|
9752abff19 | ||
|
|
ee43e68a57 | ||
|
|
348ac78f5a | ||
|
|
0b22fb933c | ||
|
|
cbd4b4849f | ||
|
|
f8a48c16f0 | ||
|
|
8cc4e4bfca | ||
|
|
6c01bb87bf | ||
|
|
02d5a6f15e | ||
|
|
f361403888 | ||
|
|
3a4550fe24 | ||
|
|
5e722c830f | ||
|
|
5a56982ad9 | ||
|
|
13d70cd12a | ||
|
|
bb87bdce8a | ||
|
|
cc7ded1be7 | ||
|
|
d8735e6a91 | ||
|
|
813ae71aad | ||
|
|
1a75bd87d8 | ||
|
|
bdc89e2056 | ||
|
|
bf09990f9c | ||
|
|
81ba8dc518 | ||
|
|
e519aaaf1e | ||
|
|
5a90a5e6c7 | ||
|
|
b855ea92a9 | ||
|
|
7e74d06cdd | ||
|
|
1e7602bc36 | ||
|
|
7477e672fe | ||
|
|
4a4522dfa3 | ||
|
|
8afe058cfc | ||
|
|
e13103a925 | ||
|
|
8e167ff174 | ||
|
|
c0dcd31819 | ||
|
|
a83ae996db | ||
|
|
a66ddabe8a | ||
|
|
cdab1e9cae | ||
|
|
f2140d1eb0 | ||
|
|
4a3fa85b5f | ||
|
|
ade23a9650 | ||
|
|
fc547e6c47 | ||
|
|
f532b5edee | ||
|
|
da76eb5cf4 | ||
|
|
ac89f352ce | ||
|
|
198b2a2ace | ||
|
|
93a3d78111 | ||
|
|
494997d021 | ||
|
|
eb1345e5c3 | ||
|
|
dc8f5d002d | ||
|
|
9030e5e6eb | ||
|
|
2b827baeed | ||
|
|
cc6fa312f0 | ||
|
|
b8eba5b643 | ||
|
|
0c3f98cc37 | ||
|
|
cd35770d26 | ||
|
|
bd3a59e9ca | ||
|
|
ff50d601b3 | ||
|
|
a4fde0f9e6 | ||
|
|
c7a621cb31 | ||
|
|
6f62d62909 | ||
|
|
c3aa2df4e9 | ||
|
|
f4fbf2da75 | ||
|
|
288de8acaa | ||
|
|
750e6cff45 | ||
|
|
f49e94052d | ||
|
|
ceb642b745 | ||
|
|
ed1d886f01 | ||
|
|
db98d811b0 | ||
|
|
14ba11e1ab | ||
|
|
483bdddb18 | ||
|
|
d16fda3f16 | ||
|
|
c718b94937 | ||
|
|
bb21ab073f | ||
|
|
3ffe530461 | ||
|
|
95cf0d86bc | ||
|
|
9899a5fc56 | ||
|
|
952648185c | ||
|
|
69740493b7 | ||
|
|
994a28f31d | ||
|
|
b0a8a03f0a | ||
|
|
36e740f4cc | ||
|
|
a5874a3f80 | ||
|
|
0c95b978b4 | ||
|
|
e21f9dd1fb | ||
|
|
74a08b86ce | ||
|
|
8212d3c540 | ||
|
|
1ed6458b39 | ||
|
|
60c8f23a63 | ||
|
|
5e627641ea | ||
|
|
abc954aa47 | ||
|
|
3bfa27e682 | ||
|
|
4b7e122254 | ||
|
|
cfd3c13751 | ||
|
|
6a1ee2b894 | ||
|
|
cbec4a4e81 | ||
|
|
c7d8c1cab5 | ||
|
|
c39e1e649d | ||
|
|
95ab64ba77 | ||
|
|
1f8fcdb0f3 | ||
|
|
fb26a8ae50 | ||
|
|
42dbeaa1a5 | ||
|
|
3305f4c03a | ||
|
|
f5beec70c8 | ||
|
|
c2cd21c15e | ||
|
|
633e389275 | ||
|
|
f5aaf298e1 | ||
|
|
7db6136436 | ||
|
|
ce7296eebb | ||
|
|
c6226547f7 | ||
|
|
e7ec8f0489 | ||
|
|
dc466f238b | ||
|
|
f164656874 | ||
|
|
ef3c59152f | ||
|
|
14c6ead389 | ||
|
|
b0d33f3a85 | ||
|
|
066cc20ee6 | ||
|
|
0f51b5b1ce | ||
|
|
ebcf3e0119 | ||
|
|
6ee248d656 | ||
|
|
8a46b410f6 | ||
|
|
cd06cea153 | ||
|
|
80e033c91d | ||
|
|
a7dd441333 | ||
|
|
48efaa9fd7 | ||
|
|
92e831175f | ||
|
|
9b75e076b5 | ||
|
|
2c5d6d1651 | ||
|
|
c5cf116f33 | ||
|
|
66a4a9bce6 | ||
|
|
7e7ef64c79 | ||
|
|
9a31f53d4d | ||
|
|
60d6314262 | ||
|
|
eff7445804 | ||
|
|
2bfe21aef4 | ||
|
|
6ae0c7dcfc | ||
|
|
883ac1007a |
@@ -1,3 +1,4 @@
|
||||
bin/rr
|
||||
config/autoload/*local*
|
||||
data/infra
|
||||
data/cache/*
|
||||
@@ -9,6 +10,7 @@ data/GeoLite2-City*
|
||||
data/database.sqlite
|
||||
data/shlink-tests.db
|
||||
CHANGELOG.md
|
||||
CONTRIBUTING.md
|
||||
UPGRADE.md
|
||||
composer.lock
|
||||
vendor
|
||||
@@ -21,4 +23,4 @@ infection*
|
||||
**/test*
|
||||
build*
|
||||
**/.*
|
||||
bin/helper
|
||||
!config/roadrunner/.rr.yml
|
||||
|
||||
2
.github/ISSUE_TEMPLATE/Bug.md
vendored
2
.github/ISSUE_TEMPLATE/Bug.md
vendored
@@ -18,7 +18,7 @@ With that said, please fill in the information requested next. More information
|
||||
|
||||
* Shlink Version: x.y.z
|
||||
* PHP Version: x.y.z
|
||||
* How do you serve Shlink: Self-hosted Apache|Self-hosted nginx|Self-hosted swoole|Docker image
|
||||
* How do you serve Shlink: Self-hosted Apache|Self-hosted nginx|Self-hosted openswoole|Docker image
|
||||
* Database engine used: MySQL|MariaDB|PostgreSQL|MicrosoftSQL|SQLite (x.y.z)
|
||||
|
||||
#### Summary
|
||||
|
||||
2
.github/ISSUE_TEMPLATE/Question_Support.md
vendored
2
.github/ISSUE_TEMPLATE/Question_Support.md
vendored
@@ -18,7 +18,7 @@ With that said, please fill in the information requested next. More information
|
||||
|
||||
* Shlink Version: x.y.z
|
||||
* PHP Version: x.y.z
|
||||
* How do you serve Shlink: Self-hosted Apache|Self-hosted nginx|Self-hosted swoole|Docker image
|
||||
* How do you serve Shlink: Self-hosted Apache|Self-hosted nginx|Self-hosted openswoole|Docker image
|
||||
* Database engine used: MySQL|MariaDB|PostgreSQL|MicrosoftSQL|SQLite (x.y.z)
|
||||
|
||||
#### Summary
|
||||
|
||||
50
.github/actions/ci-setup/action.yml
vendored
Normal file
50
.github/actions/ci-setup/action.yml
vendored
Normal file
@@ -0,0 +1,50 @@
|
||||
name: CI setup
|
||||
description: 'Sets up the environment to run CI actions for Shlink'
|
||||
|
||||
inputs:
|
||||
install-deps:
|
||||
description: 'Tells if dependencies should be installed with composer. Default value is "yes"'
|
||||
required: true
|
||||
default: 'yes'
|
||||
php-version:
|
||||
description: 'The PHP version to be setup'
|
||||
required: true
|
||||
php-extensions:
|
||||
description: 'The PHP extensions to install'
|
||||
required: false
|
||||
default: ''
|
||||
extensions-cache-key:
|
||||
description: 'The key used to cache PHP extensions. If empty value is provided, extension caching is disabled'
|
||||
required: true
|
||||
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Setup cache environment
|
||||
id: extcache
|
||||
uses: shivammathur/cache-extensions@v1
|
||||
with:
|
||||
php-version: ${{ inputs.php-version }}
|
||||
extensions: ${{ inputs.php-extensions }}
|
||||
key: ${{ inputs.extensions-cache-key }}
|
||||
- name: Cache extensions
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: ${{ steps.extcache.outputs.dir }}
|
||||
key: ${{ steps.extcache.outputs.key }}
|
||||
restore-keys: ${{ steps.extcache.outputs.key }}
|
||||
- name: Use PHP
|
||||
uses: shivammathur/setup-php@v2
|
||||
with:
|
||||
php-version: ${{ inputs.php-version }}
|
||||
tools: composer
|
||||
extensions: ${{ inputs.php-extensions }}
|
||||
coverage: pcov
|
||||
ini-values: pcov.directory=module
|
||||
- run: echo "::set-output name=composerArgs::${{ inputs.php-version == '8.2' && '--ignore-platform-req=php' || '' }}"
|
||||
id: composer_args
|
||||
shell: bash
|
||||
- name: Install dependencies
|
||||
if: ${{ inputs.install-deps == 'yes' }}
|
||||
run: composer install --no-interaction --prefer-dist ${{ steps.composer_args.outputs.composerArgs }}
|
||||
shell: bash
|
||||
45
.github/workflows/ci-db-tests.yml
vendored
Normal file
45
.github/workflows/ci-db-tests.yml
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
name: Database tests
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
platform:
|
||||
type: string
|
||||
required: true
|
||||
description: One of sqlite:ci, mysql, maria, postgres or ms
|
||||
|
||||
jobs:
|
||||
db-tests:
|
||||
runs-on: ubuntu-22.04
|
||||
strategy:
|
||||
matrix:
|
||||
php-version: ['8.1', '8.2']
|
||||
continue-on-error: ${{ matrix.php-version == '8.2' }}
|
||||
env:
|
||||
LC_ALL: C
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Install MSSQL ODBC
|
||||
if: ${{ inputs.platform == 'ms' }}
|
||||
run: sudo ./data/infra/ci/install-ms-odbc.sh
|
||||
- name: Start database server
|
||||
if: ${{ inputs.platform != 'sqlite:ci' }}
|
||||
run: docker-compose -f docker-compose.yml -f docker-compose.ci.yml up -d shlink_db_${{ inputs.platform }}
|
||||
- uses: './.github/actions/ci-setup'
|
||||
with:
|
||||
php-version: ${{ matrix.php-version }}
|
||||
php-extensions: openswoole-4.11.1, pdo_sqlsrv-5.10.1
|
||||
extensions-cache-key: db-tests-extensions-${{ matrix.php-version }}-${{ inputs.platform }}
|
||||
- name: Create test database
|
||||
if: ${{ inputs.platform == 'ms' }}
|
||||
run: docker-compose exec -T shlink_db_ms /opt/mssql-tools/bin/sqlcmd -S localhost -U sa -P 'Passw0rd!' -Q "CREATE DATABASE shlink_test;"
|
||||
- name: Run tests
|
||||
run: composer test:db:${{ inputs.platform }}
|
||||
- name: Upload code coverage
|
||||
uses: actions/upload-artifact@v3
|
||||
if: ${{ matrix.php-version == '8.1' && inputs.platform == 'sqlite:ci' }}
|
||||
with:
|
||||
name: coverage-db
|
||||
path: |
|
||||
build/coverage-db
|
||||
build/coverage-db.cov
|
||||
46
.github/workflows/ci-mutation-tests.yml
vendored
Normal file
46
.github/workflows/ci-mutation-tests.yml
vendored
Normal file
@@ -0,0 +1,46 @@
|
||||
name: Mutation tests
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
test-group:
|
||||
type: string
|
||||
required: true
|
||||
description: One of unit, db, api or cli
|
||||
|
||||
jobs:
|
||||
mutation-tests:
|
||||
runs-on: ubuntu-22.04
|
||||
strategy:
|
||||
matrix:
|
||||
php-version: ['8.1', '8.2']
|
||||
continue-on-error: ${{ matrix.php-version == '8.2' }}
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: './.github/actions/ci-setup'
|
||||
with:
|
||||
php-version: ${{ matrix.php-version }}
|
||||
php-extensions: openswoole-4.11.1
|
||||
extensions-cache-key: mutation-tests-extensions-${{ matrix.php-version }}-${{ inputs.test-group }}
|
||||
- uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: coverage-${{ inputs.test-group }}
|
||||
path: build
|
||||
- name: Resolve infection args
|
||||
id: infection_args
|
||||
run: echo "::set-output name=args::--logger-github=false"
|
||||
# TODO Try to filter mutation tests to improve execution times. Investigate why --git-diff-lines --git-diff-base=develop does not work
|
||||
# run: |
|
||||
# BRANCH="${GITHUB_REF#refs/heads/}" |
|
||||
# if [[ $BRANCH == 'main' || $BRANCH == 'develop' ]]; then
|
||||
# echo "::set-output name=args::--logger-github=false"
|
||||
# else
|
||||
# echo "::set-output name=args::--logger-github=false --git-diff-lines --git-diff-base=develop"
|
||||
# fi;
|
||||
shell: bash
|
||||
- if: ${{ inputs.test-group == 'unit' }}
|
||||
run: composer infect:ci:unit -- ${{ steps.infection_args.outputs.args }}
|
||||
env:
|
||||
INFECTION_BADGE_API_KEY: ${{ secrets.INFECTION_BADGE_API_KEY }}
|
||||
- if: ${{ inputs.test-group != 'unit' }}
|
||||
run: composer infect:ci:${{ inputs.test-group }} -- ${{ steps.infection_args.outputs.args }}
|
||||
38
.github/workflows/ci-tests.yml
vendored
Normal file
38
.github/workflows/ci-tests.yml
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
name: Tests
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
test-group:
|
||||
type: string
|
||||
required: true
|
||||
description: One of unit, api or cli
|
||||
|
||||
jobs:
|
||||
tests:
|
||||
runs-on: ubuntu-22.04
|
||||
strategy:
|
||||
matrix:
|
||||
php-version: ['8.1', '8.2']
|
||||
continue-on-error: ${{ matrix.php-version == '8.2' }}
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Start postgres database server
|
||||
if: ${{ inputs.test-group == 'api' }}
|
||||
run: docker-compose -f docker-compose.yml -f docker-compose.ci.yml up -d shlink_db_postgres
|
||||
- name: Start maria database server
|
||||
if: ${{ inputs.test-group == 'cli' }}
|
||||
run: docker-compose -f docker-compose.yml -f docker-compose.ci.yml up -d shlink_db_maria
|
||||
- uses: './.github/actions/ci-setup'
|
||||
with:
|
||||
php-version: ${{ matrix.php-version }}
|
||||
php-extensions: openswoole-4.11.1
|
||||
extensions-cache-key: tests-extensions-${{ matrix.php-version }}-${{ inputs.test-group }}
|
||||
- run: composer test:${{ inputs.test-group }}:ci
|
||||
- uses: actions/upload-artifact@v3
|
||||
if: ${{ matrix.php-version == '8.1' }}
|
||||
with:
|
||||
name: coverage-${{ inputs.test-group }}
|
||||
path: |
|
||||
build/coverage-${{ inputs.test-group }}
|
||||
build/coverage-${{ inputs.test-group }}.cov
|
||||
315
.github/workflows/ci.yml
vendored
315
.github/workflows/ci.yml
vendored
@@ -6,261 +6,140 @@ on:
|
||||
branches:
|
||||
- main
|
||||
- develop
|
||||
- 2.x
|
||||
|
||||
jobs:
|
||||
lint:
|
||||
runs-on: ubuntu-20.04
|
||||
strategy:
|
||||
matrix:
|
||||
php-version: ['8.0']
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
- name: Use PHP
|
||||
uses: shivammathur/setup-php@v2
|
||||
with:
|
||||
php-version: ${{ matrix.php-version }}
|
||||
tools: composer
|
||||
extensions: swoole-4.6.7
|
||||
coverage: none
|
||||
- run: composer install --no-interaction --prefer-dist
|
||||
- run: composer cs
|
||||
|
||||
static-analysis:
|
||||
runs-on: ubuntu-20.04
|
||||
runs-on: ubuntu-22.04
|
||||
strategy:
|
||||
matrix:
|
||||
php-version: ['8.0']
|
||||
php-version: ['8.1']
|
||||
command: ['cs', 'stan', 'swagger:validate']
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
- name: Use PHP
|
||||
uses: shivammathur/setup-php@v2
|
||||
- uses: actions/checkout@v3
|
||||
- uses: './.github/actions/ci-setup'
|
||||
with:
|
||||
php-version: ${{ matrix.php-version }}
|
||||
tools: composer
|
||||
extensions: swoole-4.6.7
|
||||
coverage: none
|
||||
- run: composer install --no-interaction --prefer-dist
|
||||
- run: composer stan
|
||||
php-extensions: openswoole-4.11.1
|
||||
extensions-cache-key: tests-extensions-${{ matrix.php-version }}-${{ matrix.command }}
|
||||
- run: composer ${{ matrix.command }}
|
||||
|
||||
unit-tests:
|
||||
runs-on: ubuntu-20.04
|
||||
uses: './.github/workflows/ci-tests.yml'
|
||||
with:
|
||||
test-group: unit
|
||||
|
||||
cli-tests:
|
||||
uses: './.github/workflows/ci-tests.yml'
|
||||
with:
|
||||
test-group: cli
|
||||
|
||||
openswoole-api-tests:
|
||||
uses: './.github/workflows/ci-tests.yml'
|
||||
with:
|
||||
test-group: api
|
||||
|
||||
roadrunner-api-tests:
|
||||
runs-on: ubuntu-22.04
|
||||
strategy:
|
||||
matrix:
|
||||
php-version: ['8.0']
|
||||
php-version: ['8.1', '8.2']
|
||||
continue-on-error: ${{ matrix.php-version == '8.2' }}
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
- name: Use PHP
|
||||
uses: shivammathur/setup-php@v2
|
||||
- uses: actions/checkout@v3
|
||||
- run: docker-compose -f docker-compose.yml -f docker-compose.ci.yml up -d shlink_db_postgres
|
||||
- uses: shivammathur/setup-php@v2
|
||||
with:
|
||||
php-version: ${{ matrix.php-version }}
|
||||
tools: composer
|
||||
extensions: swoole-4.6.7
|
||||
coverage: pcov
|
||||
ini-values: pcov.directory=module
|
||||
- run: composer install --no-interaction --prefer-dist
|
||||
- run: composer test:unit:ci
|
||||
- uses: actions/upload-artifact@v2
|
||||
if: ${{ matrix.php-version == '8.0' }}
|
||||
with:
|
||||
name: coverage-unit
|
||||
path: |
|
||||
build/coverage-unit
|
||||
build/coverage-unit.cov
|
||||
- run: echo "::set-output name=composerArgs::${{ matrix.php-version == '8.2' && '--ignore-platform-req=php' || '' }}"
|
||||
id: composer_args
|
||||
shell: bash
|
||||
- run: composer install --no-interaction --prefer-dist ${{ steps.composer_args.outputs.composerArgs }}
|
||||
- run: ./vendor/bin/rr get --no-interaction --location bin/ && chmod +x bin/rr
|
||||
- run: composer test:api:rr
|
||||
|
||||
db-tests-sqlite:
|
||||
runs-on: ubuntu-20.04
|
||||
strategy:
|
||||
matrix:
|
||||
php-version: ['8.0']
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
- name: Use PHP
|
||||
uses: shivammathur/setup-php@v2
|
||||
with:
|
||||
php-version: ${{ matrix.php-version }}
|
||||
tools: composer
|
||||
extensions: swoole-4.6.7
|
||||
coverage: pcov
|
||||
ini-values: pcov.directory=module
|
||||
- run: composer install --no-interaction --prefer-dist
|
||||
- run: composer test:db:sqlite:ci
|
||||
- uses: actions/upload-artifact@v2
|
||||
if: ${{ matrix.php-version == '8.0' }}
|
||||
with:
|
||||
name: coverage-db
|
||||
path: |
|
||||
build/coverage-db
|
||||
build/coverage-db.cov
|
||||
sqlite-db-tests:
|
||||
uses: './.github/workflows/ci-db-tests.yml'
|
||||
with:
|
||||
platform: 'sqlite:ci'
|
||||
|
||||
db-tests-mysql:
|
||||
runs-on: ubuntu-20.04
|
||||
strategy:
|
||||
matrix:
|
||||
php-version: ['8.0']
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
- name: Start database server
|
||||
run: docker-compose -f docker-compose.yml -f docker-compose.ci.yml up -d shlink_db
|
||||
- name: Use PHP
|
||||
uses: shivammathur/setup-php@v2
|
||||
with:
|
||||
php-version: ${{ matrix.php-version }}
|
||||
tools: composer
|
||||
extensions: swoole-4.6.7
|
||||
coverage: none
|
||||
- run: composer install --no-interaction --prefer-dist
|
||||
- run: composer test:db:mysql
|
||||
mysql-db-tests:
|
||||
uses: './.github/workflows/ci-db-tests.yml'
|
||||
with:
|
||||
platform: 'mysql'
|
||||
|
||||
db-tests-maria:
|
||||
runs-on: ubuntu-20.04
|
||||
strategy:
|
||||
matrix:
|
||||
php-version: ['8.0']
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
- name: Start database server
|
||||
run: docker-compose -f docker-compose.yml -f docker-compose.ci.yml up -d shlink_db_maria
|
||||
- name: Use PHP
|
||||
uses: shivammathur/setup-php@v2
|
||||
with:
|
||||
php-version: ${{ matrix.php-version }}
|
||||
tools: composer
|
||||
extensions: swoole-4.6.7
|
||||
coverage: none
|
||||
- run: composer install --no-interaction --prefer-dist
|
||||
- run: composer test:db:maria
|
||||
maria-db-tests:
|
||||
uses: './.github/workflows/ci-db-tests.yml'
|
||||
with:
|
||||
platform: 'maria'
|
||||
|
||||
db-tests-postgres:
|
||||
runs-on: ubuntu-20.04
|
||||
strategy:
|
||||
matrix:
|
||||
php-version: ['8.0']
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
- name: Start database server
|
||||
run: docker-compose -f docker-compose.yml -f docker-compose.ci.yml up -d shlink_db_postgres
|
||||
- name: Use PHP
|
||||
uses: shivammathur/setup-php@v2
|
||||
with:
|
||||
php-version: ${{ matrix.php-version }}
|
||||
tools: composer
|
||||
extensions: swoole-4.6.7
|
||||
coverage: none
|
||||
- run: composer install --no-interaction --prefer-dist
|
||||
- run: composer test:db:postgres
|
||||
postgres-db-tests:
|
||||
uses: './.github/workflows/ci-db-tests.yml'
|
||||
with:
|
||||
platform: 'postgres'
|
||||
|
||||
db-tests-ms:
|
||||
runs-on: ubuntu-20.04
|
||||
strategy:
|
||||
matrix:
|
||||
php-version: ['8.0']
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
- name: Install MSSQL ODBC
|
||||
run: sudo ./data/infra/ci/install-ms-odbc.sh
|
||||
- name: Start database server
|
||||
run: docker-compose -f docker-compose.yml -f docker-compose.ci.yml up -d shlink_db_ms
|
||||
- name: Use PHP
|
||||
uses: shivammathur/setup-php@v2
|
||||
with:
|
||||
php-version: ${{ matrix.php-version }}
|
||||
tools: composer
|
||||
extensions: swoole-4.6.7, pdo_sqlsrv-5.9.0
|
||||
coverage: none
|
||||
- run: composer install --no-interaction --prefer-dist
|
||||
- name: Create test database
|
||||
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: composer test:db:ms
|
||||
ms-db-tests:
|
||||
uses: './.github/workflows/ci-db-tests.yml'
|
||||
with:
|
||||
platform: 'ms'
|
||||
|
||||
api-tests:
|
||||
runs-on: ubuntu-20.04
|
||||
strategy:
|
||||
matrix:
|
||||
php-version: ['8.0']
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
- name: Start database server
|
||||
run: docker-compose -f docker-compose.yml -f docker-compose.ci.yml up -d shlink_db_postgres
|
||||
- name: Use PHP
|
||||
uses: shivammathur/setup-php@v2
|
||||
with:
|
||||
php-version: ${{ matrix.php-version }}
|
||||
tools: composer
|
||||
extensions: swoole-4.6.7
|
||||
coverage: pcov
|
||||
ini-values: pcov.directory=module
|
||||
- run: composer install --no-interaction --prefer-dist
|
||||
- run: bin/test/run-api-tests.sh
|
||||
- uses: actions/upload-artifact@v2
|
||||
if: ${{ matrix.php-version == '8.0' }}
|
||||
with:
|
||||
name: coverage-api
|
||||
path: |
|
||||
build/coverage-api
|
||||
build/coverage-api.cov
|
||||
|
||||
mutation-tests:
|
||||
unit-mutation-tests:
|
||||
needs:
|
||||
- unit-tests
|
||||
- db-tests-sqlite
|
||||
- api-tests
|
||||
runs-on: ubuntu-20.04
|
||||
strategy:
|
||||
matrix:
|
||||
php-version: ['8.0']
|
||||
test-group: ['unit', 'db']
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
- name: Use PHP
|
||||
uses: shivammathur/setup-php@v2
|
||||
with:
|
||||
php-version: ${{ matrix.php-version }}
|
||||
tools: composer
|
||||
extensions: swoole-4.6.7
|
||||
coverage: pcov
|
||||
ini-values: pcov.directory=module
|
||||
- run: composer install --no-interaction --prefer-dist
|
||||
- uses: actions/download-artifact@v2
|
||||
with:
|
||||
path: build
|
||||
- run: composer infect:ci:${{ matrix.test-group }}
|
||||
uses: './.github/workflows/ci-mutation-tests.yml'
|
||||
with:
|
||||
test-group: unit
|
||||
|
||||
db-mutation-tests:
|
||||
needs:
|
||||
- sqlite-db-tests
|
||||
uses: './.github/workflows/ci-mutation-tests.yml'
|
||||
with:
|
||||
test-group: db
|
||||
|
||||
api-mutation-tests:
|
||||
needs:
|
||||
- openswoole-api-tests
|
||||
uses: './.github/workflows/ci-mutation-tests.yml'
|
||||
with:
|
||||
test-group: api
|
||||
|
||||
cli-mutation-tests:
|
||||
needs:
|
||||
- cli-tests
|
||||
uses: './.github/workflows/ci-mutation-tests.yml'
|
||||
with:
|
||||
test-group: cli
|
||||
|
||||
upload-coverage:
|
||||
needs:
|
||||
- unit-tests
|
||||
- db-tests-sqlite
|
||||
- api-tests
|
||||
runs-on: ubuntu-20.04
|
||||
- openswoole-api-tests
|
||||
- cli-tests
|
||||
- sqlite-db-tests
|
||||
runs-on: ubuntu-22.04
|
||||
strategy:
|
||||
matrix:
|
||||
php-version: ['8.0']
|
||||
php-version: ['8.1']
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
- name: Use PHP
|
||||
uses: shivammathur/setup-php@v2
|
||||
with:
|
||||
php-version: ${{ matrix.php-version }}
|
||||
coverage: pcov
|
||||
ini-values: pcov.directory=module
|
||||
- uses: actions/download-artifact@v2
|
||||
- uses: actions/download-artifact@v3
|
||||
with:
|
||||
path: build
|
||||
- run: mv build/coverage-unit/coverage-unit.cov build/coverage-unit.cov
|
||||
- run: mv build/coverage-db/coverage-db.cov build/coverage-db.cov
|
||||
- run: mv build/coverage-api/coverage-api.cov build/coverage-api.cov
|
||||
- run: wget https://phar.phpunit.de/phpcov-8.2.0.phar
|
||||
- run: php phpcov-8.2.0.phar merge build --clover build/clover.xml
|
||||
- run: mv build/coverage-cli/coverage-cli.cov build/coverage-cli.cov
|
||||
- run: wget https://phar.phpunit.de/phpcov-8.2.1.phar
|
||||
- run: php phpcov-8.2.1.phar merge build --clover build/clover.xml
|
||||
- name: Publish coverage
|
||||
uses: codecov/codecov-action@v1
|
||||
with:
|
||||
@@ -268,9 +147,12 @@ jobs:
|
||||
|
||||
delete-artifacts:
|
||||
needs:
|
||||
- mutation-tests
|
||||
- unit-mutation-tests
|
||||
- db-mutation-tests
|
||||
- api-mutation-tests
|
||||
- cli-mutation-tests
|
||||
- upload-coverage
|
||||
runs-on: ubuntu-20.04
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: geekyeggo/delete-artifact@v1
|
||||
with:
|
||||
@@ -278,12 +160,13 @@ jobs:
|
||||
coverage-unit
|
||||
coverage-db
|
||||
coverage-api
|
||||
coverage-cli
|
||||
|
||||
build-docker-image:
|
||||
runs-on: ubuntu-20.04
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 100
|
||||
- uses: marceloprado/has-changed-path@v1
|
||||
|
||||
37
.github/workflows/docker-image-build.yml
vendored
37
.github/workflows/docker-image-build.yml
vendored
@@ -1,4 +1,4 @@
|
||||
name: Build docker image
|
||||
name: Build and publish docker image
|
||||
|
||||
on:
|
||||
push:
|
||||
@@ -8,21 +8,20 @@ on:
|
||||
- 'v*'
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v1
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v1
|
||||
with:
|
||||
version: latest
|
||||
- name: Login to docker hub
|
||||
uses: docker/login-action@v1
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||
- name: Build the image
|
||||
run: bash ./docker/build
|
||||
build-openswool:
|
||||
uses: shlinkio/github-actions/.github/workflows/docker-build-and-publish.yml@main
|
||||
secrets: inherit
|
||||
with:
|
||||
image-name: shlinkio/shlink
|
||||
version-arg-name: SHLINK_VERSION
|
||||
|
||||
build-roadrunner:
|
||||
uses: shlinkio/github-actions/.github/workflows/docker-build-and-publish.yml@main
|
||||
secrets: inherit
|
||||
with:
|
||||
image-name: shlinkio/shlink
|
||||
version-arg-name: SHLINK_VERSION
|
||||
platforms: 'linux/arm64/v8,linux/amd64'
|
||||
tags-suffix: roadrunner
|
||||
extra-build-args: |
|
||||
SHLINK_RUNTIME=rr
|
||||
|
||||
30
.github/workflows/publish-release.yml
vendored
30
.github/workflows/publish-release.yml
vendored
@@ -7,36 +7,34 @@ on:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-20.04
|
||||
runs-on: ubuntu-22.04
|
||||
strategy:
|
||||
matrix:
|
||||
php-version: ['8.0']
|
||||
php-version: ['8.1']
|
||||
swoole: ['yes', 'no']
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
- name: Use PHP
|
||||
uses: shivammathur/setup-php@v2
|
||||
- uses: actions/checkout@v3
|
||||
- uses: './.github/actions/ci-setup'
|
||||
with:
|
||||
php-version: ${{ matrix.php-version }}
|
||||
tools: composer
|
||||
extensions: swoole-4.6.7
|
||||
php-extensions: openswoole-4.11.1
|
||||
extensions-cache-key: publish-swagger-spec-extensions-${{ matrix.php-version }}
|
||||
install-deps: 'no'
|
||||
- if: ${{ matrix.swoole == 'yes' }}
|
||||
run: ./build.sh ${GITHUB_REF#refs/tags/v}
|
||||
- if: ${{ matrix.swoole == 'no' }}
|
||||
run: ./build.sh ${GITHUB_REF#refs/tags/v} --no-swoole
|
||||
- uses: actions/upload-artifact@v2
|
||||
- uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: dist-files-${{ matrix.php-version }}-${{ matrix.swoole }}
|
||||
path: build
|
||||
|
||||
publish:
|
||||
needs: ['build']
|
||||
runs-on: ubuntu-20.04
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
- uses: actions/download-artifact@v2
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/download-artifact@v3
|
||||
with:
|
||||
path: build
|
||||
- name: Publish release with assets
|
||||
@@ -50,11 +48,11 @@ jobs:
|
||||
|
||||
delete-artifacts:
|
||||
needs: ['publish']
|
||||
runs-on: ubuntu-20.04
|
||||
runs-on: ubuntu-22.04
|
||||
strategy:
|
||||
matrix:
|
||||
php-version: [ '8.0' ]
|
||||
swoole: [ 'yes', 'no' ]
|
||||
php-version: ['8.1']
|
||||
swoole: ['yes', 'no']
|
||||
steps:
|
||||
- uses: geekyeggo/delete-artifact@v1
|
||||
with:
|
||||
|
||||
36
.github/workflows/publish-swagger-spec.yml
vendored
Normal file
36
.github/workflows/publish-swagger-spec.yml
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
name: Publish swagger spec
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- 'v*'
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-22.04
|
||||
strategy:
|
||||
matrix:
|
||||
php-version: ['8.1']
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Determine version
|
||||
id: determine_version
|
||||
run: echo "::set-output name=version::${GITHUB_REF#refs/tags/}"
|
||||
shell: bash
|
||||
- uses: './.github/actions/ci-setup'
|
||||
with:
|
||||
php-version: ${{ matrix.php-version }}
|
||||
php-extensions: openswoole-4.11.1
|
||||
extensions-cache-key: publish-swagger-spec-extensions-${{ matrix.php-version }}
|
||||
- run: composer swagger:inline
|
||||
- run: mkdir ${{ steps.determine_version.outputs.version }}
|
||||
- run: mv docs/swagger/swagger-inlined.json ${{ steps.determine_version.outputs.version }}/open-api-spec.json
|
||||
- name: Publish spec
|
||||
uses: JamesIves/github-pages-deploy-action@4.1.7
|
||||
with:
|
||||
token: ${{ secrets.OAS_PUBLISH_TOKEN }}
|
||||
repository-name: 'shlinkio/shlink-open-api-specs'
|
||||
branch: main
|
||||
folder: ${{ steps.determine_version.outputs.version }}
|
||||
target-folder: specs/${{ steps.determine_version.outputs.version }}
|
||||
clean: false
|
||||
4
.gitignore
vendored
4
.gitignore
vendored
@@ -1,4 +1,7 @@
|
||||
.idea
|
||||
bin/.rr.*
|
||||
bin/rr
|
||||
config/roadrunner/.pid
|
||||
build
|
||||
!docker/build
|
||||
composer.lock
|
||||
@@ -11,3 +14,4 @@ docs/swagger-ui*
|
||||
docs/mercure.html
|
||||
docker-compose.override.yml
|
||||
.phpunit.result.cache
|
||||
docs/swagger/swagger-inlined.json
|
||||
|
||||
515
CHANGELOG.md
515
CHANGELOG.md
@@ -4,6 +4,509 @@ 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.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*
|
||||
|
||||
|
||||
## [2.10.3] - 2022-01-23
|
||||
### Added
|
||||
* *Nothing*
|
||||
|
||||
### Changed
|
||||
* *Nothing*
|
||||
|
||||
### Deprecated
|
||||
* *Nothing*
|
||||
|
||||
### Removed
|
||||
* *Nothing*
|
||||
|
||||
### Fixed
|
||||
* [#1349](https://github.com/shlinkio/shlink/issues/1349) Fixed memory leak in cache implementation.
|
||||
|
||||
|
||||
## [2.10.2] - 2022-01-07
|
||||
### Added
|
||||
* *Nothing*
|
||||
|
||||
### Changed
|
||||
* *Nothing*
|
||||
|
||||
### Deprecated
|
||||
* *Nothing*
|
||||
|
||||
### Removed
|
||||
* *Nothing*
|
||||
|
||||
### Fixed
|
||||
* [#1293](https://github.com/shlinkio/shlink/issues/1293) Fixed error when trying to create/import short URLs with a too long title.
|
||||
* [#1306](https://github.com/shlinkio/shlink/issues/1306) Ensured remote IP address is not logged when using swoole/openswoole.
|
||||
* [#1308](https://github.com/shlinkio/shlink/issues/1308) Fixed memory leak when using redis due to the amount of non-expiring keys created by doctrine. Now they have a 24h expiration by default.
|
||||
|
||||
|
||||
## [2.10.1] - 2021-12-21
|
||||
### Added
|
||||
* *Nothing*
|
||||
|
||||
### Changed
|
||||
* *Nothing*
|
||||
|
||||
### Deprecated
|
||||
* *Nothing*
|
||||
|
||||
### Removed
|
||||
* *Nothing*
|
||||
|
||||
### Fixed
|
||||
* [#1285](https://github.com/shlinkio/shlink/issues/1285) Fixed error caused by database connections expiring after some hours of inactivity.
|
||||
* [#1286](https://github.com/shlinkio/shlink/issues/1286) Fixed `x-request-id` header not being generated during non-rest requests.
|
||||
|
||||
|
||||
## [2.10.0] - 2021-12-12
|
||||
### Added
|
||||
* [#1163](https://github.com/shlinkio/shlink/issues/1163) Allowed setting not-found redirects for default domain in the same way it's done for any other domain.
|
||||
|
||||
This implies a few non-breaking changes:
|
||||
|
||||
* The domains list no longer has the values of `INVALID_SHORT_URL_REDIRECT_TO`, `REGULAR_404_REDIRECT_TO` and `BASE_URL_REDIRECT_TO` on the default domain redirects.
|
||||
* The `GET /domains` endpoint includes a new `defaultRedirects` property in the response, with the default redirects set via config or env vars.
|
||||
* The `INVALID_SHORT_URL_REDIRECT_TO`, `REGULAR_404_REDIRECT_TO` and `BASE_URL_REDIRECT_TO` env vars are now deprecated, and should be replaced by `DEFAULT_INVALID_SHORT_URL_REDIRECT`, `DEFAULT_REGULAR_404_REDIRECT` and `DEFAULT_BASE_URL_REDIRECT` respectively. Deprecated ones will continue to work until v3.0.0, where they will be removed.
|
||||
|
||||
* [#868](https://github.com/shlinkio/shlink/issues/868) Added support to publish real-time updates in a RabbitMQ server.
|
||||
|
||||
Shlink will create new exchanges and queues for every topic documented in the [Async API spec](https://api-spec.shlink.io/async-api/), meaning, you will have one queue for orphan visits, one for regular visits, and one queue for every short URL with its visits.
|
||||
|
||||
The RabbitMQ server config can be provided via installer config options, or via environment variables.
|
||||
|
||||
* [#1204](https://github.com/shlinkio/shlink/issues/1204) Added support for `openswoole` and migrated official docker image to `openswoole`.
|
||||
* [#1242](https://github.com/shlinkio/shlink/issues/1242) Added support to import urls and visits from YOURLS.
|
||||
|
||||
In order to do it, you need to first install this [dedicated plugin](https://slnk.to/yourls-import) in YOURLS, and then run the `short-url:import yourls` command, as with any other source.
|
||||
|
||||
* [#1235](https://github.com/shlinkio/shlink/issues/1235) Added support to disable rounding QR codes block sizing via config option, env var or query param.
|
||||
* [#1188](https://github.com/shlinkio/shlink/issues/1188) Added support for PHP 8.1.
|
||||
|
||||
The official docker image has also been updated to use PHP 8.1 by default.
|
||||
|
||||
### Changed
|
||||
* [#844](https://github.com/shlinkio/shlink/issues/844) Added mutation checks to API tests.
|
||||
* [#1218](https://github.com/shlinkio/shlink/issues/1218) Updated to symfony/mercure 0.6.
|
||||
* [#1223](https://github.com/shlinkio/shlink/issues/1223) Updated to phpstan 1.0.
|
||||
* [#1258](https://github.com/shlinkio/shlink/issues/1258) Updated to Symfony 6 components, except symfony/console.
|
||||
* Added `domain` field to `DeleteShortUrlException` exception.
|
||||
|
||||
### Deprecated
|
||||
* [#1260](https://github.com/shlinkio/shlink/issues/1260) Deprecated `USE_HTTPS` env var that was added in previous release, in favor of the new `IS_HTTPS_ENABLED`.
|
||||
|
||||
The old one proved to be confusing and misleading, making people think it was used to actually enable HTTPS transparently, instead of its actual purpose, which is just telling Shlink it is being served with HTTPS.
|
||||
|
||||
### Removed
|
||||
* *Nothing*
|
||||
|
||||
### Fixed
|
||||
* [#1206](https://github.com/shlinkio/shlink/issues/1206) Fixed debugging of the docker image, so that it does not run the commands with `-q` when the `SHELL_VERBOSITY` env var has been provided.
|
||||
* [#1254](https://github.com/shlinkio/shlink/issues/1254) Fixed examples in swagger docs.
|
||||
|
||||
|
||||
## [2.9.3] - 2021-11-15
|
||||
### Added
|
||||
* *Nothing*
|
||||
|
||||
### Changed
|
||||
* *Nothing*
|
||||
|
||||
### Deprecated
|
||||
* *Nothing*
|
||||
|
||||
### Removed
|
||||
* *Nothing*
|
||||
|
||||
### Fixed
|
||||
* [#1232](https://github.com/shlinkio/shlink/issues/1232) Solved potential SQL injection by enforcing `doctrine/dbal` 3.1.4.
|
||||
|
||||
|
||||
## [2.9.2] - 2021-10-23
|
||||
### Added
|
||||
* *Nothing*
|
||||
|
||||
### Changed
|
||||
* *Nothing*
|
||||
|
||||
### Deprecated
|
||||
* *Nothing*
|
||||
|
||||
### Removed
|
||||
* *Nothing*
|
||||
|
||||
### Fixed
|
||||
* [#1210](https://github.com/shlinkio/shlink/issues/1210) Fixed real time updates not being notified due to an incorrect handling of db transactions on multi-process tasks.
|
||||
* [#1211](https://github.com/shlinkio/shlink/issues/1211) Fixed `There is no active transaction` error when running migrations in MySQL/Mariadb after updating to doctrine-migrations 3.3.
|
||||
* [#1197](https://github.com/shlinkio/shlink/issues/1197) Fixed amount of task workers provided via config option or env var not being validated to ensure enough workers to process all parallel tasks.
|
||||
|
||||
|
||||
## [2.9.1] - 2021-10-11
|
||||
### Added
|
||||
* *Nothing*
|
||||
|
||||
### Changed
|
||||
* *Nothing*
|
||||
|
||||
### Deprecated
|
||||
* *Nothing*
|
||||
|
||||
### Removed
|
||||
* *Nothing*
|
||||
|
||||
### Fixed
|
||||
* [#1201](https://github.com/shlinkio/shlink/issues/1201) Fixed crash when using the new `USE_HTTPS`, as it's boolean raw value was being used instead of resolving "https" or "http".
|
||||
|
||||
|
||||
## [2.9.0] - 2021-10-10
|
||||
### Added
|
||||
* [#1015](https://github.com/shlinkio/shlink/issues/1015) Shlink now accepts configuration via env vars even when not using docker.
|
||||
|
||||
The config generated with the installing tool still has precedence over the env vars, so it cannot be combined. Either you use the tool, or use env vars.
|
||||
|
||||
* [#1149](https://github.com/shlinkio/shlink/issues/1149) Allowed to set custom defaults for the QR codes.
|
||||
* [#1112](https://github.com/shlinkio/shlink/issues/1112) Added new option to define if the query string should be forwarded on a per-short URL basis.
|
||||
|
||||
The new `forwardQuery=true|false` param can be provided during short URL creation or edition, via REST API or CLI command, allowing to override the default behavior which makes the query string to always be forwarded.
|
||||
|
||||
* [#1105](https://github.com/shlinkio/shlink/issues/1105) Added support to define placeholders on not-found redirects, so that the redirected URL receives the originally visited path and/or domain.
|
||||
|
||||
Currently, `{DOMAIN}` and `{ORIGINAL_PATH}` placeholders are supported, and they can be used both in the redirected URL's path or query.
|
||||
|
||||
When they are used in the query, the values are URL encoded.
|
||||
|
||||
* [#1119](https://github.com/shlinkio/shlink/issues/1119) Added support to provide redis sentinel when using redis cache.
|
||||
* [#1016](https://github.com/shlinkio/shlink/issues/1016) Added new option to send orphan visits to webhooks, via `NOTIFY_ORPHAN_VISITS_TO_WEBHOOKS` env var or installer tool.
|
||||
|
||||
The option is disabled by default, as the payload is backwards incompatible. You will need to adapt your webhooks to treat the `shortUrl` property as optional before enabling this option.
|
||||
|
||||
* [#1104](https://github.com/shlinkio/shlink/issues/1104) Added ability to disable tracking based on IP addresses.
|
||||
|
||||
IP addresses can be provided in the form of fixed addresses, CIDR blocks, or wildcard patterns (192.168.*.*).
|
||||
|
||||
### Changed
|
||||
* [#1142](https://github.com/shlinkio/shlink/issues/1142) Replaced `doctrine/cache` package with `symfony/cache`.
|
||||
* [#1157](https://github.com/shlinkio/shlink/issues/1157) All routes now support CORS, not only rest ones.
|
||||
* [#1144](https://github.com/shlinkio/shlink/issues/1144) Added experimental builds under PHP 8.1.
|
||||
|
||||
### Deprecated
|
||||
* [#1164](https://github.com/shlinkio/shlink/issues/1164) Deprecated `SHORT_DOMAIN_HOST` and `SHORT_DOMAIN_SCHEMA` env vars. Use `DEFAULT_DOMAIN` and `USE_HTTPS=true|false` instead.
|
||||
|
||||
### Removed
|
||||
* *Nothing*
|
||||
|
||||
### Fixed
|
||||
* [#1165](https://github.com/shlinkio/shlink/issues/1165) Fixed warning displayed when trying to locate visits and there are none pending.
|
||||
* [#1172](https://github.com/shlinkio/shlink/pull/1172) Removed unneeded explicitly defined volumes in docker image.
|
||||
|
||||
|
||||
## [2.8.1] - 2021-08-15
|
||||
### Added
|
||||
* *Nothing*
|
||||
@@ -50,7 +553,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com), and this
|
||||
* [#1046](https://github.com/shlinkio/shlink/issues/1046) Dropped support for PHP 7.4.
|
||||
|
||||
### Fixed
|
||||
* *Nothing*
|
||||
* [#1098](https://github.com/shlinkio/shlink/issues/1098) Fixed errors when using Redis for caching, caused by some third party lib bug that was fixed on dependencies update.
|
||||
|
||||
|
||||
## [2.7.3] - 2021-08-02
|
||||
@@ -242,7 +745,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com), and this
|
||||
* *Nothing*
|
||||
|
||||
### Fixed
|
||||
* [#979](https://github.com/shlinkio/shlink/issues/979) Added missing `itemsPerPage` query param to swagger docs for short RULs list.
|
||||
* [#979](https://github.com/shlinkio/shlink/issues/979) Added missing `itemsPerPage` query param to swagger docs for short URLs list.
|
||||
* [#980](https://github.com/shlinkio/shlink/issues/980) Fixed value used for `Access-Control-Allow-Origin`, that could not work as expected when including an IP address.
|
||||
* [#947](https://github.com/shlinkio/shlink/issues/947) Fixed incorrect value returned in `Access-Control-Allow-Methods` header, which always contained all methods.
|
||||
|
||||
@@ -711,7 +1214,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com), and this
|
||||
* Preview generation feature completely removed.
|
||||
* Authentication against REST API using JWT is no longer supported.
|
||||
|
||||
See [UPGRADE](UPGRADE.md) doc in order to get details on how to migrate to this version.
|
||||
See [UPGRADE](UPGRADE.md#from-v1x-to-v2x) doc in order to get details on how to migrate to this version.
|
||||
|
||||
### Fixed
|
||||
* [#600](https://github.com/shlinkio/shlink/issues/600) Fixed health action so that it works with and without version in the path.
|
||||
@@ -890,7 +1393,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com), and this
|
||||
|
||||
Endpoints and commands which create short URLs support providing the `domain` now (via query param or CLI flag). If not provided, the short URLs will still be "attached" to the default domain.
|
||||
|
||||
Custom slugs can be created on multiple domains, allowing to share links like `https://doma.in/my-compaign` and `https://example.com/my-campaign`, under the same shlink instance.
|
||||
Custom slugs can be created on multiple domains, allowing to share links like `https://doma.in/my-campaign` and `https://example.com/my-campaign`, under the same shlink instance.
|
||||
|
||||
When resolving a short URL to redirect end users, the following rules are applied:
|
||||
|
||||
@@ -1140,7 +1643,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com), and this
|
||||
|
||||
### Fixed
|
||||
* [#309](https://github.com/shlinkio/shlink/issues/309) Added missing favicon to prevent 404 errors logged when an error page is loaded in a browser.
|
||||
* [#310](https://github.com/shlinkio/shlink/issues/310) Fixed execution context not being properly detected, making `CloseDbConnectionMiddlware` to be always piped. Now the check is not even made, which simplifies everything.
|
||||
* [#310](https://github.com/shlinkio/shlink/issues/310) Fixed execution context not being properly detected, making `CloseDbConnectionMiddleware` to be always piped. Now the check is not even made, which simplifies everything.
|
||||
|
||||
|
||||
## [1.15.0] - 2018-12-02
|
||||
@@ -1205,7 +1708,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com), and this
|
||||
|
||||
### Changed
|
||||
* [#241](https://github.com/shlinkio/shlink/issues/241) Fixed columns in `visit_locations` table, to be snake_case instead of camelCase.
|
||||
* [#228](https://github.com/shlinkio/shlink/issues/228) Updated how exceptions are serialized into logs, by using monlog's `PsrLogMessageProcessor`.
|
||||
* [#228](https://github.com/shlinkio/shlink/issues/228) Updated how exceptions are serialized into logs, by using monolog's `PsrLogMessageProcessor`.
|
||||
* [#225](https://github.com/shlinkio/shlink/issues/225) Performance and maintainability slightly improved by enforcing via code sniffer that all global namespace classes, functions and constants are explicitly imported.
|
||||
* [#196](https://github.com/shlinkio/shlink/issues/196) Reduced anemic model in entities, defining more expressive public APIs instead.
|
||||
* [#249](https://github.com/shlinkio/shlink/issues/249) Added [functional-php](https://github.com/lstrojny/functional-php) to ease collections handling.
|
||||
|
||||
@@ -31,7 +31,7 @@ Then you will have to follow these steps:
|
||||
* Run `./indocker bin/cli db:migrate` to get database migrations up to date.
|
||||
* Run `./indocker bin/cli api-key:generate` to get your first API key generated.
|
||||
|
||||
Once you finish this, you will have the project exposed in ports `8000` through nginx+php-fpm and `8080` through swoole.
|
||||
Once you finish this, you will have the project exposed in ports `8000` through nginx+php-fpm and `8080` through openswoole.
|
||||
|
||||
> Note: The `indocker` shell script is a helper tool used to run commands inside the main docker container.
|
||||
|
||||
@@ -46,9 +46,7 @@ This is a simplified version of the project structure:
|
||||
```
|
||||
shlink
|
||||
├── bin
|
||||
│ ├── cli
|
||||
│ ├── install
|
||||
│ └── update
|
||||
│ └── cli
|
||||
├── config
|
||||
│ ├── autoload
|
||||
│ ├── params
|
||||
@@ -75,12 +73,12 @@ shlink
|
||||
|
||||
The purposes of every folder are:
|
||||
|
||||
* `bin`: It contains the CLI tools. The `cli` one is the main entry point to run shlink from the command line, while `install` and `update` are helper tools used to install and update shlink when not using the docker image.
|
||||
* `bin`: It contains the CLI tools. The `cli` one is the main entry point to run shlink from the command line.
|
||||
* `config`: Contains application-wide configurations, which are later merged with the ones provided by every module.
|
||||
* `data`: Common runtime-generated git-ignored assets, like logs, caches, etc.
|
||||
* `docs`: Any project documentation is stored here, like API spec definitions or architectural decision records.
|
||||
* `module`: Contains a subfolder for every module in the project. Modules contain the source code, tests and configurations for every context in the project.
|
||||
* `public`: Few assets (like `favicon.ico` or `robots.txt`) and the web entry point are stored here. This web entry point is not used when serving the app with swoole.
|
||||
* `module`: Contains a sub-folder for every module in the project. Modules contain the source code, tests and configurations for every context in the project.
|
||||
* `public`: Few assets (like `favicon.ico` or `robots.txt`) and the web entry point are stored here. This web entry point is not used when serving the app with openswoole.
|
||||
|
||||
## Project tests
|
||||
|
||||
@@ -96,7 +94,7 @@ In order to ensure stability and no regressions are introduced while developing
|
||||
|
||||
The project provides some tooling to run them against any of the supported database engines.
|
||||
|
||||
* **API tests**: These are E2E tests that spin up an instance of the app with swoole, and test it from the outside by interacting with the REST API.
|
||||
* **API tests**: These are E2E tests that spin up an instance of the app with openswoole, and test it from the outside by interacting with the REST API.
|
||||
|
||||
These are the best tests to catch regressions, and to verify everything behaves as expected.
|
||||
|
||||
@@ -104,7 +102,9 @@ In order to ensure stability and no regressions are introduced while developing
|
||||
|
||||
Since the app instance is run on a process different from the one running the tests, when a test fails it might not be obvious why. To help debugging that, the app will dump all its logs inside `data/log/api-tests`, where you will find the `shlink.log` and `access.log` files.
|
||||
|
||||
* **CLI tests**: *TBD. Once included, its purpose will be the same as API tests, but running through the command line*
|
||||
* **CLI tests**: These are E2E tests too, but they test console commands instead of REST endpoints.
|
||||
|
||||
They use Maria DB as the database engine, and include the same fixtures as the API tests, that ensure the same data exists at the beginning of the execution.
|
||||
|
||||
Depending on the kind of contribution, maybe not all kinds of tests are needed, but the more you provide, the better.
|
||||
|
||||
@@ -121,15 +121,9 @@ Depending on the kind of contribution, maybe not all kinds of tests are needed,
|
||||
For example, `test:db:postgres`.
|
||||
|
||||
* Run `./indocker composer test:api` to run API E2E tests. For these, the Postgres database engine is used.
|
||||
* Run `./indocker composer test:cli` to run CLI E2E tests. For these, the Maria DB database engine is used.
|
||||
* Run `./indocker composer infect:test` to run both unit and database tests (over sqlite) and then apply mutations to them with [infection](https://infection.github.io/).
|
||||
* Run `./indocker composer ci` to run all previous commands together. This command is run during the project's continuous integration.
|
||||
* Run `./indocker composer ci:parallel` to do the same as in previous case, but parallelizing non-conflicting tasks as much as possible.
|
||||
|
||||
> Note: Due to some limitations in the tooling used by shlink, the testing databases need to exist beforehand, both for db and api tests (except sqlite).
|
||||
>
|
||||
> However, they just need to be created empty, with no tables. Also, once created, they are automatically reset before every new execution.
|
||||
>
|
||||
> The testing database is always called `shlink_test`. You can create it using the database client of your choice. [DBeaver](https://dbeaver.io/) is a good multi-platform desktop database client which supports all the engines supported by shlink.
|
||||
* Run `./indocker composer ci` to run all previous commands together, parallelizing non-conflicting tasks as much as possible.
|
||||
|
||||
## Pull request process
|
||||
|
||||
@@ -141,7 +135,7 @@ Once everything is clear, to provide a pull request to this project, you should
|
||||
|
||||
The base branch should always be `develop`, and the target branch for the pull request should also be `develop`.
|
||||
|
||||
Before your branch can be merged, all the checks described in [Running code checks](#running-code-checks) have to be passing. You can verify that manually by running `./indocker composer ci:parallel`, or wait for the build to be run automatically after the pull request is created.
|
||||
Before your branch can be merged, all the checks described in [Running code checks](#running-code-checks) have to be passing. You can verify that manually by running `./indocker composer ci`, or wait for the build to be run automatically after the pull request is created.
|
||||
|
||||
## Architectural Decision Records
|
||||
|
||||
|
||||
73
Dockerfile
73
Dockerfile
@@ -1,58 +1,53 @@
|
||||
FROM php:8.0.9-alpine3.14 as base
|
||||
FROM php:8.1.9-alpine3.16 as base
|
||||
|
||||
ARG SHLINK_VERSION=latest
|
||||
ENV SHLINK_VERSION ${SHLINK_VERSION}
|
||||
ENV SWOOLE_VERSION 4.7.0
|
||||
ENV PDO_SQLSRV_VERSION 5.9.0
|
||||
ENV MS_ODBC_SQL_VERSION 17.5.2.1
|
||||
ARG SHLINK_RUNTIME=openswoole
|
||||
ENV SHLINK_RUNTIME ${SHLINK_RUNTIME}
|
||||
ENV OPENSWOOLE_VERSION 4.11.1
|
||||
ENV PDO_SQLSRV_VERSION 5.10.1
|
||||
ENV MS_ODBC_SQL_VERSION 17.5.2.2
|
||||
ENV LC_ALL "C"
|
||||
|
||||
WORKDIR /etc/shlink
|
||||
|
||||
# Install required PHP extensions
|
||||
RUN \
|
||||
# Install mysql and calendar
|
||||
docker-php-ext-install -j"$(nproc)" pdo_mysql calendar && \
|
||||
# Install sqlite
|
||||
apk add --no-cache sqlite-libs sqlite-dev && \
|
||||
# Temp install dev dependencies needed to compile the extensions
|
||||
apk add --no-cache --virtual .dev-deps sqlite-dev postgresql-dev icu-dev libzip-dev zlib-dev libpng-dev && \
|
||||
docker-php-ext-install -j"$(nproc)" pdo_mysql pdo_pgsql intl calendar sockets bcmath zip gd && \
|
||||
apk add --no-cache sqlite-libs && \
|
||||
docker-php-ext-install -j"$(nproc)" pdo_sqlite && \
|
||||
# Install postgres
|
||||
apk add --no-cache postgresql-dev && \
|
||||
docker-php-ext-install -j"$(nproc)" pdo_pgsql && \
|
||||
# Install intl
|
||||
apk add --no-cache icu-dev && \
|
||||
docker-php-ext-install -j"$(nproc)" intl && \
|
||||
# Install zip and gd
|
||||
apk add --no-cache libzip-dev zlib-dev libpng-dev && \
|
||||
docker-php-ext-install -j"$(nproc)" zip gd && \
|
||||
# Install gmp
|
||||
apk add --no-cache gmp-dev && \
|
||||
docker-php-ext-install -j"$(nproc)" gmp
|
||||
# Remove temp dev extensions, and install prod equivalents that are required at runtime
|
||||
apk del .dev-deps && \
|
||||
apk add --no-cache postgresql icu libzip libpng
|
||||
|
||||
# Install sqlsrv driver
|
||||
RUN if [ $(uname -m) == "x86_64" ]; then \
|
||||
# Install openswoole and sqlsrv driver for x86_64 builds
|
||||
RUN apk add --no-cache --virtual .phpize-deps ${PHPIZE_DEPS} unixodbc-dev && \
|
||||
if [ "$SHLINK_RUNTIME" == 'openswoole' ]; then \
|
||||
pecl install openswoole-${OPENSWOOLE_VERSION} && \
|
||||
docker-php-ext-enable openswoole ; \
|
||||
fi; \
|
||||
if [ $(uname -m) == "x86_64" ]; then \
|
||||
wget https://download.microsoft.com/download/e/4/e/e4e67866-dffd-428c-aac7-8d28ddafb39b/msodbcsql17_${MS_ODBC_SQL_VERSION}-1_amd64.apk && \
|
||||
apk add --allow-untrusted msodbcsql17_${MS_ODBC_SQL_VERSION}-1_amd64.apk && \
|
||||
apk add --no-cache --virtual .phpize-deps ${PHPIZE_DEPS} unixodbc-dev && \
|
||||
apk add --no-cache --allow-untrusted msodbcsql17_${MS_ODBC_SQL_VERSION}-1_amd64.apk && \
|
||||
pecl install pdo_sqlsrv-${PDO_SQLSRV_VERSION} && \
|
||||
docker-php-ext-enable pdo_sqlsrv && \
|
||||
apk del .phpize-deps && \
|
||||
rm msodbcsql17_${MS_ODBC_SQL_VERSION}-1_amd64.apk ; \
|
||||
fi
|
||||
|
||||
# Install swoole
|
||||
RUN apk add --no-cache --virtual .phpize-deps ${PHPIZE_DEPS} && \
|
||||
pecl install swoole-${SWOOLE_VERSION} && \
|
||||
docker-php-ext-enable swoole && \
|
||||
fi; \
|
||||
apk del .phpize-deps
|
||||
|
||||
|
||||
# Install shlink
|
||||
FROM base as builder
|
||||
COPY . .
|
||||
COPY --from=composer:2 /usr/bin/composer ./composer.phar
|
||||
RUN apk add --no-cache git && \
|
||||
php composer.phar install --no-dev --optimize-autoloader --prefer-dist --no-progress --no-interaction && \
|
||||
php composer.phar install --no-dev --prefer-dist --optimize-autoloader --no-progress --no-interaction && \
|
||||
if [ "$SHLINK_RUNTIME" == 'openswoole' ]; then \
|
||||
php composer.phar remove spiral/roadrunner spiral/roadrunner-jobs --with-all-dependencies --update-no-dev --optimize-autoloader --no-progress --no-interactionc ; \
|
||||
elif [ $SHLINK_RUNTIME == 'rr' ]; then \
|
||||
php composer.phar remove mezzio/mezzio-swoole --with-all-dependencies --update-no-dev --optimize-autoloader --no-progress --no-interaction ; \
|
||||
fi; \
|
||||
php composer.phar clear-cache && \
|
||||
rm -r docker composer.* && \
|
||||
sed -i "s/%SHLINK_VERSION%/${SHLINK_VERSION}/g" config/autoload/app_options.global.php
|
||||
@@ -63,16 +58,14 @@ FROM base
|
||||
LABEL maintainer="Alejandro Celaya <alejandro@alejandrocelaya.com>"
|
||||
|
||||
COPY --from=builder /etc/shlink .
|
||||
RUN ln -s /etc/shlink/bin/cli /usr/local/bin/shlink
|
||||
RUN ln -s /etc/shlink/bin/cli /usr/local/bin/shlink && \
|
||||
if [ "$SHLINK_RUNTIME" == 'rr' ]; then \
|
||||
php ./vendor/bin/rr get --no-interaction --location bin/ && chmod +x bin/rr ; \
|
||||
fi;
|
||||
|
||||
# Expose default swoole port
|
||||
# Expose default port
|
||||
EXPOSE 8080
|
||||
|
||||
# Expose params config dir, since the user is expected to provide custom config from there
|
||||
VOLUME /etc/shlink/config/params
|
||||
# Expose data dir to allow persistent runtime data and SQLite db
|
||||
VOLUME /etc/shlink/data
|
||||
|
||||
# Copy config specific for the image
|
||||
COPY docker/docker-entrypoint.sh docker-entrypoint.sh
|
||||
COPY docker/config/shlink_in_docker.local.php config/autoload/shlink_in_docker.local.php
|
||||
|
||||
40
README.md
40
README.md
@@ -2,18 +2,20 @@
|
||||
|
||||
[](https://github.com/shlinkio/shlink/actions?query=workflow%3A%22Continuous+integration%22)
|
||||
[](https://app.codecov.io/gh/shlinkio/shlink)
|
||||
[](https://dashboard.stryker-mutator.io/reports/github.com/shlinkio/shlink/develop)
|
||||
[](https://packagist.org/packages/shlinkio/shlink)
|
||||
[](https://hub.docker.com/r/shlinkio/shlink/)
|
||||
[](https://github.com/shlinkio/shlink/blob/main/LICENSE)
|
||||
[](https://twitter.com/shlinkio)
|
||||
[](https://slnk.to/donate)
|
||||
|
||||
A PHP-based self-hosted URL shortener that can be used to serve shortened URLs under your own custom domain.
|
||||
A PHP-based self-hosted URL shortener that can be used to serve shortened URLs under your own domain.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
- [Full documentation](#full-documentation)
|
||||
- [Docker image](#docker-image)
|
||||
- [Self hosted](#self-hosted)
|
||||
- [Self-hosted](#self-hosted)
|
||||
- [Download](#download)
|
||||
- [Configure](#configure)
|
||||
- [Using shlink](#using-shlink)
|
||||
@@ -25,7 +27,7 @@ This document contains the very basics to get started with Shlink. If you want t
|
||||
|
||||
## Docker image
|
||||
|
||||
Starting with version 1.15.0, an official docker image is provided. You can learn how to use it by reading [the docs](https://shlink.io/documentation/install-docker-image/).
|
||||
You can learn how to use the official docker image by reading [the docs](https://shlink.io/documentation/install-docker-image/).
|
||||
|
||||
The idea is that you can just generate a container using the image and provide the custom config via env vars.
|
||||
|
||||
@@ -33,12 +35,14 @@ The idea is that you can just generate a container using the image and provide t
|
||||
|
||||
First, make sure the host where you are going to run shlink fulfills these requirements:
|
||||
|
||||
* PHP 8.0
|
||||
* The next PHP extensions: json, curl, pdo, intl, gd and gmp.
|
||||
* apcu extension is recommended if you don't plan to use swoole.
|
||||
* PHP 8.1
|
||||
* The next PHP extensions: json, curl, pdo, intl, gd and gmp/bcmath.
|
||||
* apcu extension is recommended if you don't plan to use openswoole.
|
||||
* xml extension is required if you want to generate QR codes in svg format.
|
||||
* MySQL, MariaDB, PostgreSQL, Microsoft SQL Server or SQLite.
|
||||
* The web server of your choice with PHP integration (Apache or Nginx recommended).
|
||||
* sockets and bcmath extensions are required if you want to integrate with a RabbitMQ instance.
|
||||
* MySQL, MariaDB, PostgreSQL, MicrosoftSQL or SQLite.
|
||||
* You will also need the corresponding pdo variation for the database you are planning to use: `pdo_mysql`, `pdo_pgsql`, `pdo_sqlsrv` or `pdo_sqlite`.
|
||||
* The [openswoole](https://openswoole.com/) PHP extension (if you plan to serve Shlink with openswoole) or the web server of your choice with PHP integration (like Apache or Nginx).
|
||||
|
||||
### Download
|
||||
|
||||
@@ -48,7 +52,7 @@ In order to run Shlink, you will need a built version of the project. There are
|
||||
|
||||
The easiest way to install shlink is by using one of the pre-bundled distributable packages.
|
||||
|
||||
Go to the [latest version](https://github.com/shlinkio/shlink/releases/latest) and download the `shlink*_dist.zip` file that suits your needs. You will find one for every supported PHP version and with/without swoole integration.
|
||||
Go to the [latest version](https://github.com/shlinkio/shlink/releases/latest) and download the `shlink*_dist.zip` file that suits your needs. You will find one for every supported PHP version and with/without openswoole integration.
|
||||
|
||||
Finally, decompress the file in the location of your choice.
|
||||
|
||||
@@ -58,11 +62,13 @@ In order to run Shlink, you will need a built version of the project. There are
|
||||
|
||||
* Clone the project with git (`git clone https://github.com/shlinkio/shlink.git`), or download it by clicking the **Clone or download** green button.
|
||||
* Download the [Composer](https://getcomposer.org/download/) PHP package manager inside the project folder.
|
||||
* Run `./build.sh 1.0.0`, replacing the version with the version number you are going to build (the version number is used as part of the generated dist file name, and to set the value returned when running `shlink -V` from the command line).
|
||||
* Run `./build.sh 3.0.0`, replacing the version with the version number you are going to build (the version number is used as part of the generated dist file name, and to set the value returned when running `shlink -V` from the command line).
|
||||
|
||||
After that, you will have a dist file inside the `build` directory, that you need to decompress in the location of your choice.
|
||||
|
||||
> This is the process used when releasing new shlink versions. After tagging the new version with git, the Github release is automatically created by a [GitHub workflow](https://github.com/shlinkio/shlink/actions?query=workflow%3A%22Publish+release%22), attaching the generated dist file to it.
|
||||
> **Note**
|
||||
>
|
||||
> This is the process used when releasing new Shlink versions. After tagging the new version with git, the GitHub release is automatically created by a [GitHub workflow](https://github.com/shlinkio/shlink/actions?query=workflow%3A%22Publish+release%22), attaching the generated dist file to it.
|
||||
|
||||
### Configure
|
||||
|
||||
@@ -70,24 +76,24 @@ Despite how you built the project, you now need to configure it, by following th
|
||||
|
||||
* If you are going to use MySQL, MariaDB, PostgreSQL or Microsoft SQL Server, create an empty database with the name of your choice.
|
||||
* Recursively grant write permissions to the `data` directory. Shlink uses it to cache some information.
|
||||
* Setup the application by running the `bin/install` script. It is a command line tool that will guide you through the installation process. **Take into account that this tool has to be run directly on the server where you plan to host Shlink. Do not run it before uploading/moving it there.**
|
||||
* Generate your first API key by running `bin/cli api-key:generate`. You will need the key in order to interact with shlink's API.
|
||||
* Set up the application by running the `vendor/bin/shlink-installer install` script. It is a command line tool that will guide you through the installation process. **Take into account that this tool has to be run directly on the server where you plan to host Shlink. Do not run it before uploading/moving it there.**
|
||||
* Generate your first API key by running `bin/cli api-key:generate`. You will need the key in order to interact with Shlink's API.
|
||||
|
||||
## Using shlink
|
||||
|
||||
Once shlink is installed, there are two main ways to interact with it:
|
||||
|
||||
* **The command line**. Try running `bin/cli` and see all the [available commands](#shlink-cli-help).
|
||||
* **The command line**: Try running `bin/cli` to see all the available commands.
|
||||
|
||||
All of those commands can be run with the `--help`/`-h` flag in order to see how to use them and all the available options.
|
||||
All of them can be run with the `--help`/`-h` flag in order to see how to use them and all the available options.
|
||||
|
||||
It is probably a good idea to symlink the CLI entry point (`bin/cli`) to somewhere in your path, so that you can run shlink from any directory.
|
||||
|
||||
* **The REST API**. The complete docs on how to use the API can be found [here](https://shlink.io/documentation/api-docs), and a sandbox which also documents every endpoint can be found in the [API Spec](https://api-spec.shlink.io/) portal.
|
||||
* **The REST API**: The complete docs on how to use the API can be found [here](https://shlink.io/documentation/api-docs), and a sandbox which also documents every endpoint can be found in the [API Spec](https://api-spec.shlink.io/) portal.
|
||||
|
||||
However, you probably don't want to consume the raw API yourself. That's why a nice [web client](https://github.com/shlinkio/shlink-web-client) is provided that can be directly used from [https://app.shlink.io](https://app.shlink.io), or hosted by yourself.
|
||||
|
||||
Both the API and CLI allow you to do the same operations, except for API key management, which can be done from the command line interface only.
|
||||
Both the API and CLI allow you to do mostly the same operations, except for API key management, which can be done from the command line interface only.
|
||||
|
||||
## Contributing
|
||||
|
||||
|
||||
50
UPGRADE.md
50
UPGRADE.md
@@ -1,5 +1,53 @@
|
||||
# Upgrading
|
||||
|
||||
## From v2.x to v3.x
|
||||
|
||||
### Changes in REST API
|
||||
|
||||
* The `type` property returned when trying to delete a URL that reached the visits threshold, when using the `DELETE /short-urls/{shortCode}` endpoint, is now `INVALID_SHORT_URL_DELETION` instead of `INVALID_SHORTCODE_DELETION`.
|
||||
* The `INVALID_AUTHORIZATION` error no longer includes the `expectedTypes` property. Use `expectedHeaders` one instead.
|
||||
* The `GET /rest/v2/short-urls` endpoint no longer allows ordering by `visitsCount`, `visitCount` or `originalUrl`. Use `visits` instead of the first two, and `longUrl` instead of the last one.
|
||||
* The `GET /rest/v2/short-urls` endpoint no longer allows providing the ordering params with array notation, as in `/shortUrls?orderBy[longUrl]=DESC`. Instead, use the following notation `/shortUrls?orderBy=longUrl-DESC`.
|
||||
* The `GET /rest/v2/short-urls` endpoint now has a default ordering of newest-to-oldest. Use `/shortUrls?orderBy=dateCreated-ASC` in order to keep the oldest-to-newest behavior.
|
||||
* Requests expecting a body no longer support url-encoded payloads. Instead, always use JSON bodies with `Content-Type: application/json`.
|
||||
* The next endpoints have been removed:
|
||||
* `PUT /rest/v2/short-urls/{shortCode}/tags`: Use the `PATCH /rest/v2/short-urls/{shortCode}` endpoint to set the short URL tags.
|
||||
* `POST /rest/v2/tags`: Use `POST /rest/v2/short-urls` or `PATCH /rest/v2/short-urls/{shortCodes}` to create new tags already attached to a short URL. Creating orphan tags makes no sense.
|
||||
|
||||
### Changes in CLI
|
||||
|
||||
* The next commands have been removed:
|
||||
* `short-url:generate`: Use `short-url:create` instead.
|
||||
* `tag:create`: Creating orphan tags makes no sense.
|
||||
* Params in camelCase format are no longer supported. They all have an equivalent kebab-case replacement. (for example, from `--startDate` to `--start-date`).
|
||||
* The `short-url:create` command no longer accepts the `--no-validate-url` flag. Now URLs are never validated, unless `--validate-url` is passed.
|
||||
* The CLI installer tool entry-points have changed.
|
||||
* `bin/install`: replaced by `vendor/bin/shlink-installer install`
|
||||
* `bin/update`: replaced by `vendor/bin/shlink-installer update`
|
||||
* `bin/set-option`: replaced by `vendor/bin/shlink-installer set-option`
|
||||
|
||||
### Changes in config
|
||||
|
||||
* The next env vars have been removed:
|
||||
* `INVALID_SHORT_URL_REDIRECT_TO`: Replaced by `DEFAULT_INVALID_SHORT_URL_REDIRECT`.
|
||||
* `REGULAR_404_REDIRECT_TO`: Replaced by `DEFAULT_REGULAR_404_REDIRECT`.
|
||||
* `BASE_URL_REDIRECT_TO`: Replaced by `DEFAULT_BASE_URL_REDIRECT`.
|
||||
* `SHORT_DOMAIN_HOST`: Replaced by `DEFAULT_DOMAIN`.
|
||||
* `SHORT_DOMAIN_SCHEMA`: Replaced by `IS_HTTPS_ENABLED`.
|
||||
* `USE_HTTPS`: Replaced by `IS_HTTPS_ENABLED`.
|
||||
* `VALIDATE_URLS`: There's no replacement. URLs are not validated, unless explicitly requested during creation or edition.
|
||||
* The next env vars behavior has changed:
|
||||
* `DELETE_SHORT_URL_THRESHOLD`: Now, if this env var is not provided, the "visits threshold" won't be checked at all when deleting short URLs. Make sure you explicitly provide a value if you want to enable this feature.
|
||||
* Environment variables now have precedence over configuration set via the installer tool.
|
||||
|
||||
### Other changes
|
||||
|
||||
* A default GeoLite2 license key is no longer provided. If you don't provide your own as explained in [the docs](https://shlink.io/documentation/geolite-license-key/), Shlink will not try to update the file anymore.
|
||||
* The docker image no longer accepts providing configuration via json files mounted in the `config/params` folder. Only env vars are supported now.
|
||||
* If you were manually serving Shlink with swoole, the entry script has to be changed from `/path/to/shlink/vendor/bin/mezzio-swoole start` to `/path/to/shlink/vendor/bin/laminas mezzio:swoole:start`
|
||||
* The `GET /{shortCode}/qr-code/{size}` url has been removed. Use `GET /{shortCode}/qr-code?size={size}` instead.
|
||||
* Regular swoole extension is no longer supported. Use openswoole instead, as a direct replacement. In most of the cases you just need to uninstall one and install the other, the rest is transparent.
|
||||
|
||||
## From v1.x to v2.x
|
||||
|
||||
### PHP 7.4 required
|
||||
@@ -28,7 +76,7 @@ These routes have been removed, but have a direct replacement:
|
||||
* `/qr/{shortCode}[/{size}]` -> `/{shortCode}/qr-code[/{size}]`
|
||||
* `PUT /rest/v{version}/short-urls/{shortCode}` -> `PATCH /rest/v{version}/short-urls/{shortCode}`
|
||||
|
||||
When using the old ones, a 404 status will me returned now.
|
||||
When using the old ones, a 404 status will be returned now.
|
||||
|
||||
### Removed command and route aliases
|
||||
|
||||
|
||||
12
bin/doctrine
Executable file
12
bin/doctrine
Executable file
@@ -0,0 +1,12 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Doctrine\ORM\EntityManager;
|
||||
use Doctrine\ORM\Tools\Console\ConsoleRunner;
|
||||
use Doctrine\ORM\Tools\Console\EntityManagerProvider\SingleManagerProvider;
|
||||
|
||||
/** @var EntityManager $app */
|
||||
$em = require __DIR__ . '/../config/entity-manager.php';
|
||||
ConsoleRunner::run(new SingleManagerProvider($em));
|
||||
@@ -1,51 +0,0 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @deprecated To be removed with Shlink 3.0.0
|
||||
* This script is provided to keep backwards compatibility on how to run shlink with swoole while being still able to
|
||||
* update to mezzio/mezzio-swoole 3.x
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Mezzio\Swoole\Command;
|
||||
|
||||
use Laminas\ServiceManager\ServiceManager;
|
||||
use PackageVersions\Versions;
|
||||
use Symfony\Component\Console\Application as CommandLine;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\CommandLoader\ContainerCommandLoader;
|
||||
|
||||
use function explode;
|
||||
use function Functional\filter;
|
||||
use function str_starts_with;
|
||||
use function strstr;
|
||||
|
||||
/** @var ServiceManager $container */
|
||||
$container = require __DIR__ . '/../../config/container.php';
|
||||
$version = strstr(Versions::getVersion('mezzio/mezzio-swoole'), '@', true);
|
||||
$commandsPrefix = 'mezzio:swoole:';
|
||||
$commands = filter(
|
||||
$container->get('config')['laminas-cli']['commands'] ?? [],
|
||||
fn ($c, string $command) => str_starts_with($command, $commandsPrefix),
|
||||
);
|
||||
$registeredCommands = [];
|
||||
|
||||
foreach ($commands as $newName => $commandServiceName) {
|
||||
[, $oldName] = explode($commandsPrefix, $newName);
|
||||
$registeredCommands[$oldName] = $commandServiceName;
|
||||
|
||||
$container->addDelegator($commandServiceName, static function ($c, $n, callable $factory) use ($oldName) {
|
||||
/** @var Command $command */
|
||||
$command = $factory();
|
||||
$command->setAliases([$oldName]);
|
||||
|
||||
return $command;
|
||||
});
|
||||
}
|
||||
|
||||
$commandLine = new CommandLine('Mezzio web server', $version);
|
||||
$commandLine->setAutoExit(true);
|
||||
$commandLine->setCommandLoader(new ContainerCommandLoader($container, $registeredCommands));
|
||||
$commandLine->run();
|
||||
12
bin/install
12
bin/install
@@ -1,12 +0,0 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Shlinkio\Shlink;
|
||||
|
||||
use function chdir;
|
||||
use function dirname;
|
||||
|
||||
chdir(dirname(__DIR__));
|
||||
[$install] = require __DIR__ . '/../vendor/shlinkio/shlink-installer/bin/run.php';
|
||||
$install();
|
||||
32
bin/roadrunner-worker.php
Normal file
32
bin/roadrunner-worker.php
Normal file
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Mezzio\Application;
|
||||
use Psr\Container\ContainerInterface;
|
||||
use Shlinkio\Shlink\EventDispatcher\RoadRunner\RoadRunnerTaskConsumerToListener;
|
||||
use Spiral\RoadRunner\Http\PSR7Worker;
|
||||
|
||||
use function Shlinkio\Shlink\Config\env;
|
||||
|
||||
(static function (): void {
|
||||
/** @var ContainerInterface $container */
|
||||
$container = include __DIR__ . '/../config/container.php';
|
||||
$rrMode = env('RR_MODE');
|
||||
|
||||
if ($rrMode === 'http') {
|
||||
// This was spin-up as a web worker
|
||||
$app = $container->get(Application::class);
|
||||
$worker = $container->get(PSR7Worker::class);
|
||||
|
||||
while ($req = $worker->waitRequest()) {
|
||||
try {
|
||||
$worker->respond($app->handle($req));
|
||||
} catch (Throwable $e) {
|
||||
$worker->getWorker()->error((string) $e);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$container->get(RoadRunnerTaskConsumerToListener::class)->listenForTasks();
|
||||
}
|
||||
})();
|
||||
@@ -1,14 +0,0 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Shlinkio\Shlink;
|
||||
|
||||
use Shlinkio\Shlink\Installer\Command\SetOptionCommand;
|
||||
|
||||
use function chdir;
|
||||
use function dirname;
|
||||
|
||||
chdir(dirname(__DIR__));
|
||||
[,, $installer] = require __DIR__ . '/../vendor/shlinkio/shlink-installer/bin/run.php';
|
||||
$installer(SetOptionCommand::NAME);
|
||||
@@ -1,21 +1,38 @@
|
||||
#!/usr/bin/env sh
|
||||
export APP_ENV=test
|
||||
export DB_DRIVER=postgres
|
||||
export TEST_ENV=api
|
||||
|
||||
export APP_ENV=test
|
||||
export TEST_ENV=api
|
||||
export TEST_RUNTIME="${TEST_RUNTIME:-"openswoole"}"
|
||||
export DB_DRIVER="${DB_DRIVER:-"postgres"}"
|
||||
export GENERATE_COVERAGE="${GENERATE_COVERAGE:-"no"}"
|
||||
|
||||
# Reset logs
|
||||
OUTPUT_LOGS=data/log/api-tests/output.log
|
||||
rm -rf data/log/api-tests
|
||||
mkdir data/log/api-tests
|
||||
touch $OUTPUT_LOGS
|
||||
|
||||
# Try to stop server just in case it hanged in last execution
|
||||
vendor/bin/laminas mezzio:swoole:stop
|
||||
[ "$TEST_RUNTIME" = 'openswoole' ] && vendor/bin/laminas mezzio:swoole:stop
|
||||
[ "$TEST_RUNTIME" = 'rr' ] && bin/rr stop -f
|
||||
|
||||
echo 'Starting server...'
|
||||
vendor/bin/laminas mezzio:swoole:start -d
|
||||
sleep 2
|
||||
[ "$TEST_RUNTIME" = 'openswoole' ] && vendor/bin/laminas mezzio:swoole:start -d
|
||||
[ "$TEST_RUNTIME" = 'rr' ] && bin/rr serve -p -c=config/roadrunner/.rr.dev.yml \
|
||||
-o=http.address=0.0.0.0:9999 \
|
||||
-o=logs.encoding=json \
|
||||
-o=logs.channels.http.encoding=json \
|
||||
-o=logs.channels.server.encoding=json \
|
||||
-o=logs.output="${PWD}/${OUTPUT_LOGS}" \
|
||||
-o=logs.channels.http.output="${PWD}/${OUTPUT_LOGS}" \
|
||||
-o=logs.channels.server.output="${PWD}/${OUTPUT_LOGS}" &
|
||||
sleep 2 # Let's give the server a couple of seconds to start
|
||||
|
||||
vendor/bin/phpunit --order-by=random -c phpunit-api.xml --testdox --colors=always --log-junit=build/coverage-api/junit.xml $*
|
||||
testsExitCode=$?
|
||||
|
||||
vendor/bin/laminas mezzio:swoole:stop
|
||||
[ "$TEST_RUNTIME" = 'openswoole' ] && vendor/bin/laminas mezzio:swoole:stop
|
||||
[ "$TEST_RUNTIME" = 'rr' ] && bin/rr stop -c config/roadrunner/.rr.dev.yml -o=http.address=0.0.0.0:9999
|
||||
|
||||
# Exit this script with the same code as the tests. If tests failed, this script has to fail
|
||||
exit $testsExitCode
|
||||
|
||||
12
bin/update
12
bin/update
@@ -1,12 +0,0 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Shlinkio\Shlink;
|
||||
|
||||
use function chdir;
|
||||
use function dirname;
|
||||
|
||||
chdir(dirname(__DIR__));
|
||||
[, $update] = require __DIR__ . '/../vendor/shlinkio/shlink-installer/bin/run.php';
|
||||
$update();
|
||||
9
build.sh
9
build.sh
@@ -10,7 +10,7 @@ fi
|
||||
version=$1
|
||||
noSwoole=$2
|
||||
phpVersion=$(php -r 'echo PHP_MAJOR_VERSION . "." . PHP_MINOR_VERSION;')
|
||||
[[ $noSwoole ]] && swooleSuffix="" || swooleSuffix="_swoole"
|
||||
[[ $noSwoole ]] && swooleSuffix="" || swooleSuffix="_openswoole"
|
||||
distId="shlink${version}_php${phpVersion}${swooleSuffix}_dist"
|
||||
builtContent="./build/${distId}"
|
||||
projectdir=$(pwd)
|
||||
@@ -24,6 +24,7 @@ rsync -av * "${builtContent}" \
|
||||
--exclude=*docker* \
|
||||
--exclude=Dockerfile \
|
||||
--include=.htaccess \
|
||||
--include=config/roadrunner/.rr.yml \
|
||||
--exclude-from=./.dockerignore
|
||||
cd "${builtContent}"
|
||||
|
||||
@@ -34,11 +35,11 @@ ${composerBin} self-update
|
||||
${composerBin} install --no-dev --prefer-dist $composerFlags
|
||||
|
||||
if [[ $noSwoole ]]; then
|
||||
# If generating a dist not for swoole, uninstall mezzio-swoole
|
||||
# If generating a dist not for openswoole, uninstall mezzio-swoole
|
||||
${composerBin} remove mezzio/mezzio-swoole --with-all-dependencies --update-no-dev $composerFlags
|
||||
else
|
||||
# Copy mezzio helper script to vendor (deprecated - Remove with Shlink 3.0.0)
|
||||
cp "${projectdir}/bin/helper/mezzio-swoole" "./vendor/bin"
|
||||
# If generating a dist for openswoole, uninstall RoadRunner
|
||||
${composerBin} remove spiral/roadrunner spiral/roadrunner-jobs --with-all-dependencies --update-no-dev $composerFlags
|
||||
fi
|
||||
|
||||
# Delete development files
|
||||
|
||||
192
composer.json
192
composer.json
@@ -12,70 +12,70 @@
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": "^8.0",
|
||||
"php": "^8.1",
|
||||
"ext-curl": "*",
|
||||
"ext-gd": "*",
|
||||
"ext-json": "*",
|
||||
"ext-pdo": "*",
|
||||
"akrabat/ip-address-middleware": "^2.0",
|
||||
"cakephp/chronos": "^2.2",
|
||||
"cocur/slugify": "^4.0",
|
||||
"doctrine/cache": "^1.12",
|
||||
"doctrine/migrations": "^3.2",
|
||||
"doctrine/orm": "^2.9",
|
||||
"endroid/qr-code": "^4.2",
|
||||
"geoip2/geoip2": "^2.11",
|
||||
"guzzlehttp/guzzle": "^7.3",
|
||||
"akrabat/ip-address-middleware": "^2.1",
|
||||
"cakephp/chronos": "^2.3",
|
||||
"doctrine/migrations": "^3.5",
|
||||
"doctrine/orm": "^2.13.3",
|
||||
"endroid/qr-code": "^4.4",
|
||||
"geoip2/geoip2": "^2.12",
|
||||
"guzzlehttp/guzzle": "^7.4",
|
||||
"happyr/doctrine-specification": "^2.0",
|
||||
"jaybizzle/crawler-detect": "^1.2",
|
||||
"laminas/laminas-config": "^3.5",
|
||||
"laminas/laminas-config-aggregator": "^1.5",
|
||||
"laminas/laminas-diactoros": "^2.6",
|
||||
"laminas/laminas-inputfilter": "^2.12",
|
||||
"laminas/laminas-servicemanager": "^3.7",
|
||||
"laminas/laminas-stdlib": "^3.5",
|
||||
"jaybizzle/crawler-detect": "^1.2.110",
|
||||
"laminas/laminas-config": "^3.7",
|
||||
"laminas/laminas-config-aggregator": "^1.8",
|
||||
"laminas/laminas-diactoros": "^2.14",
|
||||
"laminas/laminas-inputfilter": "^2.19",
|
||||
"laminas/laminas-servicemanager": "^3.16",
|
||||
"laminas/laminas-stdlib": "^3.11",
|
||||
"lcobucci/jwt": "^4.1",
|
||||
"league/uri": "^6.4",
|
||||
"league/uri": "^6.7",
|
||||
"lstrojny/functional-php": "^1.17",
|
||||
"mezzio/mezzio": "^3.5",
|
||||
"mezzio/mezzio-fastroute": "^3.2",
|
||||
"mezzio/mezzio-problem-details": "^1.4",
|
||||
"mezzio/mezzio-swoole": "^3.3",
|
||||
"monolog/monolog": "^2.3",
|
||||
"nikolaposa/monolog-factory": "^3.1",
|
||||
"ocramius/proxy-manager": "^2.11",
|
||||
"pagerfanta/core": "^2.7",
|
||||
"mezzio/mezzio": "^3.11",
|
||||
"mezzio/mezzio-fastroute": "^3.5",
|
||||
"mezzio/mezzio-problem-details": "^1.6",
|
||||
"mezzio/mezzio-swoole": "^4.3",
|
||||
"mlocati/ip-lib": "^1.18",
|
||||
"ocramius/proxy-manager": "^2.14",
|
||||
"pagerfanta/core": "^3.6",
|
||||
"php-middleware/request-id": "^4.1",
|
||||
"predis/predis": "^1.1",
|
||||
"pugx/shortid-php": "^0.7",
|
||||
"ramsey/uuid": "^3.9",
|
||||
"shlinkio/shlink-common": "^3.7",
|
||||
"shlinkio/shlink-config": "^1.2",
|
||||
"shlinkio/shlink-event-dispatcher": "^2.1",
|
||||
"shlinkio/shlink-importer": "^2.3.1",
|
||||
"shlinkio/shlink-installer": "^6.1",
|
||||
"shlinkio/shlink-ip-geolocation": "^2.0",
|
||||
"symfony/console": "^5.3",
|
||||
"symfony/filesystem": "^5.3",
|
||||
"symfony/lock": "^5.3",
|
||||
"symfony/mercure": "^0.5.3",
|
||||
"symfony/process": "^5.3",
|
||||
"symfony/string": "^5.3"
|
||||
"pugx/shortid-php": "^1.0",
|
||||
"ramsey/uuid": "^4.3",
|
||||
"shlinkio/shlink-common": "^5.1",
|
||||
"shlinkio/shlink-config": "^2.1",
|
||||
"shlinkio/shlink-event-dispatcher": "^2.6",
|
||||
"shlinkio/shlink-importer": "^4.0",
|
||||
"shlinkio/shlink-installer": "^8.2",
|
||||
"shlinkio/shlink-ip-geolocation": "^3.1",
|
||||
"spiral/roadrunner": "^2.11",
|
||||
"spiral/roadrunner-jobs": "^2.3",
|
||||
"symfony/console": "^6.1",
|
||||
"symfony/filesystem": "^6.1",
|
||||
"symfony/lock": "^6.1",
|
||||
"symfony/process": "^6.1",
|
||||
"symfony/string": "^6.1"
|
||||
},
|
||||
"require-dev": {
|
||||
"cebe/php-openapi": "^1.7",
|
||||
"devster/ubench": "^2.1",
|
||||
"dms/phpunit-arraysubset-asserts": "^0.3.0",
|
||||
"eaglewu/swoole-ide-helper": "dev-master",
|
||||
"infection/infection": "^0.24.0",
|
||||
"dms/phpunit-arraysubset-asserts": "^0.4.0",
|
||||
"infection/infection": "^0.26.15",
|
||||
"openswoole/ide-helper": "~4.11.1",
|
||||
"phpspec/prophecy-phpunit": "^2.0",
|
||||
"phpstan/phpstan": "^0.12.94",
|
||||
"phpstan/phpstan-doctrine": "^0.12.42",
|
||||
"phpstan/phpstan-symfony": "^0.12.41",
|
||||
"phpstan/phpstan": "^1.8",
|
||||
"phpstan/phpstan-doctrine": "^1.3",
|
||||
"phpstan/phpstan-symfony": "^1.2",
|
||||
"phpunit/php-code-coverage": "^9.2",
|
||||
"phpunit/phpunit": "^9.5",
|
||||
"roave/security-advisories": "dev-master",
|
||||
"shlinkio/php-coding-standard": "~2.1.1",
|
||||
"shlinkio/shlink-test-utils": "^2.2",
|
||||
"symfony/var-dumper": "^5.3",
|
||||
"veewee/composer-run-parallel": "^1.0"
|
||||
"shlinkio/php-coding-standard": "~2.3.0",
|
||||
"shlinkio/shlink-test-utils": "^3.3",
|
||||
"symfony/var-dumper": "^6.1",
|
||||
"veewee/composer-run-parallel": "^1.1"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
@@ -84,18 +84,19 @@
|
||||
"Shlinkio\\Shlink\\Core\\": "module/Core/src"
|
||||
},
|
||||
"files": [
|
||||
"config/constants.php",
|
||||
"module/Core/functions/functions.php"
|
||||
]
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"ShlinkioTest\\Shlink\\CLI\\": "module/CLI/test",
|
||||
"ShlinkioCliTest\\Shlink\\CLI\\": "module/CLI/test-cli",
|
||||
"ShlinkioTest\\Shlink\\Rest\\": "module/Rest/test",
|
||||
"ShlinkioApiTest\\Shlink\\Rest\\": "module/Rest/test-api",
|
||||
"ShlinkioTest\\Shlink\\Core\\": [
|
||||
"module/Core/test",
|
||||
"module/Core/test-db"
|
||||
]
|
||||
"ShlinkioDbTest\\Shlink\\Rest\\": "module/Rest/test-db",
|
||||
"ShlinkioTest\\Shlink\\Core\\": "module/Core/test",
|
||||
"ShlinkioDbTest\\Shlink\\Core\\": "module/Core/test-db"
|
||||
},
|
||||
"files": [
|
||||
"config/test/constants.php"
|
||||
@@ -103,31 +104,19 @@
|
||||
},
|
||||
"scripts": {
|
||||
"ci": [
|
||||
"@cs",
|
||||
"@stan",
|
||||
"@test:ci",
|
||||
"@infect:ci"
|
||||
],
|
||||
"ci:parallel": [
|
||||
"@parallel cs stan test:unit:ci test:db:sqlite:ci test:db:mysql test:db:maria test:db:postgres test:db:ms",
|
||||
"@parallel test:api infect:ci:unit infect:ci:db"
|
||||
"@parallel cs stan swagger:validate test:unit:ci test:db:sqlite:ci test:db:mysql test:db:maria test:db:postgres test:db:ms",
|
||||
"@parallel infect:test:api infect:test:cli infect:ci:unit infect:ci:db"
|
||||
],
|
||||
"cs": "phpcs",
|
||||
"cs:fix": "phpcbf",
|
||||
"stan": "APP_ENV=test php vendor/bin/phpstan analyse module/*/src module/*/config config docker/config data/migrations --level=8",
|
||||
"test": [
|
||||
"@test:unit",
|
||||
"@test:db",
|
||||
"@test:api"
|
||||
"@parallel test:unit test:db",
|
||||
"@parallel test:api test:cli"
|
||||
],
|
||||
"test:ci": [
|
||||
"@test:unit:ci",
|
||||
"@test:db",
|
||||
"@test:api"
|
||||
],
|
||||
"test:unit": "@php vendor/bin/phpunit --order-by=random --colors=always --coverage-php build/coverage-unit.cov --testdox",
|
||||
"test:unit:ci": "@test:unit --coverage-xml=build/coverage-unit/coverage-xml --log-junit=build/coverage-unit/junit.xml",
|
||||
"test:unit:pretty": "@php vendor/bin/phpunit --order-by=random --colors=always --coverage-html build/coverage-unit-html",
|
||||
"test:unit": "@php vendor/bin/phpunit --order-by=random --colors=always --testdox",
|
||||
"test:unit:ci": "@test:unit --coverage-php=build/coverage-unit.cov --coverage-xml=build/coverage-unit/coverage-xml --log-junit=build/coverage-unit/junit.xml",
|
||||
"test:unit:pretty": "@test:unit --coverage-html build/coverage-unit/coverage-html",
|
||||
"test:db": "@parallel test:db:sqlite:ci test:db:mysql test:db:maria test:db:postgres test:db:ms",
|
||||
"test:db:sqlite": "APP_ENV=test php vendor/bin/phpunit --order-by=random --colors=always --testdox -c phpunit-db.xml",
|
||||
"test:db:sqlite:ci": "@test:db:sqlite --coverage-php build/coverage-db.cov --coverage-xml=build/coverage-db/coverage-xml --log-junit=build/coverage-db/junit.xml",
|
||||
@@ -136,43 +125,76 @@
|
||||
"test:db:postgres": "DB_DRIVER=postgres composer test:db:sqlite",
|
||||
"test:db:ms": "DB_DRIVER=mssql composer test:db:sqlite",
|
||||
"test:api": "bin/test/run-api-tests.sh",
|
||||
"infect:ci:base": "infection --threads=4 --log-verbosity=default --only-covered --only-covering-test-cases --skip-initial-tests",
|
||||
"infect:ci:unit": "@infect:ci:base --coverage=build/coverage-unit --min-msi=80",
|
||||
"infect:ci:db": "@infect:ci:base --coverage=build/coverage-db --min-msi=95 --configuration=infection-db.json",
|
||||
"infect:ci": "@parallel infect:ci:unit infect:ci:db",
|
||||
"test:api:rr": "TEST_RUNTIME=rr bin/test/run-api-tests.sh",
|
||||
"test:api:ci": "GENERATE_COVERAGE=yes composer test:api",
|
||||
"test:api:pretty": "GENERATE_COVERAGE=pretty composer test:api",
|
||||
"test:cli": "APP_ENV=test DB_DRIVER=maria TEST_ENV=cli php vendor/bin/phpunit --order-by=random --colors=always --testdox -c phpunit-cli.xml --log-junit=build/coverage-cli/junit.xml",
|
||||
"test:cli:ci": "GENERATE_COVERAGE=yes composer test:cli",
|
||||
"test:cli:pretty": "GENERATE_COVERAGE=pretty composer test:cli",
|
||||
"infect:ci:base": "infection --threads=max --only-covered --only-covering-test-cases --skip-initial-tests",
|
||||
"infect:ci:unit": "@infect:ci:base --coverage=build/coverage-unit --min-msi=84",
|
||||
"infect:ci:db": "@infect:ci:base --coverage=build/coverage-db --min-msi=95 --configuration=infection-db.json5",
|
||||
"infect:ci:api": "@infect:ci:base --coverage=build/coverage-api --min-msi=80 --configuration=infection-api.json5",
|
||||
"infect:ci:cli": "@infect:ci:base --coverage=build/coverage-cli --min-msi=80 --configuration=infection-cli.json5",
|
||||
"infect:ci": "@parallel infect:ci:unit infect:ci:db infect:ci:api infect:ci:cli",
|
||||
"infect:test": [
|
||||
"@parallel test:unit:ci test:db:sqlite:ci",
|
||||
"@parallel test:unit:ci test:db:sqlite:ci test:api:ci",
|
||||
"@infect:ci"
|
||||
],
|
||||
"infect:test:unit": [
|
||||
"@test:unit:ci",
|
||||
"@infect:ci:unit"
|
||||
],
|
||||
"infect:test:api": [
|
||||
"@test:api:ci",
|
||||
"@infect:ci:api"
|
||||
],
|
||||
"infect:test:cli": [
|
||||
"@test:cli:ci",
|
||||
"@infect:ci:cli"
|
||||
],
|
||||
"swagger:validate": "php-openapi validate docs/swagger/swagger.json",
|
||||
"swagger:inline": "php-openapi inline docs/swagger/swagger.json docs/swagger/swagger-inlined.json",
|
||||
"clean:dev": "rm -f data/database.sqlite && rm -f config/params/generated_config.php"
|
||||
},
|
||||
"scripts-descriptions": {
|
||||
"ci": "<fg=blue;options=bold>Alias for \"cs\", \"stan\", \"test:ci\" and \"infect:ci\"</>",
|
||||
"ci:parallel": "<fg=blue;options=bold>Same as \"ci\", but parallelizing tasks as much as possible</>",
|
||||
"ci": "<fg=blue;options=bold>Alias for \"cs\", \"stan\", \"swagger:validate\", \"test:ci\" and \"infect: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:ci": "<fg=blue;options=bold>Runs all test suites, generating all needed reports and logs for CI envs</>",
|
||||
"test:unit": "<fg=blue;options=bold>Runs unit test suites</>",
|
||||
"test:unit:ci": "<fg=blue;options=bold>Runs unit test suites, generating all needed reports and logs for CI envs</>",
|
||||
"test: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 Miscrosoft SQL Server database</>",
|
||||
"test:db:ms": "<fg=blue;options=bold>Runs database test suites on a Microsoft SQL Server database</>",
|
||||
"test:api": "<fg=blue;options=bold>Runs API test suites</>",
|
||||
"test:unit:pretty": "<fg=blue;options=bold>Runs unit test suites and generates an HTML code coverage report</>",
|
||||
"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</>",
|
||||
"infect:ci": "<fg=blue;options=bold>Checks unit and db tests quality applying mutation testing with existing reports and logs</>",
|
||||
"infect:ci:unit": "<fg=blue;options=bold>Checks unit tests quality applying mutation testing with existing reports and logs</>",
|
||||
"infect:ci:db": "<fg=blue;options=bold>Checks db tests quality applying mutation testing with existing reports and logs</>",
|
||||
"infect:test": "<fg=blue;options=bold>Runs unit and db tests, then checks tests quality applying mutation testing</>",
|
||||
"swagger:validate": "<fg=blue;options=bold>Validates the swagger docs, making sure they fulfil the spec</>",
|
||||
"swagger:inline": "<fg=blue;options=bold>Inlines swagger docs in a single file</>",
|
||||
"clean:dev": "<fg=blue;options=bold>Deletes artifacts which are gitignored and could affect dev env</>"
|
||||
},
|
||||
"config": {
|
||||
"sort-packages": true,
|
||||
"platform-check": false
|
||||
"platform-check": false,
|
||||
"allow-plugins": {
|
||||
"composer/package-versions-deprecated": true,
|
||||
"dealerdirect/phpcodesniffer-composer-installer": true,
|
||||
"infection/extension-installer": true,
|
||||
"veewee/composer-run-parallel": true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
11
config/autoload/app_options.local.php.dist
Normal file
11
config/autoload/app_options.local.php.dist
Normal file
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
return [
|
||||
|
||||
'app_options' => [
|
||||
'version' => 'latest',
|
||||
],
|
||||
|
||||
];
|
||||
@@ -8,8 +8,8 @@ return [
|
||||
|
||||
'debug' => false,
|
||||
|
||||
// Disabling config cache for cli, ensures it's never used for swoole and also that console commands don't generate
|
||||
// a cache file that's then used by non-swoole web executions
|
||||
// Disabling config cache for cli, ensures it's never used for openswoole/RoadRunner, and also that console
|
||||
// commands don't generate a cache file that's then used by php-fpm web executions
|
||||
ConfigAggregator::ENABLE_CACHE => PHP_SAPI !== 'cli',
|
||||
|
||||
];
|
||||
|
||||
@@ -4,11 +4,17 @@ declare(strict_types=1);
|
||||
|
||||
namespace Shlinkio\Shlink;
|
||||
|
||||
return [
|
||||
use Shlinkio\Shlink\Core\Config\EnvVars;
|
||||
|
||||
'delete_short_urls' => [
|
||||
'visits_threshold' => 15,
|
||||
'check_visits_threshold' => true,
|
||||
],
|
||||
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),
|
||||
],
|
||||
|
||||
];
|
||||
})();
|
||||
|
||||
@@ -3,12 +3,22 @@
|
||||
declare(strict_types=1);
|
||||
|
||||
use GuzzleHttp\Client;
|
||||
use Laminas\ServiceManager\AbstractFactory\ConfigAbstractFactory;
|
||||
use Mezzio\Container;
|
||||
use Psr\Http\Client\ClientInterface;
|
||||
use Psr\Http\Message\ServerRequestFactoryInterface;
|
||||
use Psr\Http\Message\StreamFactoryInterface;
|
||||
use Psr\Http\Message\UploadedFileFactoryInterface;
|
||||
use Spiral\RoadRunner\Http\PSR7Worker;
|
||||
use Spiral\RoadRunner\WorkerInterface;
|
||||
|
||||
return [
|
||||
|
||||
'dependencies' => [
|
||||
'factories' => [
|
||||
PSR7Worker::class => ConfigAbstractFactory::class,
|
||||
],
|
||||
|
||||
'delegators' => [
|
||||
Mezzio\Application::class => [
|
||||
Container\ApplicationConfigInjectionDelegator::class,
|
||||
@@ -26,4 +36,13 @@ return [
|
||||
],
|
||||
],
|
||||
|
||||
ConfigAbstractFactory::class => [
|
||||
PSR7Worker::class => [
|
||||
WorkerInterface::class,
|
||||
ServerRequestFactoryInterface::class,
|
||||
StreamFactoryInterface::class,
|
||||
UploadedFileFactoryInterface::class,
|
||||
],
|
||||
],
|
||||
|
||||
];
|
||||
|
||||
@@ -2,24 +2,59 @@
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Shlinkio\Shlink\Common;
|
||||
|
||||
use Happyr\DoctrineSpecification\Repository\EntitySpecificationRepository;
|
||||
use Shlinkio\Shlink\Core\Config\EnvVars;
|
||||
|
||||
return [
|
||||
use function Functional\contains;
|
||||
|
||||
'entity_manager' => [
|
||||
'orm' => [
|
||||
'proxies_dir' => 'data/proxies',
|
||||
'load_mappings_using_functional_style' => true,
|
||||
'default_repository_classname' => EntitySpecificationRepository::class,
|
||||
return (static function (): array {
|
||||
$driver = EnvVars::DB_DRIVER->loadFromEnv();
|
||||
$isMysqlCompatible = contains(['maria', 'mysql'], $driver);
|
||||
|
||||
$resolveDriver = static fn () => match ($driver) {
|
||||
'postgres' => 'pdo_pgsql',
|
||||
'mssql' => 'pdo_sqlsrv',
|
||||
default => 'pdo_mysql',
|
||||
};
|
||||
$resolveDefaultPort = static fn () => 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
|
||||
// flowing in the connection, so it has to match what has been set in the database.
|
||||
'maria', 'mysql' => 'utf8mb4',
|
||||
'postgres' => 'utf8',
|
||||
default => null,
|
||||
};
|
||||
$resolveConnection = static fn () => match ($driver) {
|
||||
null, 'sqlite' => [
|
||||
'driver' => 'pdo_sqlite',
|
||||
'path' => 'data/database.sqlite',
|
||||
],
|
||||
'connection' => [
|
||||
'user' => '',
|
||||
'password' => '',
|
||||
'dbname' => 'shlink',
|
||||
'charset' => 'utf8',
|
||||
default => [
|
||||
'driver' => $resolveDriver(),
|
||||
'dbname' => EnvVars::DB_NAME->loadFromEnv('shlink'),
|
||||
'user' => EnvVars::DB_USER->loadFromEnv(),
|
||||
'password' => EnvVars::DB_PASSWORD->loadFromEnv(),
|
||||
'host' => EnvVars::DB_HOST->loadFromEnv(EnvVars::DB_UNIX_SOCKET->loadFromEnv()),
|
||||
'port' => EnvVars::DB_PORT->loadFromEnv($resolveDefaultPort()),
|
||||
'unix_socket' => $isMysqlCompatible ? EnvVars::DB_UNIX_SOCKET->loadFromEnv() : null,
|
||||
'charset' => $resolveCharset(),
|
||||
],
|
||||
],
|
||||
};
|
||||
|
||||
];
|
||||
return [
|
||||
|
||||
'entity_manager' => [
|
||||
'orm' => [
|
||||
'proxies_dir' => 'data/proxies',
|
||||
'load_mappings_using_functional_style' => true,
|
||||
'default_repository_classname' => EntitySpecificationRepository::class,
|
||||
],
|
||||
'connection' => $resolveConnection(),
|
||||
],
|
||||
|
||||
];
|
||||
})();
|
||||
|
||||
@@ -9,7 +9,9 @@ return [
|
||||
'user' => 'root',
|
||||
'password' => 'root',
|
||||
'driver' => 'pdo_mysql',
|
||||
'host' => 'shlink_db',
|
||||
'host' => 'shlink_db_mysql',
|
||||
'dbname' => 'shlink',
|
||||
'charset' => 'utf8mb4',
|
||||
],
|
||||
],
|
||||
|
||||
|
||||
@@ -6,12 +6,14 @@ use Laminas\Stratigility\Middleware\ErrorHandler;
|
||||
use Mezzio\ProblemDetails\ProblemDetailsMiddleware;
|
||||
use Shlinkio\Shlink\Common\Logger;
|
||||
|
||||
use function Shlinkio\Shlink\Core\toProblemDetailsType;
|
||||
|
||||
return [
|
||||
|
||||
'problem-details' => [
|
||||
'default_types_map' => [
|
||||
404 => 'NOT_FOUND',
|
||||
500 => 'INTERNAL_SERVER_ERROR',
|
||||
404 => toProblemDetailsType('not-found'),
|
||||
500 => toProblemDetailsType('internal-server-error'),
|
||||
],
|
||||
],
|
||||
|
||||
|
||||
@@ -2,12 +2,14 @@
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Shlinkio\Shlink\Core\Config\EnvVars;
|
||||
|
||||
return [
|
||||
|
||||
'geolite2' => [
|
||||
'db_location' => __DIR__ . '/../../data/GeoLite2-City.mmdb',
|
||||
'temp_dir' => __DIR__ . '/../../data',
|
||||
'license_key' => 'G4Lm0C60yJsnkdPi', // Deprecated. Remove hardcoded license on v3
|
||||
'license_key' => EnvVars::GEOLITE_LICENSE_KEY->loadFromEnv(),
|
||||
],
|
||||
|
||||
];
|
||||
|
||||
@@ -18,21 +18,21 @@ return [
|
||||
Option\Database\DatabaseUserConfigOption::class,
|
||||
Option\Database\DatabasePasswordConfigOption::class,
|
||||
Option\Database\DatabaseUnixSocketConfigOption::class,
|
||||
Option\Database\DatabaseSqlitePathConfigOption::class,
|
||||
Option\Database\DatabaseMySqlOptionsConfigOption::class,
|
||||
Option\UrlShortener\ShortDomainHostConfigOption::class,
|
||||
Option\UrlShortener\ShortDomainSchemaConfigOption::class,
|
||||
Option\UrlShortener\ValidateUrlConfigOption::class,
|
||||
Option\Visit\VisitsWebhooksConfigOption::class,
|
||||
Option\Visit\OrphanVisitsWebhooksConfigOption::class,
|
||||
Option\Redirect\BaseUrlRedirectConfigOption::class,
|
||||
Option\Redirect\InvalidShortUrlRedirectConfigOption::class,
|
||||
Option\Redirect\Regular404RedirectConfigOption::class,
|
||||
Option\Visit\CheckVisitsThresholdConfigOption::class,
|
||||
Option\Visit\VisitsThresholdConfigOption::class,
|
||||
Option\BasePathConfigOption::class,
|
||||
Option\TimezoneConfigOption::class,
|
||||
Option\Worker\TaskWorkerNumConfigOption::class,
|
||||
Option\Worker\WebWorkerNumConfigOption::class,
|
||||
Option\RedisServersConfigOption::class,
|
||||
Option\Redis\RedisServersConfigOption::class,
|
||||
Option\Redis\RedisSentinelServiceConfigOption::class,
|
||||
Option\Redis\RedisPubSubConfigOption::class,
|
||||
Option\UrlShortener\ShortCodeLengthOption::class,
|
||||
Option\Mercure\EnableMercureConfigOption::class,
|
||||
Option\Mercure\MercurePublicUrlConfigOption::class,
|
||||
@@ -43,25 +43,48 @@ return [
|
||||
Option\UrlShortener\RedirectCacheLifeTimeConfigOption::class,
|
||||
Option\UrlShortener\AutoResolveTitlesConfigOption::class,
|
||||
Option\UrlShortener\AppendExtraPathConfigOption::class,
|
||||
Option\UrlShortener\EnableMultiSegmentSlugsConfigOption::class,
|
||||
Option\UrlShortener\EnableTrailingSlashConfigOption::class,
|
||||
Option\Tracking\IpAnonymizationConfigOption::class,
|
||||
Option\Tracking\OrphanVisitsTrackingConfigOption::class,
|
||||
Option\Tracking\DisableTrackParamConfigOption::class,
|
||||
Option\Tracking\DisableTrackingFromConfigOption::class,
|
||||
Option\Tracking\DisableTrackingConfigOption::class,
|
||||
Option\Tracking\DisableIpTrackingConfigOption::class,
|
||||
Option\Tracking\DisableReferrerTrackingConfigOption::class,
|
||||
Option\Tracking\DisableUaTrackingConfigOption::class,
|
||||
Option\QrCode\DefaultSizeConfigOption::class,
|
||||
Option\QrCode\DefaultMarginConfigOption::class,
|
||||
Option\QrCode\DefaultFormatConfigOption::class,
|
||||
Option\QrCode\DefaultErrorCorrectionConfigOption::class,
|
||||
Option\QrCode\DefaultRoundBlockSizeConfigOption::class,
|
||||
Option\RabbitMq\RabbitMqEnabledConfigOption::class,
|
||||
Option\RabbitMq\RabbitMqHostConfigOption::class,
|
||||
Option\RabbitMq\RabbitMqPortConfigOption::class,
|
||||
Option\RabbitMq\RabbitMqUserConfigOption::class,
|
||||
Option\RabbitMq\RabbitMqPasswordConfigOption::class,
|
||||
Option\RabbitMq\RabbitMqVhostConfigOption::class,
|
||||
],
|
||||
|
||||
'installation_commands' => [
|
||||
InstallationCommand::DB_CREATE_SCHEMA => [
|
||||
InstallationCommand::DB_CREATE_SCHEMA->value => [
|
||||
'command' => 'bin/cli ' . Command\Db\CreateDatabaseCommand::NAME,
|
||||
],
|
||||
InstallationCommand::DB_MIGRATE => [
|
||||
InstallationCommand::DB_MIGRATE->value => [
|
||||
'command' => 'bin/cli ' . Command\Db\MigrateDatabaseCommand::NAME,
|
||||
],
|
||||
InstallationCommand::GEOLITE_DOWNLOAD_DB => [
|
||||
InstallationCommand::ORM_PROXIES->value => [
|
||||
'command' => 'bin/doctrine orm:generate-proxies',
|
||||
],
|
||||
InstallationCommand::ORM_CLEAR_CACHE->value => [
|
||||
'command' => 'bin/doctrine orm:clear-cache:metadata',
|
||||
],
|
||||
InstallationCommand::GEOLITE_DOWNLOAD_DB->value => [
|
||||
'command' => 'bin/cli ' . Command\Visit\DownloadGeoLiteDbCommand::NAME,
|
||||
],
|
||||
InstallationCommand::API_KEY_GENERATE->value => [
|
||||
'command' => 'bin/cli ' . Command\Api\GenerateKeyCommand::NAME,
|
||||
],
|
||||
],
|
||||
],
|
||||
|
||||
|
||||
@@ -4,11 +4,11 @@ declare(strict_types=1);
|
||||
|
||||
use Laminas\ServiceManager\AbstractFactory\ConfigAbstractFactory;
|
||||
use Shlinkio\Shlink\Common\Cache\RedisFactory;
|
||||
use Shlinkio\Shlink\Common\Lock\RetryLockStoreDelegatorFactory;
|
||||
use Shlinkio\Shlink\Common\Logger\LoggerAwareDelegatorFactory;
|
||||
use Shlinkio\Shlink\Core\Config\EnvVars;
|
||||
use Symfony\Component\Lock;
|
||||
|
||||
use const Shlinkio\Shlink\Core\LOCAL_LOCK_FACTORY;
|
||||
use const Shlinkio\Shlink\LOCAL_LOCK_FACTORY;
|
||||
|
||||
return [
|
||||
|
||||
@@ -24,16 +24,12 @@ return [
|
||||
LOCAL_LOCK_FACTORY => ConfigAbstractFactory::class,
|
||||
],
|
||||
'aliases' => [
|
||||
// With this config, a user could alias 'lock_store' => 'redis_lock_store' to override the default
|
||||
'lock_store' => 'local_lock_store',
|
||||
'lock_store' => EnvVars::REDIS_SERVERS->existsInEnv() ? 'redis_lock_store' : 'local_lock_store',
|
||||
|
||||
'redis_lock_store' => Lock\Store\RedisStore::class,
|
||||
'local_lock_store' => Lock\Store\FlockStore::class,
|
||||
],
|
||||
'delegators' => [
|
||||
Lock\Store\RedisStore::class => [
|
||||
RetryLockStoreDelegatorFactory::class,
|
||||
],
|
||||
Lock\LockFactory::class => [
|
||||
LoggerAwareDelegatorFactory::class,
|
||||
],
|
||||
|
||||
@@ -4,72 +4,36 @@ declare(strict_types=1);
|
||||
|
||||
namespace Shlinkio\Shlink;
|
||||
|
||||
use Monolog\Formatter;
|
||||
use Monolog\Handler;
|
||||
use Monolog\Level;
|
||||
use Monolog\Logger;
|
||||
use Monolog\Processor;
|
||||
use MonologFactory\DiContainerLoggerFactory;
|
||||
use PhpMiddleware\RequestId;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Shlinkio\Shlink\Common\Logger\LoggerFactory;
|
||||
use Shlinkio\Shlink\Common\Logger\LoggerType;
|
||||
|
||||
use const PHP_EOL;
|
||||
|
||||
$processors = [
|
||||
'exception_with_new_line' => [
|
||||
'name' => Common\Logger\Processor\ExceptionWithNewLineProcessor::class,
|
||||
],
|
||||
'psr3' => [
|
||||
'name' => Processor\PsrLogMessageProcessor::class,
|
||||
],
|
||||
'request_id' => RequestId\MonologProcessor::class,
|
||||
];
|
||||
$formatter = [
|
||||
'name' => Formatter\LineFormatter::class,
|
||||
'params' => [
|
||||
'format' => '[%datetime%] [%extra.request_id%] %channel%.%level_name% - %message%' . PHP_EOL,
|
||||
'allow_inline_line_breaks' => true,
|
||||
],
|
||||
$common = [
|
||||
'level' => Level::Info->value,
|
||||
'processors' => [RequestId\MonologProcessor::class],
|
||||
'line_format' => '[%datetime%] [%extra.request_id%] %channel%.%level_name% - %message%',
|
||||
];
|
||||
|
||||
return [
|
||||
|
||||
'logger' => [
|
||||
'Shlink' => [
|
||||
'name' => 'Shlink',
|
||||
'handlers' => [
|
||||
'shlink_handler' => [
|
||||
'name' => Handler\RotatingFileHandler::class,
|
||||
'params' => [
|
||||
'level' => Logger::INFO,
|
||||
'filename' => 'data/log/shlink_log.log',
|
||||
'max_files' => 30,
|
||||
'file_permission' => 0666,
|
||||
],
|
||||
'formatter' => $formatter,
|
||||
],
|
||||
],
|
||||
'processors' => $processors,
|
||||
'type' => LoggerType::FILE->value,
|
||||
...$common,
|
||||
],
|
||||
'Access' => [
|
||||
'name' => 'Access',
|
||||
'handlers' => [
|
||||
'access_handler' => [
|
||||
'name' => Handler\StreamHandler::class,
|
||||
'params' => [
|
||||
'level' => Logger::INFO,
|
||||
'stream' => 'php://stdout',
|
||||
],
|
||||
'formatter' => $formatter,
|
||||
],
|
||||
],
|
||||
'processors' => $processors,
|
||||
'type' => LoggerType::STREAM->value,
|
||||
...$common,
|
||||
],
|
||||
],
|
||||
|
||||
'dependencies' => [
|
||||
'factories' => [
|
||||
'Logger_Shlink' => [DiContainerLoggerFactory::class, 'Shlink'],
|
||||
'Logger_Access' => [DiContainerLoggerFactory::class, 'Access'],
|
||||
'Logger_Shlink' => [LoggerFactory::class, 'Shlink'],
|
||||
'Logger_Access' => [LoggerFactory::class, 'Access'],
|
||||
],
|
||||
'aliases' => [
|
||||
'logger' => 'Logger_Shlink',
|
||||
@@ -82,7 +46,7 @@ return [
|
||||
'swoole-http-server' => [
|
||||
'logger' => [
|
||||
'logger-name' => 'Logger_Access',
|
||||
'format' => '%h %l %u "%r" %>s %b',
|
||||
'format' => '%u "%r" %>s %B',
|
||||
],
|
||||
],
|
||||
],
|
||||
|
||||
@@ -2,33 +2,18 @@
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Monolog\Handler\StreamHandler;
|
||||
use Monolog\Logger;
|
||||
use Monolog\Level;
|
||||
use Shlinkio\Shlink\Common\Logger\LoggerType;
|
||||
|
||||
$isSwoole = extension_loaded('swoole');
|
||||
|
||||
// For swoole, send logs to standard output
|
||||
$handler = $isSwoole
|
||||
? [
|
||||
'name' => StreamHandler::class,
|
||||
'params' => [
|
||||
'level' => Logger::DEBUG,
|
||||
'stream' => 'php://stdout',
|
||||
],
|
||||
]
|
||||
: [
|
||||
'params' => [
|
||||
'level' => Logger::DEBUG,
|
||||
],
|
||||
];
|
||||
$isSwoole = extension_loaded('openswoole');
|
||||
|
||||
return [
|
||||
|
||||
'logger' => [
|
||||
'Shlink' => [
|
||||
'handlers' => [
|
||||
'shlink_handler' => $handler,
|
||||
],
|
||||
// For swoole, send logs as stream
|
||||
'type' => $isSwoole ? LoggerType::STREAM->value : LoggerType::FILE->value,
|
||||
'level' => Level::Debug->value,
|
||||
],
|
||||
],
|
||||
|
||||
|
||||
@@ -4,33 +4,38 @@ declare(strict_types=1);
|
||||
|
||||
use Laminas\ServiceManager\Proxy\LazyServiceFactory;
|
||||
use Shlinkio\Shlink\Common\Mercure\LcobucciJwtProvider;
|
||||
use Shlinkio\Shlink\Core\Config\EnvVars;
|
||||
use Symfony\Component\Mercure\Hub;
|
||||
use Symfony\Component\Mercure\HubInterface;
|
||||
|
||||
return [
|
||||
return (static function (): array {
|
||||
$publicUrl = EnvVars::MERCURE_PUBLIC_HUB_URL->loadFromEnv();
|
||||
|
||||
'mercure' => [
|
||||
'public_hub_url' => null,
|
||||
'internal_hub_url' => null,
|
||||
'jwt_secret' => null,
|
||||
'jwt_issuer' => 'Shlink',
|
||||
],
|
||||
return [
|
||||
|
||||
'dependencies' => [
|
||||
'delegators' => [
|
||||
LcobucciJwtProvider::class => [
|
||||
LazyServiceFactory::class,
|
||||
'mercure' => [
|
||||
'public_hub_url' => $publicUrl,
|
||||
'internal_hub_url' => EnvVars::MERCURE_INTERNAL_HUB_URL->loadFromEnv($publicUrl),
|
||||
'jwt_secret' => EnvVars::MERCURE_JWT_SECRET->loadFromEnv(),
|
||||
'jwt_issuer' => 'Shlink',
|
||||
],
|
||||
|
||||
'dependencies' => [
|
||||
'delegators' => [
|
||||
LcobucciJwtProvider::class => [
|
||||
LazyServiceFactory::class,
|
||||
],
|
||||
Hub::class => [
|
||||
LazyServiceFactory::class,
|
||||
],
|
||||
],
|
||||
Hub::class => [
|
||||
LazyServiceFactory::class,
|
||||
'lazy_services' => [
|
||||
'class_map' => [
|
||||
LcobucciJwtProvider::class => LcobucciJwtProvider::class,
|
||||
Hub::class => HubInterface::class,
|
||||
],
|
||||
],
|
||||
],
|
||||
'lazy_services' => [
|
||||
'class_map' => [
|
||||
LcobucciJwtProvider::class => LcobucciJwtProvider::class,
|
||||
Hub::class => HubInterface::class,
|
||||
],
|
||||
],
|
||||
],
|
||||
|
||||
];
|
||||
];
|
||||
})();
|
||||
|
||||
@@ -7,7 +7,7 @@ return [
|
||||
'mercure' => [
|
||||
'public_hub_url' => 'http://localhost:8001',
|
||||
'internal_hub_url' => 'http://shlink_mercure_proxy',
|
||||
'jwt_secret' => 'mercure_jwt_key',
|
||||
'jwt_secret' => 'mercure_jwt_key_long_enough_to_avoid_error',
|
||||
],
|
||||
|
||||
];
|
||||
|
||||
@@ -17,14 +17,14 @@ return [
|
||||
'error-handler' => [
|
||||
'middleware' => [
|
||||
ContentLengthMiddleware::class,
|
||||
RequestIdMiddleware::class,
|
||||
ErrorHandler::class,
|
||||
Rest\Middleware\CrossDomainMiddleware::class,
|
||||
],
|
||||
],
|
||||
'error-handler-rest' => [
|
||||
'path' => '/rest',
|
||||
'middleware' => [
|
||||
Rest\Middleware\CrossDomainMiddleware::class,
|
||||
RequestIdMiddleware::class,
|
||||
ProblemDetails\ProblemDetailsMiddleware::class,
|
||||
],
|
||||
],
|
||||
@@ -45,6 +45,7 @@ return [
|
||||
'rest' => [
|
||||
'path' => '/rest',
|
||||
'middleware' => [
|
||||
Rest\Middleware\ErrorHandler\BackwardsCompatibleProblemDetailsHandler::class,
|
||||
Router\Middleware\ImplicitOptionsMiddleware::class,
|
||||
Rest\Middleware\BodyParserMiddleware::class,
|
||||
Rest\Middleware\AuthenticationMiddleware::class,
|
||||
|
||||
27
config/autoload/qr-codes.global.php
Normal file
27
config/autoload/qr-codes.global.php
Normal file
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Shlinkio\Shlink\Core\Config\EnvVars;
|
||||
|
||||
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,
|
||||
),
|
||||
],
|
||||
|
||||
];
|
||||
21
config/autoload/rabbit.global.php
Normal file
21
config/autoload/rabbit.global.php
Normal file
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Shlinkio\Shlink\Core\Config\EnvVars;
|
||||
|
||||
return [
|
||||
|
||||
'rabbitmq' => [
|
||||
'enabled' => (bool) EnvVars::RABBITMQ_ENABLED->loadFromEnv(false),
|
||||
'host' => EnvVars::RABBITMQ_HOST->loadFromEnv(),
|
||||
'port' => (int) EnvVars::RABBITMQ_PORT->loadFromEnv('5672'),
|
||||
'user' => EnvVars::RABBITMQ_USER->loadFromEnv(),
|
||||
'password' => EnvVars::RABBITMQ_PASSWORD->loadFromEnv(),
|
||||
'vhost' => EnvVars::RABBITMQ_VHOST->loadFromEnv('/'),
|
||||
|
||||
// Deprecated
|
||||
'legacy_visits_publishing' => (bool) EnvVars::RABBITMQ_LEGACY_VISITS_PUBLISHING->loadFromEnv(false),
|
||||
],
|
||||
|
||||
];
|
||||
14
config/autoload/rabbit.local.php.dist
Normal file
14
config/autoload/rabbit.local.php.dist
Normal file
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
return [
|
||||
|
||||
'rabbitmq' => [
|
||||
'enabled' => true,
|
||||
'host' => 'shlink_rabbitmq',
|
||||
'user' => 'rabbit',
|
||||
'password' => 'rabbit',
|
||||
],
|
||||
|
||||
];
|
||||
@@ -2,12 +2,24 @@
|
||||
|
||||
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' => null,
|
||||
'regular_404' => null,
|
||||
'base_url' => null,
|
||||
'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),
|
||||
'redirect_cache_lifetime' => (int) EnvVars::REDIRECT_CACHE_LIFETIME->loadFromEnv(
|
||||
DEFAULT_REDIRECT_CACHE_LIFETIME,
|
||||
),
|
||||
],
|
||||
|
||||
];
|
||||
|
||||
27
config/autoload/redis.global.php
Normal file
27
config/autoload/redis.global.php
Normal file
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Shlinkio\Shlink\Core\Config\EnvVars;
|
||||
|
||||
return (static function (): array {
|
||||
$redisServers = EnvVars::REDIS_SERVERS->loadFromEnv();
|
||||
$pubSub = [
|
||||
'redis' => [
|
||||
'pub_sub_enabled' => $redisServers !== null && EnvVars::REDIS_PUB_SUB_ENABLED->loadFromEnv(false),
|
||||
],
|
||||
];
|
||||
|
||||
return match ($redisServers) {
|
||||
null => $pubSub,
|
||||
default => [
|
||||
'cache' => [
|
||||
'redis' => [
|
||||
'servers' => $redisServers,
|
||||
'sentinel_service' => EnvVars::REDIS_SENTINEL_SERVICE->loadFromEnv(),
|
||||
],
|
||||
],
|
||||
...$pubSub,
|
||||
],
|
||||
};
|
||||
})();
|
||||
@@ -7,12 +7,13 @@ return [
|
||||
'cache' => [
|
||||
'redis' => [
|
||||
'servers' => 'tcp://shlink_redis:6379',
|
||||
// 'servers' => [
|
||||
// 'tcp://shlink_redis:6379',
|
||||
// ],
|
||||
],
|
||||
],
|
||||
|
||||
'redis' => [
|
||||
'pub_sub_enabled' => true,
|
||||
],
|
||||
|
||||
'dependencies' => [
|
||||
'aliases' => [
|
||||
// With this config, a user could alias 'lock_store' => 'redis_lock_store' to override the default
|
||||
|
||||
@@ -5,6 +5,7 @@ declare(strict_types=1);
|
||||
use Laminas\ServiceManager\AbstractFactory\ConfigAbstractFactory;
|
||||
use Laminas\ServiceManager\Factory\InvokableFactory;
|
||||
use PhpMiddleware\RequestId;
|
||||
use Shlinkio\Shlink\Common\Logger\Processor\BackwardsCompatibleMonologProcessorDelegator;
|
||||
|
||||
return [
|
||||
|
||||
@@ -20,6 +21,11 @@ return [
|
||||
RequestId\RequestIdMiddleware::class => ConfigAbstractFactory::class,
|
||||
RequestId\MonologProcessor::class => ConfigAbstractFactory::class,
|
||||
],
|
||||
'delegators' => [
|
||||
RequestId\MonologProcessor::class => [
|
||||
BackwardsCompatibleMonologProcessorDelegator::class,
|
||||
],
|
||||
],
|
||||
],
|
||||
|
||||
ConfigAbstractFactory::class => [
|
||||
|
||||
@@ -3,11 +3,12 @@
|
||||
declare(strict_types=1);
|
||||
|
||||
use Mezzio\Router\FastRouteRouter;
|
||||
use Shlinkio\Shlink\Core\Config\EnvVars;
|
||||
|
||||
return [
|
||||
|
||||
'router' => [
|
||||
'base_path' => '',
|
||||
'base_path' => EnvVars::BASE_PATH->loadFromEnv(''),
|
||||
|
||||
'fastroute' => [
|
||||
FastRouteRouter::CONFIG_CACHE_ENABLED => true,
|
||||
|
||||
111
config/autoload/routes.config.php
Normal file
111
config/autoload/routes.config.php
Normal file
@@ -0,0 +1,111 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Shlinkio\Shlink;
|
||||
|
||||
use Fig\Http\Message\RequestMethodInterface;
|
||||
use RKA\Middleware\IpAddress;
|
||||
use Shlinkio\Shlink\Core\Action as CoreAction;
|
||||
use Shlinkio\Shlink\Core\Config\EnvVars;
|
||||
use Shlinkio\Shlink\Core\ShortUrl\Middleware\TrimTrailingSlashMiddleware;
|
||||
use Shlinkio\Shlink\Rest\Action;
|
||||
use Shlinkio\Shlink\Rest\ConfigProvider;
|
||||
use Shlinkio\Shlink\Rest\Middleware;
|
||||
use Shlinkio\Shlink\Rest\Middleware\Mercure\NotConfiguredMercureErrorHandler;
|
||||
|
||||
use function sprintf;
|
||||
|
||||
return (static function (): array {
|
||||
$contentNegotiationMiddleware = Middleware\ShortUrl\CreateShortUrlContentNegotiationMiddleware::class;
|
||||
$dropDomainMiddleware = Middleware\ShortUrl\DropDefaultDomainFromRequestMiddleware::class;
|
||||
$overrideDomainMiddleware = Middleware\ShortUrl\OverrideDomainMiddleware::class;
|
||||
|
||||
// TODO This should be based on config, not the env var
|
||||
$shortUrlRouteSuffix = EnvVars::SHORT_URL_TRAILING_SLASH->loadFromEnv(false) ? '[/]' : '';
|
||||
|
||||
return [
|
||||
|
||||
// The order of the routes defined here matters. Changing it might cause path conflicts
|
||||
'routes' => [
|
||||
// Rest
|
||||
...ConfigProvider::applyRoutesPrefix([
|
||||
Action\HealthAction::getRouteDef(),
|
||||
|
||||
// Visits
|
||||
Action\Visit\ShortUrlVisitsAction::getRouteDef([$dropDomainMiddleware]),
|
||||
Action\Visit\TagVisitsAction::getRouteDef(),
|
||||
Action\Visit\DomainVisitsAction::getRouteDef(),
|
||||
Action\Visit\GlobalVisitsAction::getRouteDef(),
|
||||
Action\Visit\OrphanVisitsAction::getRouteDef(),
|
||||
Action\Visit\NonOrphanVisitsAction::getRouteDef(),
|
||||
|
||||
// Short URLs
|
||||
Action\ShortUrl\CreateShortUrlAction::getRouteDef([
|
||||
$contentNegotiationMiddleware,
|
||||
$dropDomainMiddleware,
|
||||
$overrideDomainMiddleware,
|
||||
Middleware\ShortUrl\DefaultShortCodesLengthMiddleware::class,
|
||||
]),
|
||||
Action\ShortUrl\SingleStepCreateShortUrlAction::getRouteDef([
|
||||
$contentNegotiationMiddleware,
|
||||
$overrideDomainMiddleware,
|
||||
]),
|
||||
Action\ShortUrl\EditShortUrlAction::getRouteDef([$dropDomainMiddleware]),
|
||||
Action\ShortUrl\DeleteShortUrlAction::getRouteDef([$dropDomainMiddleware]),
|
||||
Action\ShortUrl\ResolveShortUrlAction::getRouteDef([$dropDomainMiddleware]),
|
||||
Action\ShortUrl\ListShortUrlsAction::getRouteDef(),
|
||||
|
||||
// Tags
|
||||
Action\Tag\ListTagsAction::getRouteDef(),
|
||||
Action\Tag\TagsStatsAction::getRouteDef(),
|
||||
Action\Tag\DeleteTagsAction::getRouteDef(),
|
||||
Action\Tag\UpdateTagAction::getRouteDef(),
|
||||
|
||||
// Domains
|
||||
Action\Domain\ListDomainsAction::getRouteDef(),
|
||||
Action\Domain\DomainRedirectsAction::getRouteDef(),
|
||||
|
||||
Action\MercureInfoAction::getRouteDef([NotConfiguredMercureErrorHandler::class]),
|
||||
]),
|
||||
|
||||
// Non-rest
|
||||
[
|
||||
'name' => CoreAction\RobotsAction::class,
|
||||
'path' => '/robots.txt',
|
||||
'middleware' => [
|
||||
CoreAction\RobotsAction::class,
|
||||
],
|
||||
'allowed_methods' => [RequestMethodInterface::METHOD_GET],
|
||||
],
|
||||
[
|
||||
'name' => CoreAction\PixelAction::class,
|
||||
'path' => '/{shortCode}/track',
|
||||
'middleware' => [
|
||||
IpAddress::class,
|
||||
CoreAction\PixelAction::class,
|
||||
],
|
||||
'allowed_methods' => [RequestMethodInterface::METHOD_GET],
|
||||
],
|
||||
[
|
||||
'name' => CoreAction\QrCodeAction::class,
|
||||
'path' => '/{shortCode}/qr-code',
|
||||
'middleware' => [
|
||||
CoreAction\QrCodeAction::class,
|
||||
],
|
||||
'allowed_methods' => [RequestMethodInterface::METHOD_GET],
|
||||
],
|
||||
[
|
||||
'name' => CoreAction\RedirectAction::class,
|
||||
'path' => sprintf('/{shortCode}%s', $shortUrlRouteSuffix),
|
||||
'middleware' => [
|
||||
IpAddress::class,
|
||||
TrimTrailingSlashMiddleware::class,
|
||||
CoreAction\RedirectAction::class,
|
||||
],
|
||||
'allowed_methods' => [RequestMethodInterface::METHOD_GET],
|
||||
],
|
||||
],
|
||||
|
||||
];
|
||||
})();
|
||||
@@ -2,21 +2,30 @@
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
return [
|
||||
use Shlinkio\Shlink\Core\Config\EnvVars;
|
||||
|
||||
'mezzio-swoole' => [
|
||||
// Setting this to true can have unexpected behaviors when running several concurrent slow DB queries
|
||||
'enable_coroutine' => false,
|
||||
use const Shlinkio\Shlink\MIN_TASK_WORKERS;
|
||||
|
||||
'swoole-http-server' => [
|
||||
'host' => '0.0.0.0',
|
||||
'process-name' => 'shlink',
|
||||
return (static function (): array {
|
||||
$taskWorkers = (int) EnvVars::TASK_WORKER_NUM->loadFromEnv(16);
|
||||
|
||||
'options' => [
|
||||
'worker_num' => 16,
|
||||
'task_worker_num' => 16,
|
||||
return [
|
||||
|
||||
'mezzio-swoole' => [
|
||||
// Setting this to true can have unexpected behaviors when running several concurrent slow DB queries
|
||||
'enable_coroutine' => false,
|
||||
|
||||
'swoole-http-server' => [
|
||||
'host' => '0.0.0.0',
|
||||
'port' => (int) EnvVars::PORT->loadFromEnv(8080),
|
||||
'process-name' => 'shlink',
|
||||
|
||||
'options' => [
|
||||
'worker_num' => (int) EnvVars::WEB_WORKER_NUM->loadFromEnv(16),
|
||||
'task_worker_num' => max($taskWorkers, MIN_TASK_WORKERS),
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
|
||||
];
|
||||
];
|
||||
})();
|
||||
|
||||
@@ -2,30 +2,42 @@
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
return [
|
||||
use Shlinkio\Shlink\Core\Config\EnvVars;
|
||||
|
||||
'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' => true,
|
||||
return (static function (): array {
|
||||
/** @var string|null $disableTrackingFrom */
|
||||
$disableTrackingFrom = EnvVars::DISABLE_TRACKING_FROM->loadFromEnv();
|
||||
|
||||
// Tells if visits to not-found URLs should be tracked. The disable_tracking option takes precedence
|
||||
'track_orphan_visits' => true,
|
||||
return [
|
||||
|
||||
// A query param that, if provided, will disable tracking of one particular visit. Always takes precedence
|
||||
'disable_track_param' => null,
|
||||
'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),
|
||||
|
||||
// If true, visits will not be tracked at all
|
||||
'disable_tracking' => false,
|
||||
// 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),
|
||||
|
||||
// If true, visits will be tracked, but neither the IP address, nor the location will be resolved
|
||||
'disable_ip_tracking' => false,
|
||||
// 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, the referrer will not be tracked
|
||||
'disable_referrer_tracking' => false,
|
||||
// If true, visits will not be tracked at all
|
||||
'disable_tracking' => (bool) EnvVars::DISABLE_TRACKING->loadFromEnv(false),
|
||||
|
||||
// If true, the user agent will not be tracked
|
||||
'disable_ua_tracking' => 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)),
|
||||
],
|
||||
|
||||
];
|
||||
})();
|
||||
|
||||
@@ -2,26 +2,30 @@
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use const Shlinkio\Shlink\Core\DEFAULT_REDIRECT_CACHE_LIFETIME;
|
||||
use const Shlinkio\Shlink\Core\DEFAULT_REDIRECT_STATUS_CODE;
|
||||
use const Shlinkio\Shlink\Core\DEFAULT_SHORT_CODES_LENGTH;
|
||||
use Shlinkio\Shlink\Core\Config\EnvVars;
|
||||
|
||||
return [
|
||||
use const Shlinkio\Shlink\DEFAULT_SHORT_CODES_LENGTH;
|
||||
use const Shlinkio\Shlink\MIN_SHORT_CODES_LENGTH;
|
||||
|
||||
'url_shortener' => [
|
||||
'domain' => [
|
||||
'schema' => 'https',
|
||||
'hostname' => '',
|
||||
return (static function (): array {
|
||||
$shortCodesLength = max(
|
||||
(int) EnvVars::DEFAULT_SHORT_CODES_LENGTH->loadFromEnv(DEFAULT_SHORT_CODES_LENGTH),
|
||||
MIN_SHORT_CODES_LENGTH,
|
||||
);
|
||||
|
||||
return [
|
||||
|
||||
'url_shortener' => [
|
||||
'domain' => [ // TODO Refactor this structure to url_shortener.schema and url_shortener.default_domain
|
||||
'schema' => ((bool) EnvVars::IS_HTTPS_ENABLED->loadFromEnv(true)) ? 'https' : 'http',
|
||||
'hostname' => EnvVars::DEFAULT_DOMAIN->loadFromEnv(''),
|
||||
],
|
||||
'default_short_codes_length' => $shortCodesLength,
|
||||
'auto_resolve_titles' => (bool) EnvVars::AUTO_RESOLVE_TITLES->loadFromEnv(false),
|
||||
'append_extra_path' => (bool) EnvVars::REDIRECT_APPEND_EXTRA_PATH->loadFromEnv(false),
|
||||
'multi_segment_slugs_enabled' => (bool) EnvVars::MULTI_SEGMENT_SLUGS_ENABLED->loadFromEnv(false),
|
||||
'trailing_slash_enabled' => (bool) EnvVars::SHORT_URL_TRAILING_SLASH->loadFromEnv(false),
|
||||
],
|
||||
'validate_url' => false, // Deprecated
|
||||
'visits_webhooks' => [],
|
||||
'default_short_codes_length' => DEFAULT_SHORT_CODES_LENGTH,
|
||||
'auto_resolve_titles' => false,
|
||||
'append_extra_path' => false,
|
||||
|
||||
// TODO Move these two options to their own config namespace. Maybe "redirects".
|
||||
'redirect_status_code' => DEFAULT_REDIRECT_STATUS_CODE,
|
||||
'redirect_cache_lifetime' => DEFAULT_REDIRECT_CACHE_LIFETIME,
|
||||
],
|
||||
|
||||
];
|
||||
];
|
||||
})();
|
||||
|
||||
@@ -2,16 +2,23 @@
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
$isSwoole = extension_loaded('swoole');
|
||||
use function Shlinkio\Shlink\Config\runningInOpenswoole;
|
||||
use function Shlinkio\Shlink\Config\runningInRoadRunner;
|
||||
|
||||
return [
|
||||
|
||||
'url_shortener' => [
|
||||
'domain' => [
|
||||
'schema' => 'http',
|
||||
'hostname' => sprintf('localhost:%s', $isSwoole ? '8080' : '8000'),
|
||||
'hostname' => sprintf('localhost:%s', match (true) {
|
||||
runningInRoadRunner() => '8800',
|
||||
runningInOpenswoole() => '8080',
|
||||
default => '8000',
|
||||
}),
|
||||
],
|
||||
'auto_resolve_titles' => true,
|
||||
// 'multi_segment_slugs_enabled' => true,
|
||||
// 'trailing_slash_enabled' => true,
|
||||
],
|
||||
|
||||
];
|
||||
|
||||
20
config/autoload/webhooks.global.php
Normal file
20
config/autoload/webhooks.global.php
Normal file
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Shlinkio\Shlink\Core\Config\EnvVars;
|
||||
|
||||
// Deprecated. Webhooks are no longer supported. To be removed in Shlink 4.0.0
|
||||
return (static function (): array {
|
||||
$webhooks = EnvVars::VISITS_WEBHOOKS->loadFromEnv();
|
||||
|
||||
return [
|
||||
|
||||
'visits_webhooks' => [
|
||||
'webhooks' => $webhooks === null ? [] : explode(',', $webhooks),
|
||||
'notify_orphan_visits_to_webhooks' =>
|
||||
(bool) EnvVars::NOTIFY_ORPHAN_VISITS_TO_WEBHOOKS->loadFromEnv(false),
|
||||
],
|
||||
|
||||
];
|
||||
})();
|
||||
@@ -5,7 +5,7 @@ declare(strict_types=1);
|
||||
use Psr\Container\ContainerInterface;
|
||||
use Symfony\Component\Console\Application as CliApp;
|
||||
|
||||
return (static function () {
|
||||
return (static function (): CliApp {
|
||||
/** @var ContainerInterface $container */
|
||||
$container = include __DIR__ . '/container.php';
|
||||
return $container->get(CliApp::class);
|
||||
|
||||
@@ -8,16 +8,29 @@ use Laminas\ConfigAggregator;
|
||||
use Laminas\Diactoros;
|
||||
use Mezzio;
|
||||
use Mezzio\ProblemDetails;
|
||||
use Mezzio\Swoole\ConfigProvider as SwooleConfigProvider;
|
||||
use Mezzio\Swoole;
|
||||
use Shlinkio\Shlink\Config\ConfigAggregator\EnvVarLoaderProvider;
|
||||
|
||||
use function class_exists;
|
||||
use function Shlinkio\Shlink\Common\env;
|
||||
use function Shlinkio\Shlink\Config\env;
|
||||
use function Shlinkio\Shlink\Config\openswooleIsInstalled;
|
||||
use function Shlinkio\Shlink\Config\runningInRoadRunner;
|
||||
|
||||
use const PHP_SAPI;
|
||||
|
||||
$isTestEnv = env('APP_ENV') === 'test';
|
||||
$enableSwoole = PHP_SAPI === 'cli' && openswooleIsInstalled() && ! runningInRoadRunner();
|
||||
|
||||
return (new ConfigAggregator\ConfigAggregator([
|
||||
! $isTestEnv
|
||||
? new EnvVarLoaderProvider('config/params/generated_config.php', Core\Config\EnvVars::values())
|
||||
: new ConfigAggregator\ArrayProvider([]),
|
||||
Mezzio\ConfigProvider::class,
|
||||
Mezzio\Router\ConfigProvider::class,
|
||||
Mezzio\Router\FastRouteRouter\ConfigProvider::class,
|
||||
class_exists(SwooleConfigProvider::class) ? SwooleConfigProvider::class : new ConfigAggregator\ArrayProvider([]),
|
||||
$enableSwoole && class_exists(Swoole\ConfigProvider::class)
|
||||
? Swoole\ConfigProvider::class
|
||||
: new ConfigAggregator\ArrayProvider([]),
|
||||
ProblemDetails\ConfigProvider::class,
|
||||
Diactoros\ConfigProvider::class,
|
||||
Common\ConfigProvider::class,
|
||||
@@ -29,11 +42,12 @@ return (new ConfigAggregator\ConfigAggregator([
|
||||
CLI\ConfigProvider::class,
|
||||
Rest\ConfigProvider::class,
|
||||
new ConfigAggregator\PhpFileProvider('config/autoload/{{,*.}global,{,*.}local}.php'),
|
||||
env('APP_ENV') === 'test'
|
||||
$isTestEnv
|
||||
? new ConfigAggregator\PhpFileProvider('config/test/*.global.php')
|
||||
: new ConfigAggregator\LaminasConfigProvider('config/params/{generated_config.php,*.config.{php,json}}'),
|
||||
: new ConfigAggregator\ArrayProvider([]),
|
||||
// Routes have to be loaded last
|
||||
new ConfigAggregator\PhpFileProvider('config/autoload/routes.config.php'),
|
||||
], 'data/cache/app_config.php', [
|
||||
Core\Config\SimplifiedConfigParser::class,
|
||||
Core\Config\BasePathPrefixer::class,
|
||||
Core\Config\DeprecatedConfigParser::class,
|
||||
Core\Config\MultiSegmentSlugProcessor::class,
|
||||
]))->getMergedConfig();
|
||||
|
||||
22
config/constants.php
Normal file
22
config/constants.php
Normal file
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Shlinkio\Shlink;
|
||||
|
||||
use Fig\Http\Message\StatusCodeInterface;
|
||||
|
||||
const DEFAULT_DELETE_SHORT_URL_THRESHOLD = 15;
|
||||
const DEFAULT_SHORT_CODES_LENGTH = 5;
|
||||
const MIN_SHORT_CODES_LENGTH = 4;
|
||||
const DEFAULT_REDIRECT_STATUS_CODE = StatusCodeInterface::STATUS_FOUND;
|
||||
const DEFAULT_REDIRECT_CACHE_LIFETIME = 30;
|
||||
const LOCAL_LOCK_FACTORY = 'Shlinkio\Shlink\LocalLockFactory';
|
||||
const TITLE_TAG_VALUE = '/<title[^>]*>(.*?)<\/title>/i'; // Matches the value inside a html title tag
|
||||
const DEFAULT_QR_CODE_SIZE = 300;
|
||||
const DEFAULT_QR_CODE_MARGIN = 0;
|
||||
const DEFAULT_QR_CODE_FORMAT = 'png';
|
||||
const DEFAULT_QR_CODE_ERROR_CORRECTION = 'l';
|
||||
const DEFAULT_QR_CODE_ROUND_BLOCK_SIZE = true;
|
||||
const MIN_TASK_WORKERS = 4;
|
||||
const MIGRATIONS_TABLE = 'migrations';
|
||||
@@ -3,14 +3,18 @@
|
||||
declare(strict_types=1);
|
||||
|
||||
use Laminas\ServiceManager\ServiceManager;
|
||||
use Shlinkio\Shlink\Core\Config\EnvVars;
|
||||
use Symfony\Component\Lock;
|
||||
|
||||
use const Shlinkio\Shlink\Core\LOCAL_LOCK_FACTORY;
|
||||
use const Shlinkio\Shlink\LOCAL_LOCK_FACTORY;
|
||||
|
||||
chdir(dirname(__DIR__));
|
||||
|
||||
require 'vendor/autoload.php';
|
||||
|
||||
// This is one of the first files loaded. Configure the timezone here
|
||||
date_default_timezone_set(EnvVars::TIMEZONE->loadFromEnv(date_default_timezone_get()));
|
||||
|
||||
// This class alias tricks the ConfigAbstractFactory to return Lock\Factory instances even with a different service name
|
||||
// It needs to be placed here as individual config files will not be loaded once config is cached
|
||||
if (! class_exists(LOCAL_LOCK_FACTORY)) {
|
||||
@@ -18,7 +22,7 @@ if (! class_exists(LOCAL_LOCK_FACTORY)) {
|
||||
}
|
||||
|
||||
// Build container
|
||||
return (function () {
|
||||
return (static function (): ServiceManager {
|
||||
$config = require __DIR__ . '/config.php';
|
||||
$container = new ServiceManager($config['dependencies']);
|
||||
$container->setService('config', $config);
|
||||
|
||||
@@ -3,9 +3,10 @@
|
||||
declare(strict_types=1);
|
||||
|
||||
use Doctrine\ORM\EntityManager;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Psr\Container\ContainerInterface;
|
||||
|
||||
return (static function () {
|
||||
return (static function (): EntityManagerInterface {
|
||||
/** @var ContainerInterface $container */
|
||||
$container = include __DIR__ . '/container.php';
|
||||
return $container->get(EntityManager::class);
|
||||
|
||||
49
config/roadrunner/.rr.dev.yml
Normal file
49
config/roadrunner/.rr.dev.yml
Normal file
@@ -0,0 +1,49 @@
|
||||
version: '2.7'
|
||||
|
||||
rpc:
|
||||
listen: tcp://127.0.0.1:6001
|
||||
|
||||
server:
|
||||
command: 'php ../../bin/roadrunner-worker.php'
|
||||
|
||||
http:
|
||||
address: '0.0.0.0:8080'
|
||||
middleware: ['static']
|
||||
static:
|
||||
dir: '../../public'
|
||||
forbid: ['.php', '.htaccess']
|
||||
pool:
|
||||
num_workers: 1
|
||||
|
||||
jobs:
|
||||
pool:
|
||||
num_workers: 1
|
||||
timeout: 300
|
||||
consume: ['shlink']
|
||||
pipelines:
|
||||
shlink:
|
||||
driver: memory
|
||||
config:
|
||||
priority: 10
|
||||
prefetch: 10
|
||||
|
||||
logs:
|
||||
mode: development
|
||||
channels:
|
||||
http:
|
||||
level: debug
|
||||
server:
|
||||
level: debug
|
||||
metrics:
|
||||
level: debug
|
||||
|
||||
reload:
|
||||
interval: 1s
|
||||
patterns: ['.php']
|
||||
services:
|
||||
http:
|
||||
dirs: ['../../bin', '../../config', '../../data/migrations', '../../module', '../../vendor']
|
||||
recursive: true
|
||||
jobs:
|
||||
dirs: ['../../bin', '../../config', '../../data/migrations', '../../module', '../../vendor']
|
||||
recursive: true
|
||||
36
config/roadrunner/.rr.yml
Normal file
36
config/roadrunner/.rr.yml
Normal file
@@ -0,0 +1,36 @@
|
||||
version: '2.7'
|
||||
|
||||
rpc:
|
||||
listen: tcp://127.0.0.1:6001
|
||||
|
||||
server:
|
||||
command: 'php -dopcache.enable_cli=1 -dopcache.validate_timestamps=0 ../../bin/roadrunner-worker.php'
|
||||
|
||||
http:
|
||||
address: '0.0.0.0:${PORT}'
|
||||
middleware: ['static']
|
||||
static:
|
||||
dir: '../../public'
|
||||
forbid: ['.php', '.htaccess']
|
||||
pool:
|
||||
num_workers: ${WEB_WORKER_NUM}
|
||||
|
||||
jobs:
|
||||
timeout: 300 # 5 minutes
|
||||
pool:
|
||||
num_workers: ${TASK_WORKER_NUM}
|
||||
consume: ['shlink']
|
||||
pipelines:
|
||||
shlink:
|
||||
driver: memory
|
||||
config:
|
||||
priority: 10
|
||||
prefetch: 10
|
||||
|
||||
logs:
|
||||
mode: production
|
||||
channels:
|
||||
http:
|
||||
level: info # Log all http requests, set to info to disable
|
||||
server:
|
||||
level: debug # Everything written to worker stderr is logged
|
||||
@@ -10,8 +10,8 @@ use Psr\Container\ContainerInterface;
|
||||
use function register_shutdown_function;
|
||||
use function sprintf;
|
||||
|
||||
use const ShlinkioTest\Shlink\SWOOLE_TESTING_HOST;
|
||||
use const ShlinkioTest\Shlink\SWOOLE_TESTING_PORT;
|
||||
use const ShlinkioTest\Shlink\API_TESTS_HOST;
|
||||
use const ShlinkioTest\Shlink\API_TESTS_PORT;
|
||||
|
||||
/** @var ContainerInterface $container */
|
||||
$container = require __DIR__ . '/../container.php';
|
||||
@@ -20,15 +20,19 @@ $config = $container->get('config');
|
||||
$em = $container->get(EntityManager::class);
|
||||
$httpClient = $container->get('shlink_test_api_client');
|
||||
|
||||
// Start code coverage collecting on swoole process, and stop it when process shuts down
|
||||
$httpClient->request('GET', sprintf('http://%s:%s/api-tests/start-coverage', SWOOLE_TESTING_HOST, SWOOLE_TESTING_PORT));
|
||||
// Dump code coverage when process shuts down
|
||||
register_shutdown_function(function () use ($httpClient): void {
|
||||
$httpClient->request(
|
||||
'GET',
|
||||
sprintf('http://%s:%s/api-tests/stop-coverage', SWOOLE_TESTING_HOST, SWOOLE_TESTING_PORT),
|
||||
sprintf('http://%s:%s/api-tests/stop-coverage', API_TESTS_HOST, API_TESTS_PORT),
|
||||
);
|
||||
});
|
||||
|
||||
$testHelper->createTestDb();
|
||||
$testHelper->createTestDb(
|
||||
['bin/cli', 'db:create'],
|
||||
['bin/cli', 'db:migrate'],
|
||||
['bin/doctrine', 'orm:schema-tool:drop'],
|
||||
['bin/doctrine', 'dbal:run-sql'],
|
||||
);
|
||||
ApiTest\ApiTestCase::setApiClient($httpClient);
|
||||
ApiTest\ApiTestCase::setSeedFixturesCallback(fn () => $testHelper->seedFixtures($em, $config['data_fixtures'] ?? []));
|
||||
|
||||
33
config/test/bootstrap_cli_tests.php
Normal file
33
config/test/bootstrap_cli_tests.php
Normal file
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Shlinkio\Shlink\TestUtils;
|
||||
|
||||
use Doctrine\ORM\EntityManager;
|
||||
use Psr\Container\ContainerInterface;
|
||||
|
||||
use function file_exists;
|
||||
use function unlink;
|
||||
|
||||
/** @var ContainerInterface $container */
|
||||
$container = require __DIR__ . '/../container.php';
|
||||
$testHelper = $container->get(Helper\TestHelper::class);
|
||||
$config = $container->get('config');
|
||||
$em = $container->get(EntityManager::class);
|
||||
|
||||
// Delete old coverage in PHP, to avoid merging older executions with current one
|
||||
$covFile = __DIR__ . '/../../build/coverage-cli.cov';
|
||||
if (file_exists($covFile)) {
|
||||
unlink($covFile);
|
||||
}
|
||||
|
||||
$testHelper->createTestDb(
|
||||
['bin/cli', 'db:create'],
|
||||
['bin/cli', 'db:migrate'],
|
||||
['bin/doctrine', 'orm:schema-tool:drop'],
|
||||
['bin/doctrine', 'dbal:run-sql'],
|
||||
);
|
||||
CliTest\CliTestCase::setSeedFixturesCallback(
|
||||
static fn () => $testHelper->seedFixtures($em, $config['data_fixtures'] ?? []),
|
||||
);
|
||||
@@ -8,5 +8,10 @@ use Psr\Container\ContainerInterface;
|
||||
|
||||
/** @var ContainerInterface $container */
|
||||
$container = require __DIR__ . '/../container.php';
|
||||
$container->get(Helper\TestHelper::class)->createTestDb();
|
||||
$container->get(Helper\TestHelper::class)->createTestDb(
|
||||
['bin/cli', 'db:create'],
|
||||
['bin/cli', 'db:migrate'],
|
||||
['bin/doctrine', 'orm:schema-tool:drop'],
|
||||
['bin/doctrine', 'dbal:run-sql'],
|
||||
);
|
||||
DbTest\DatabaseTestCase::setEntityManager($container->get('em'));
|
||||
|
||||
@@ -4,5 +4,5 @@ declare(strict_types=1);
|
||||
|
||||
namespace ShlinkioTest\Shlink;
|
||||
|
||||
const SWOOLE_TESTING_HOST = '127.0.0.1';
|
||||
const SWOOLE_TESTING_PORT = 9999;
|
||||
const API_TESTS_HOST = '127.0.0.1';
|
||||
const API_TESTS_PORT = 9999;
|
||||
|
||||
@@ -8,37 +8,77 @@ use GuzzleHttp\Client;
|
||||
use Laminas\ConfigAggregator\ConfigAggregator;
|
||||
use Laminas\Diactoros\Response\EmptyResponse;
|
||||
use Laminas\ServiceManager\Factory\InvokableFactory;
|
||||
use Laminas\Stdlib\Glob;
|
||||
use Monolog\Handler\StreamHandler;
|
||||
use Monolog\Logger;
|
||||
use League\Event\EventDispatcher;
|
||||
use Monolog\Level;
|
||||
use PHPUnit\Runner\Version;
|
||||
use Psr\Container\ContainerInterface;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Psr\Http\Server\RequestHandlerInterface;
|
||||
use SebastianBergmann\CodeCoverage\CodeCoverage;
|
||||
use SebastianBergmann\CodeCoverage\Driver\Selector;
|
||||
use SebastianBergmann\CodeCoverage\Filter;
|
||||
use SebastianBergmann\CodeCoverage\Report\Html\Facade as Html;
|
||||
use SebastianBergmann\CodeCoverage\Report\PHP;
|
||||
use SebastianBergmann\CodeCoverage\Report\Xml\Facade as Xml;
|
||||
use Shlinkio\Shlink\Common\Logger\LoggerType;
|
||||
use Symfony\Component\Console\Application;
|
||||
use Symfony\Component\Console\Event\ConsoleCommandEvent;
|
||||
use Symfony\Component\Console\Event\ConsoleTerminateEvent;
|
||||
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
|
||||
|
||||
use function file_exists;
|
||||
use function Functional\contains;
|
||||
use function Laminas\Stratigility\middleware;
|
||||
use function Shlinkio\Shlink\Common\env;
|
||||
use function Shlinkio\Shlink\Config\env;
|
||||
use function sprintf;
|
||||
use function sys_get_temp_dir;
|
||||
|
||||
use const ShlinkioTest\Shlink\SWOOLE_TESTING_HOST;
|
||||
use const ShlinkioTest\Shlink\SWOOLE_TESTING_PORT;
|
||||
use const ShlinkioTest\Shlink\API_TESTS_HOST;
|
||||
use const ShlinkioTest\Shlink\API_TESTS_PORT;
|
||||
|
||||
$isApiTest = env('TEST_ENV') === 'api';
|
||||
if ($isApiTest) {
|
||||
$isCliTest = env('TEST_ENV') === 'cli';
|
||||
$isE2eTest = $isApiTest || $isCliTest;
|
||||
$coverageType = env('GENERATE_COVERAGE');
|
||||
$generateCoverage = contains(['yes', 'pretty'], $coverageType);
|
||||
|
||||
$coverage = null;
|
||||
if ($isE2eTest && $generateCoverage) {
|
||||
$filter = new Filter();
|
||||
foreach (Glob::glob(__DIR__ . '/../../module/*/src') as $item) {
|
||||
$filter->includeDirectory($item);
|
||||
}
|
||||
$filter->includeDirectory(__DIR__ . '/../../module/Core/src');
|
||||
$filter->includeDirectory(__DIR__ . '/../../module/' . ($isApiTest ? 'Rest' : 'CLI') . '/src');
|
||||
$coverage = new CodeCoverage((new Selector())->forLineCoverage($filter), $filter);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param 'api'|'cli' $type
|
||||
*/
|
||||
$exportCoverage = static function (string $type = 'api') use (&$coverage, $coverageType): void {
|
||||
if ($coverage === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
$basePath = __DIR__ . '/../../build/coverage-' . $type;
|
||||
$covPath = $basePath . '.cov';
|
||||
|
||||
// Every CLI test runs on its own process and dumps the coverage afterwards.
|
||||
// Try to load it and merge it, so that we end up with the whole coverage at the end.
|
||||
if ($type === 'cli' && file_exists($covPath)) {
|
||||
$coverage->merge(require $covPath);
|
||||
}
|
||||
|
||||
if ($coverageType === 'pretty') {
|
||||
(new Html())->process($coverage, $basePath . '/coverage-html');
|
||||
} else {
|
||||
(new PHP())->process($coverage, $covPath);
|
||||
(new Xml(Version::getVersionString()))->process($coverage, $basePath . '/coverage-xml');
|
||||
}
|
||||
};
|
||||
|
||||
$buildDbConnection = static function (): array {
|
||||
$driver = env('DB_DRIVER', 'sqlite');
|
||||
$isCi = env('CI', false);
|
||||
$getMysqlHost = static fn (string $driver) => sprintf('shlink_db%s', $driver === 'mysql' ? '' : '_maria');
|
||||
$getCiMysqlPort = static fn (string $driver) => $driver === 'mysql' ? '3307' : '3308';
|
||||
|
||||
return match ($driver) {
|
||||
@@ -64,26 +104,20 @@ $buildDbConnection = static function (): array {
|
||||
],
|
||||
default => [ // mysql and maria
|
||||
'driver' => 'pdo_mysql',
|
||||
'host' => $isCi ? '127.0.0.1' : $getMysqlHost($driver),
|
||||
'host' => $isCi ? '127.0.0.1' : sprintf('shlink_db_%s', $driver),
|
||||
'port' => $isCi ? $getCiMysqlPort($driver) : '3306',
|
||||
'user' => 'root',
|
||||
'password' => 'root',
|
||||
'dbname' => 'shlink_test',
|
||||
'charset' => 'utf8',
|
||||
'charset' => 'utf8mb4',
|
||||
],
|
||||
};
|
||||
};
|
||||
|
||||
$buildTestLoggerConfig = fn (string $handlerName, string $filename) => [
|
||||
'handlers' => [
|
||||
$handlerName => [
|
||||
'name' => StreamHandler::class,
|
||||
'params' => [
|
||||
'level' => Logger::DEBUG,
|
||||
'stream' => sprintf('data/log/api-tests/%s', $filename),
|
||||
],
|
||||
],
|
||||
],
|
||||
$buildTestLoggerConfig = static fn (string $filename) => [
|
||||
'level' => Level::Debug->value,
|
||||
'type' => LoggerType::STREAM->value,
|
||||
'destination' => sprintf('data/log/api-tests/%s', $filename),
|
||||
];
|
||||
|
||||
return [
|
||||
@@ -96,17 +130,17 @@ return [
|
||||
'schema' => 'http',
|
||||
'hostname' => 'doma.in',
|
||||
],
|
||||
'validate_url' => true,
|
||||
],
|
||||
|
||||
'mezzio-swoole' => [
|
||||
'enable_coroutine' => false,
|
||||
'swoole-http-server' => [
|
||||
'host' => SWOOLE_TESTING_HOST,
|
||||
'port' => SWOOLE_TESTING_PORT,
|
||||
'host' => API_TESTS_HOST,
|
||||
'port' => API_TESTS_PORT,
|
||||
'process-name' => 'shlink_test',
|
||||
'options' => [
|
||||
'pid_file' => sys_get_temp_dir() . '/shlink-test-swoole.pid',
|
||||
'log_file' => __DIR__ . '/../../data/log/api-tests/output.log',
|
||||
'enable_coroutine' => false,
|
||||
],
|
||||
],
|
||||
@@ -114,30 +148,33 @@ return [
|
||||
|
||||
'routes' => !$isApiTest ? [] : [
|
||||
[
|
||||
'name' => 'start_collecting_coverage',
|
||||
'path' => '/api-tests/start-coverage',
|
||||
'middleware' => middleware(static function () use (&$coverage) {
|
||||
if ($coverage) { // @phpstan-ignore-line
|
||||
$coverage->start('API tests');
|
||||
}
|
||||
'name' => 'dump_coverage',
|
||||
'path' => '/api-tests/stop-coverage',
|
||||
'middleware' => middleware(static function () use ($exportCoverage) {
|
||||
// TODO I have tried moving this block to a listener so that it's invoked automatically,
|
||||
// but then the coverage is generated empty ¯\_(ツ)_/¯
|
||||
$exportCoverage();
|
||||
return new EmptyResponse();
|
||||
}),
|
||||
'allowed_methods' => ['GET'],
|
||||
],
|
||||
[
|
||||
'name' => 'dump_coverage',
|
||||
'path' => '/api-tests/stop-coverage',
|
||||
'middleware' => middleware(static function () use (&$coverage) {
|
||||
if ($coverage) { // @phpstan-ignore-line
|
||||
$basePath = __DIR__ . '/../../build/coverage-api';
|
||||
$coverage->stop();
|
||||
(new PHP())->process($coverage, $basePath . '.cov');
|
||||
(new Xml(Version::getVersionString()))->process($coverage, $basePath . '/coverage-xml');
|
||||
}
|
||||
],
|
||||
|
||||
return new EmptyResponse();
|
||||
'middleware_pipeline' => !$isApiTest ? [] : [
|
||||
'capture_code_coverage' => [
|
||||
'middleware' => middleware(static function (
|
||||
ServerRequestInterface $req,
|
||||
RequestHandlerInterface $handler,
|
||||
) use (&$coverage): ResponseInterface {
|
||||
$coverage?->start($req->getHeaderLine('x-coverage-id'));
|
||||
|
||||
try {
|
||||
return $handler->handle($req);
|
||||
} finally {
|
||||
$coverage?->stop();
|
||||
}
|
||||
}),
|
||||
'allowed_methods' => ['GET'],
|
||||
'priority' => 9999,
|
||||
],
|
||||
],
|
||||
|
||||
@@ -150,13 +187,69 @@ return [
|
||||
'dependencies' => [
|
||||
'services' => [
|
||||
'shlink_test_api_client' => new Client([
|
||||
'base_uri' => sprintf('http://%s:%s/', SWOOLE_TESTING_HOST, SWOOLE_TESTING_PORT),
|
||||
'base_uri' => sprintf('http://%s:%s/', API_TESTS_HOST, API_TESTS_PORT),
|
||||
'http_errors' => false,
|
||||
]),
|
||||
],
|
||||
'factories' => [
|
||||
TestUtils\Helper\TestHelper::class => InvokableFactory::class,
|
||||
],
|
||||
'delegators' => $isCliTest ? [
|
||||
Application::class => [
|
||||
static function (
|
||||
ContainerInterface $c,
|
||||
string $serviceName,
|
||||
callable $callback,
|
||||
) use (
|
||||
&$coverage,
|
||||
$exportCoverage,
|
||||
) {
|
||||
/** @var Application $app */
|
||||
$app = $callback();
|
||||
$wrappedEventDispatcher = new EventDispatcher();
|
||||
|
||||
// When the command starts, start collecting coverage
|
||||
$wrappedEventDispatcher->subscribeTo(
|
||||
ConsoleCommandEvent::class,
|
||||
static function () use (&$coverage): void {
|
||||
$id = env('COVERAGE_ID');
|
||||
if ($id === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
$coverage?->start($id);
|
||||
},
|
||||
);
|
||||
// When the command ends, stop collecting coverage
|
||||
$wrappedEventDispatcher->subscribeTo(
|
||||
ConsoleTerminateEvent::class,
|
||||
static function () use (&$coverage, $exportCoverage): void {
|
||||
$id = env('COVERAGE_ID');
|
||||
if ($id === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
$coverage?->stop();
|
||||
$exportCoverage('cli');
|
||||
},
|
||||
);
|
||||
|
||||
$app->setDispatcher(new class ($wrappedEventDispatcher) implements EventDispatcherInterface {
|
||||
public function __construct(private EventDispatcher $wrappedDispatcher)
|
||||
{
|
||||
}
|
||||
|
||||
public function dispatch(object $event, ?string $eventName = null): object
|
||||
{
|
||||
$this->wrappedDispatcher->dispatch($event);
|
||||
return $event;
|
||||
}
|
||||
});
|
||||
|
||||
return $app;
|
||||
},
|
||||
],
|
||||
] : [],
|
||||
],
|
||||
|
||||
'entity_manager' => [
|
||||
@@ -165,13 +258,14 @@ return [
|
||||
|
||||
'data_fixtures' => [
|
||||
'paths' => [
|
||||
// TODO These are used for CLI tests too, so maybe should be somewhere else
|
||||
__DIR__ . '/../../module/Rest/test-api/Fixtures',
|
||||
],
|
||||
],
|
||||
|
||||
'logger' => [
|
||||
'Shlink' => $buildTestLoggerConfig('shlink_handler', 'shlink.log'),
|
||||
'Access' => $buildTestLoggerConfig('access_handler', 'access.log'),
|
||||
'Shlink' => $buildTestLoggerConfig('shlink.log'),
|
||||
'Access' => $buildTestLoggerConfig('access.log'),
|
||||
],
|
||||
|
||||
];
|
||||
|
||||
@@ -11,7 +11,7 @@ server {
|
||||
|
||||
location ~ \.php$ {
|
||||
fastcgi_split_path_info ^(.+\.php)(/.+)$;
|
||||
fastcgi_pass unix:/var/run/php/php8.0-fpm.sock;
|
||||
fastcgi_pass unix:/var/run/php/php8.1-fpm.sock;
|
||||
fastcgi_index index.php;
|
||||
include fastcgi.conf;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/var/log/shlink/shlink_swoole.log {
|
||||
/var/log/shlink/shlink_openswoole.log {
|
||||
su root root
|
||||
daily
|
||||
missingok
|
||||
@@ -8,6 +8,6 @@
|
||||
notifempty
|
||||
create 0640 root root
|
||||
postrotate
|
||||
/etc/init.d/shlink_swoole restart
|
||||
/etc/init.d/shlink_openswoole restart
|
||||
endscript
|
||||
}
|
||||
|
||||
@@ -1,26 +1,26 @@
|
||||
#!/bin/bash
|
||||
### BEGIN INIT INFO
|
||||
# Provides: shlink_swoole
|
||||
# Provides: shlink_openswoole
|
||||
# Required-Start: $local_fs $network $named $time $syslog
|
||||
# Required-Stop: $local_fs $network $named $time $syslog
|
||||
# Default-Start: 2 3 4 5
|
||||
# Default-Stop: 0 1 6
|
||||
# Description: Shlink non-blocking server with swoole
|
||||
# Description: Shlink non-blocking server with openswoole
|
||||
### END INIT INFO
|
||||
|
||||
SCRIPT=/path/to/shlink/vendor/bin/laminas\ mezzio:swoole:start
|
||||
RUNAS=root
|
||||
|
||||
PIDFILE=/var/run/shlink_swoole.pid
|
||||
PIDFILE=/var/run/shlink_openswoole.pid
|
||||
LOGDIR=/var/log/shlink
|
||||
LOGFILE=${LOGDIR}/shlink_swoole.log
|
||||
LOGFILE=${LOGDIR}/shlink_openswoole.log
|
||||
|
||||
start() {
|
||||
if [[ -f "$PIDFILE" ]] && kill -0 $(cat "$PIDFILE"); then
|
||||
echo 'Shlink with swoole already running' >&2
|
||||
echo 'Shlink with openswoole already running' >&2
|
||||
return 1
|
||||
fi
|
||||
echo 'Starting shlink with swoole' >&2
|
||||
echo 'Starting shlink with openswoole' >&2
|
||||
mkdir -p "$LOGDIR"
|
||||
touch "$LOGFILE"
|
||||
local CMD="$SCRIPT &> \"$LOGFILE\" & echo \$!"
|
||||
@@ -30,10 +30,10 @@ start() {
|
||||
|
||||
stop() {
|
||||
if [[ ! -f "$PIDFILE" ]] || ! kill -0 $(cat "$PIDFILE"); then
|
||||
echo 'Shlink with swoole not running' >&2
|
||||
echo 'Shlink with openswoole not running' >&2
|
||||
return 1
|
||||
fi
|
||||
echo 'Stopping shlink with swoole' >&2
|
||||
echo 'Stopping shlink with openswoole' >&2
|
||||
kill -15 $(cat "$PIDFILE") && rm -f "$PIDFILE"
|
||||
echo 'Shlink stopped' >&2
|
||||
}
|
||||
|
||||
@@ -1,15 +1,14 @@
|
||||
FROM php:8.0.9-fpm-alpine3.14
|
||||
FROM php:8.1.9-fpm-alpine3.16
|
||||
MAINTAINER Alejandro Celaya <alejandro@alejandrocelaya.com>
|
||||
|
||||
ENV APCU_VERSION 5.1.20
|
||||
ENV PDO_SQLSRV_VERSION 5.9.0
|
||||
ENV MS_ODBC_SQL_VERSION 17.5.2.1
|
||||
ENV APCU_VERSION 5.1.21
|
||||
ENV PDO_SQLSRV_VERSION 5.10.1
|
||||
ENV MS_ODBC_SQL_VERSION 17.5.2.2
|
||||
|
||||
RUN apk update
|
||||
|
||||
# Install common php extensions
|
||||
RUN docker-php-ext-install pdo_mysql
|
||||
RUN docker-php-ext-install iconv
|
||||
RUN docker-php-ext-install calendar
|
||||
|
||||
RUN apk add --no-cache oniguruma-dev
|
||||
@@ -31,8 +30,8 @@ RUN docker-php-ext-install gd
|
||||
RUN apk add --no-cache postgresql-dev
|
||||
RUN docker-php-ext-install pdo_pgsql
|
||||
|
||||
RUN apk add --no-cache gmp-dev
|
||||
RUN docker-php-ext-install gmp
|
||||
RUN docker-php-ext-install sockets
|
||||
RUN docker-php-ext-install bcmath
|
||||
|
||||
# Install APCu extension
|
||||
ADD https://pecl.php.net/get/apcu-$APCU_VERSION.tgz /tmp/apcu.tar.gz
|
||||
|
||||
73
data/infra/roadrunner.Dockerfile
Normal file
73
data/infra/roadrunner.Dockerfile
Normal file
@@ -0,0 +1,73 @@
|
||||
FROM php:8.1.9-alpine3.16
|
||||
MAINTAINER Alejandro Celaya <alejandro@alejandrocelaya.com>
|
||||
|
||||
ENV APCU_VERSION 5.1.21
|
||||
ENV PDO_SQLSRV_VERSION 5.10.1
|
||||
ENV MS_ODBC_SQL_VERSION 17.5.2.2
|
||||
|
||||
RUN apk update
|
||||
|
||||
# Install common php extensions
|
||||
RUN docker-php-ext-install pdo_mysql
|
||||
RUN docker-php-ext-install calendar
|
||||
|
||||
RUN apk add --no-cache oniguruma-dev
|
||||
RUN docker-php-ext-install mbstring
|
||||
|
||||
RUN apk add --no-cache sqlite-libs
|
||||
RUN apk add --no-cache sqlite-dev
|
||||
RUN docker-php-ext-install pdo_sqlite
|
||||
|
||||
RUN apk add --no-cache icu-dev
|
||||
RUN docker-php-ext-install intl
|
||||
|
||||
RUN apk add --no-cache libzip-dev zlib-dev
|
||||
RUN docker-php-ext-install zip
|
||||
|
||||
RUN apk add --no-cache libpng-dev
|
||||
RUN docker-php-ext-install gd
|
||||
|
||||
RUN apk add --no-cache postgresql-dev
|
||||
RUN docker-php-ext-install pdo_pgsql
|
||||
|
||||
RUN docker-php-ext-install sockets
|
||||
RUN docker-php-ext-install bcmath
|
||||
|
||||
# Install APCu extension
|
||||
ADD https://pecl.php.net/get/apcu-$APCU_VERSION.tgz /tmp/apcu.tar.gz
|
||||
RUN mkdir -p /usr/src/php/ext/apcu \
|
||||
&& tar xf /tmp/apcu.tar.gz -C /usr/src/php/ext/apcu --strip-components=1 \
|
||||
&& docker-php-ext-configure apcu \
|
||||
&& docker-php-ext-install apcu \
|
||||
&& rm /tmp/apcu.tar.gz \
|
||||
&& rm /usr/local/etc/php/conf.d/docker-php-ext-apcu.ini \
|
||||
&& echo extension=apcu.so > /usr/local/etc/php/conf.d/20-php-ext-apcu.ini
|
||||
|
||||
# Install pcov and sqlsrv driver
|
||||
RUN wget https://download.microsoft.com/download/e/4/e/e4e67866-dffd-428c-aac7-8d28ddafb39b/msodbcsql17_${MS_ODBC_SQL_VERSION}-1_amd64.apk && \
|
||||
apk add --allow-untrusted msodbcsql17_${MS_ODBC_SQL_VERSION}-1_amd64.apk && \
|
||||
apk add --no-cache --virtual .phpize-deps $PHPIZE_DEPS unixodbc-dev && \
|
||||
pecl install pdo_sqlsrv-${PDO_SQLSRV_VERSION} pcov && \
|
||||
docker-php-ext-enable pdo_sqlsrv pcov && \
|
||||
apk del .phpize-deps && \
|
||||
rm msodbcsql17_${MS_ODBC_SQL_VERSION}-1_amd64.apk
|
||||
|
||||
# Install composer
|
||||
COPY --from=composer:2 /usr/bin/composer /usr/local/bin/composer
|
||||
|
||||
# Make home directory writable by anyone
|
||||
RUN chmod 777 /home
|
||||
|
||||
VOLUME /home/shlink
|
||||
WORKDIR /home/shlink
|
||||
|
||||
# Expose roadrunner port
|
||||
EXPOSE 8080
|
||||
|
||||
CMD \
|
||||
# Install dependencies if the vendor dir does not exist
|
||||
if [[ ! -d "./vendor" ]]; then /usr/local/bin/composer install ; fi && \
|
||||
# Download roadrunner binary
|
||||
if [[ ! -f "./bin/rr" ]]; then ./vendor/bin/rr get --no-interaction --location bin/ && chmod +x bin/rr ; fi && \
|
||||
# This forces the app to be started every second until the exit code is 0
|
||||
until ./bin/rr serve -c config/roadrunner/.rr.dev.yml; do sleep 1 ; done
|
||||
@@ -1,17 +1,16 @@
|
||||
FROM php:8.0.9-alpine3.14
|
||||
FROM php:8.1.9-alpine3.16
|
||||
MAINTAINER Alejandro Celaya <alejandro@alejandrocelaya.com>
|
||||
|
||||
ENV APCU_VERSION 5.1.20
|
||||
ENV PDO_SQLSRV_VERSION 5.9.0
|
||||
ENV APCU_VERSION 5.1.21
|
||||
ENV INOTIFY_VERSION 3.0.0
|
||||
ENV SWOOLE_VERSION 4.7.0
|
||||
ENV MS_ODBC_SQL_VERSION 17.5.2.1
|
||||
ENV OPENSWOOLE_VERSION 4.11.1
|
||||
ENV PDO_SQLSRV_VERSION 5.10.1
|
||||
ENV MS_ODBC_SQL_VERSION 17.5.2.2
|
||||
|
||||
RUN apk update
|
||||
|
||||
# Install common php extensions
|
||||
RUN docker-php-ext-install pdo_mysql
|
||||
RUN docker-php-ext-install iconv
|
||||
RUN docker-php-ext-install calendar
|
||||
|
||||
RUN apk add --no-cache oniguruma-dev
|
||||
@@ -33,8 +32,8 @@ RUN docker-php-ext-install gd
|
||||
RUN apk add --no-cache postgresql-dev
|
||||
RUN docker-php-ext-install pdo_pgsql
|
||||
|
||||
RUN apk add --no-cache gmp-dev
|
||||
RUN docker-php-ext-install gmp
|
||||
RUN docker-php-ext-install sockets
|
||||
RUN docker-php-ext-install bcmath
|
||||
|
||||
# Install APCu extension
|
||||
ADD https://pecl.php.net/get/apcu-$APCU_VERSION.tgz /tmp/apcu.tar.gz
|
||||
@@ -54,12 +53,12 @@ RUN mkdir -p /usr/src/php/ext/inotify \
|
||||
&& docker-php-ext-install inotify \
|
||||
&& rm /tmp/inotify.tar.gz
|
||||
|
||||
# Install swoole, pcov and mssql driver
|
||||
# Install openswoole, pcov and mssql driver
|
||||
RUN wget https://download.microsoft.com/download/e/4/e/e4e67866-dffd-428c-aac7-8d28ddafb39b/msodbcsql17_${MS_ODBC_SQL_VERSION}-1_amd64.apk && \
|
||||
apk add --allow-untrusted msodbcsql17_${MS_ODBC_SQL_VERSION}-1_amd64.apk && \
|
||||
apk add --no-cache --virtual .phpize-deps $PHPIZE_DEPS unixodbc-dev && \
|
||||
pecl install swoole-${SWOOLE_VERSION} pdo_sqlsrv-${PDO_SQLSRV_VERSION} pcov && \
|
||||
docker-php-ext-enable swoole pdo_sqlsrv pcov && \
|
||||
pecl install openswoole-${OPENSWOOLE_VERSION} pdo_sqlsrv-${PDO_SQLSRV_VERSION} pcov && \
|
||||
docker-php-ext-enable openswoole pdo_sqlsrv pcov && \
|
||||
apk del .phpize-deps && \
|
||||
rm msodbcsql17_${MS_ODBC_SQL_VERSION}-1_amd64.apk
|
||||
|
||||
@@ -72,12 +71,12 @@ RUN chmod 777 /home
|
||||
VOLUME /home/shlink
|
||||
WORKDIR /home/shlink
|
||||
|
||||
# Expose swoole port
|
||||
# Expose openswoole port
|
||||
EXPOSE 8080
|
||||
|
||||
CMD \
|
||||
# Install dependencies if the vendor dir does not exist
|
||||
if [[ ! -d "./vendor" ]]; then /usr/local/bin/composer install ; fi && \
|
||||
# When restarting the container, swoole might think it is already in execution
|
||||
# When restarting the container, openswoole might think it is already in execution
|
||||
# This forces the app to be started every second until the exit code is 0
|
||||
until php ./vendor/bin/laminas mezzio:swoole:start; do sleep 1 ; done
|
||||
|
||||
@@ -5,40 +5,45 @@ declare(strict_types=1);
|
||||
namespace ShlinkMigrations;
|
||||
|
||||
use Doctrine\DBAL\Exception;
|
||||
use Doctrine\DBAL\Platforms\MySQLPlatform;
|
||||
use Doctrine\DBAL\Platforms\SqlitePlatform;
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\DBAL\Schema\SchemaException;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
|
||||
use function is_subclass_of;
|
||||
|
||||
/**
|
||||
* Auto-generated Migration: Please modify to your needs!
|
||||
*/
|
||||
class Version20160819142757 extends AbstractMigration
|
||||
{
|
||||
private const MYSQL = 'mysql';
|
||||
private const SQLITE = 'sqlite';
|
||||
|
||||
/**
|
||||
* @throws Exception
|
||||
* @throws SchemaException
|
||||
*/
|
||||
public function up(Schema $schema): void
|
||||
{
|
||||
$db = $this->connection->getDatabasePlatform()->getName();
|
||||
$platformClass = $this->connection->getDatabasePlatform();
|
||||
$table = $schema->getTable('short_urls');
|
||||
$column = $table->getColumn('short_code');
|
||||
|
||||
if ($db === self::MYSQL) {
|
||||
$column->setPlatformOption('collation', 'utf8_bin');
|
||||
} elseif ($db === self::SQLITE) {
|
||||
$column->setPlatformOption('collate', 'BINARY');
|
||||
}
|
||||
match (true) {
|
||||
is_subclass_of($platformClass, MySQLPlatform::class) => $column
|
||||
->setPlatformOption('charset', 'utf8mb4')
|
||||
->setPlatformOption('collation', 'utf8mb4_bin'),
|
||||
is_subclass_of($platformClass, SqlitePlatform::class) => $column->setPlatformOption('collate', 'BINARY'),
|
||||
default => null,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws Exception
|
||||
*/
|
||||
public function down(Schema $schema): void
|
||||
{
|
||||
$db = $this->connection->getDatabasePlatform()->getName();
|
||||
// Nothing to roll back
|
||||
}
|
||||
|
||||
public function isTransactional(): bool
|
||||
{
|
||||
return ! ($this->connection->getDatabasePlatform() instanceof MySQLPlatform);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace ShlinkMigrations;
|
||||
|
||||
use Doctrine\DBAL\Platforms\MySQLPlatform;
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\DBAL\Types\Types;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
@@ -73,4 +74,9 @@ class Version20160820191203 extends AbstractMigration
|
||||
$schema->dropTable('short_urls_in_tags');
|
||||
$schema->dropTable('tags');
|
||||
}
|
||||
|
||||
public function isTransactional(): bool
|
||||
{
|
||||
return ! ($this->connection->getDatabasePlatform() instanceof MySQLPlatform);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace ShlinkMigrations;
|
||||
|
||||
use Doctrine\DBAL\Platforms\MySQLPlatform;
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\DBAL\Schema\SchemaException;
|
||||
use Doctrine\DBAL\Types\Types;
|
||||
@@ -45,4 +46,9 @@ class Version20171021093246 extends AbstractMigration
|
||||
$shortUrls->dropColumn('valid_since');
|
||||
$shortUrls->dropColumn('valid_until');
|
||||
}
|
||||
|
||||
public function isTransactional(): bool
|
||||
{
|
||||
return ! ($this->connection->getDatabasePlatform() instanceof MySQLPlatform);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace ShlinkMigrations;
|
||||
|
||||
use Doctrine\DBAL\Platforms\MySQLPlatform;
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\DBAL\Schema\SchemaException;
|
||||
use Doctrine\DBAL\Types\Types;
|
||||
@@ -42,4 +43,9 @@ class Version20171022064541 extends AbstractMigration
|
||||
|
||||
$shortUrls->dropColumn('max_visits');
|
||||
}
|
||||
|
||||
public function isTransactional(): bool
|
||||
{
|
||||
return ! ($this->connection->getDatabasePlatform() instanceof MySQLPlatform);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace ShlinkMigrations;
|
||||
|
||||
use Doctrine\DBAL\Platforms\MySQLPlatform;
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\DBAL\Schema\SchemaException;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
@@ -39,4 +40,9 @@ final class Version20180801183328 extends AbstractMigration
|
||||
{
|
||||
$schema->getTable('short_urls')->getColumn('short_code')->setLength($size);
|
||||
}
|
||||
|
||||
public function isTransactional(): bool
|
||||
{
|
||||
return ! ($this->connection->getDatabasePlatform() instanceof MySQLPlatform);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ declare(strict_types=1);
|
||||
namespace ShlinkMigrations;
|
||||
|
||||
use Doctrine\DBAL\Exception;
|
||||
use Doctrine\DBAL\Platforms\MySQLPlatform;
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
use PDO;
|
||||
@@ -66,4 +67,9 @@ final class Version20180913205455 extends AbstractMigration
|
||||
{
|
||||
// Nothing to rollback
|
||||
}
|
||||
|
||||
public function isTransactional(): bool
|
||||
{
|
||||
return ! ($this->connection->getDatabasePlatform() instanceof MySQLPlatform);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace ShlinkMigrations;
|
||||
|
||||
use Doctrine\DBAL\Platforms\MySQLPlatform;
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\DBAL\Schema\SchemaException;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
@@ -47,4 +48,9 @@ final class Version20180915110857 extends AbstractMigration
|
||||
{
|
||||
// Nothing to run
|
||||
}
|
||||
|
||||
public function isTransactional(): bool
|
||||
{
|
||||
return ! ($this->connection->getDatabasePlatform() instanceof MySQLPlatform);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ declare(strict_types=1);
|
||||
namespace ShlinkMigrations;
|
||||
|
||||
use Doctrine\DBAL\Exception;
|
||||
use Doctrine\DBAL\Platforms\MySQLPlatform;
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\DBAL\Schema\SchemaException;
|
||||
use Doctrine\DBAL\Schema\Table;
|
||||
@@ -58,11 +59,16 @@ final class Version20181020060559 extends AbstractMigration
|
||||
foreach (self::COLUMNS as $camelCaseName => $snakeCaseName) {
|
||||
$qb->set($snakeCaseName, $camelCaseName);
|
||||
}
|
||||
$qb->execute();
|
||||
$qb->executeStatement();
|
||||
}
|
||||
|
||||
public function down(Schema $schema): void
|
||||
{
|
||||
// No down
|
||||
}
|
||||
|
||||
public function isTransactional(): bool
|
||||
{
|
||||
return ! ($this->connection->getDatabasePlatform() instanceof MySQLPlatform);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace ShlinkMigrations;
|
||||
|
||||
use Doctrine\DBAL\Platforms\MySQLPlatform;
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\DBAL\Schema\SchemaException;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
@@ -38,4 +39,9 @@ final class Version20181020065148 extends AbstractMigration
|
||||
{
|
||||
// No down
|
||||
}
|
||||
|
||||
public function isTransactional(): bool
|
||||
{
|
||||
return ! ($this->connection->getDatabasePlatform() instanceof MySQLPlatform);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace ShlinkMigrations;
|
||||
|
||||
use Doctrine\DBAL\Platforms\MySQLPlatform;
|
||||
use Doctrine\DBAL\Schema\Column;
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\DBAL\Schema\SchemaException;
|
||||
@@ -34,4 +35,9 @@ final class Version20181110175521 extends AbstractMigration
|
||||
{
|
||||
return $schema->getTable('visits')->getColumn('user_agent');
|
||||
}
|
||||
|
||||
public function isTransactional(): bool
|
||||
{
|
||||
return ! ($this->connection->getDatabasePlatform() instanceof MySQLPlatform);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace ShlinkMigrations;
|
||||
|
||||
use Doctrine\DBAL\Platforms\MySQLPlatform;
|
||||
use Doctrine\DBAL\Schema\Column;
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\DBAL\Schema\SchemaException;
|
||||
@@ -34,4 +35,9 @@ final class Version20190824075137 extends AbstractMigration
|
||||
{
|
||||
return $schema->getTable('visits')->getColumn('referer');
|
||||
}
|
||||
|
||||
public function isTransactional(): bool
|
||||
{
|
||||
return ! ($this->connection->getDatabasePlatform() instanceof MySQLPlatform);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace ShlinkMigrations;
|
||||
|
||||
use Doctrine\DBAL\Platforms\MySQLPlatform;
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\DBAL\Schema\SchemaException;
|
||||
use Doctrine\DBAL\Types\Types;
|
||||
@@ -52,4 +53,9 @@ final class Version20190930165521 extends AbstractMigration
|
||||
$schema->getTable('short_urls')->dropColumn('domain_id');
|
||||
$schema->dropTable('domains');
|
||||
}
|
||||
|
||||
public function isTransactional(): bool
|
||||
{
|
||||
return ! ($this->connection->getDatabasePlatform() instanceof MySQLPlatform);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace ShlinkMigrations;
|
||||
|
||||
use Doctrine\DBAL\Platforms\MySQLPlatform;
|
||||
use Doctrine\DBAL\Schema\Index;
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\DBAL\Schema\SchemaException;
|
||||
@@ -46,4 +47,9 @@ final class Version20191001201532 extends AbstractMigration
|
||||
$shortUrls->dropIndex('unique_short_code_plus_domain');
|
||||
$shortUrls->addUniqueIndex(['short_code']);
|
||||
}
|
||||
|
||||
public function isTransactional(): bool
|
||||
{
|
||||
return ! ($this->connection->getDatabasePlatform() instanceof MySQLPlatform);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace ShlinkMigrations;
|
||||
|
||||
use Doctrine\DBAL\Platforms\MySQLPlatform;
|
||||
use Doctrine\DBAL\Schema\Column;
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\DBAL\Schema\SchemaException;
|
||||
@@ -34,4 +35,9 @@ final class Version20191020074522 extends AbstractMigration
|
||||
{
|
||||
return $schema->getTable('short_urls')->getColumn('original_url');
|
||||
}
|
||||
|
||||
public function isTransactional(): bool
|
||||
{
|
||||
return ! ($this->connection->getDatabasePlatform() instanceof MySQLPlatform);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,8 @@ declare(strict_types=1);
|
||||
namespace ShlinkMigrations;
|
||||
|
||||
use Doctrine\DBAL\Exception;
|
||||
use Doctrine\DBAL\Platforms\MySQLPlatform;
|
||||
use Doctrine\DBAL\Platforms\PostgreSQLPlatform;
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\DBAL\Types\Types;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
@@ -38,7 +40,7 @@ final class Version20200105165647 extends AbstractMigration
|
||||
'zeroValue' => '0',
|
||||
'emptyString' => '',
|
||||
])
|
||||
->execute();
|
||||
->executeStatement();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,14 +63,14 @@ final class Version20200105165647 extends AbstractMigration
|
||||
*/
|
||||
public function postUp(Schema $schema): void
|
||||
{
|
||||
$platformName = $this->connection->getDatabasePlatform()->getName();
|
||||
$castType = $platformName === 'postgres' ? 'DOUBLE PRECISION' : 'DECIMAL(9,2)';
|
||||
$isPostgres = $this->connection->getDatabasePlatform() instanceof PostgreSQLPlatform;
|
||||
$castType = $isPostgres ? 'DOUBLE PRECISION' : 'DECIMAL(9,2)';
|
||||
|
||||
foreach (self::COLUMNS as $newName => $oldName) {
|
||||
$qb = $this->connection->createQueryBuilder();
|
||||
$qb->update('visit_locations')
|
||||
->set($newName, 'CAST(' . $oldName . ' AS ' . $castType . ')')
|
||||
->execute();
|
||||
->executeStatement();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,7 +80,7 @@ final class Version20200105165647 extends AbstractMigration
|
||||
$qb = $this->connection->createQueryBuilder();
|
||||
$qb->update('visit_locations')
|
||||
->set($oldName, $newName)
|
||||
->execute();
|
||||
->executeStatement();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -93,4 +95,9 @@ final class Version20200105165647 extends AbstractMigration
|
||||
$visitLocations->dropColumn($colName);
|
||||
}
|
||||
}
|
||||
|
||||
public function isTransactional(): bool
|
||||
{
|
||||
return ! ($this->connection->getDatabasePlatform() instanceof MySQLPlatform);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ declare(strict_types=1);
|
||||
namespace ShlinkMigrations;
|
||||
|
||||
use Doctrine\DBAL\Exception;
|
||||
use Doctrine\DBAL\Platforms\MySQLPlatform;
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\DBAL\Types\Types;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
@@ -44,4 +45,9 @@ final class Version20200106215144 extends AbstractMigration
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
public function isTransactional(): bool
|
||||
{
|
||||
return ! ($this->connection->getDatabasePlatform() instanceof MySQLPlatform);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,8 @@ declare(strict_types=1);
|
||||
|
||||
namespace ShlinkMigrations;
|
||||
|
||||
use Doctrine\DBAL\Exception;
|
||||
use Doctrine\DBAL\Platforms\MySQLPlatform;
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
|
||||
@@ -36,6 +38,9 @@ final class Version20200110182849 extends AbstractMigration
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws Exception
|
||||
*/
|
||||
public function setDefaultValueForColumnInTable(string $tableName, string $columnName): void
|
||||
{
|
||||
$qb = $this->connection->createQueryBuilder();
|
||||
@@ -43,11 +48,16 @@ final class Version20200110182849 extends AbstractMigration
|
||||
->set($columnName, ':emptyValue')
|
||||
->setParameter('emptyValue', self::DEFAULT_EMPTY_VALUE)
|
||||
->where($qb->expr()->isNull($columnName))
|
||||
->execute();
|
||||
->executeStatement();
|
||||
}
|
||||
|
||||
public function down(Schema $schema): void
|
||||
{
|
||||
// No need (and no way) to undo this migration
|
||||
}
|
||||
|
||||
public function isTransactional(): bool
|
||||
{
|
||||
return ! ($this->connection->getDatabasePlatform() instanceof MySQLPlatform);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace ShlinkMigrations;
|
||||
|
||||
use Doctrine\DBAL\Platforms\MySQLPlatform;
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\DBAL\Types\Types;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
@@ -32,7 +33,7 @@ final class Version20200323190014 extends AbstractMigration
|
||||
->andWhere($qb->expr()->eq('lon', 0))
|
||||
->setParameter('isEmpty', true)
|
||||
->setParameter('emptyString', '')
|
||||
->execute();
|
||||
->executeStatement();
|
||||
}
|
||||
|
||||
public function down(Schema $schema): void
|
||||
@@ -42,4 +43,9 @@ final class Version20200323190014 extends AbstractMigration
|
||||
|
||||
$visitLocations->dropColumn('is_empty');
|
||||
}
|
||||
|
||||
public function isTransactional(): bool
|
||||
{
|
||||
return ! ($this->connection->getDatabasePlatform() instanceof MySQLPlatform);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace ShlinkMigrations;
|
||||
|
||||
use Doctrine\DBAL\Platforms\MySQLPlatform;
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
|
||||
@@ -24,4 +25,9 @@ final class Version20200503170404 extends AbstractMigration
|
||||
$this->skipIf(! $visits->hasIndex(self::INDEX_NAME));
|
||||
$visits->dropIndex(self::INDEX_NAME);
|
||||
}
|
||||
|
||||
public function isTransactional(): bool
|
||||
{
|
||||
return ! ($this->connection->getDatabasePlatform() instanceof MySQLPlatform);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace ShlinkMigrations;
|
||||
|
||||
use Doctrine\DBAL\Platforms\MySQLPlatform;
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\DBAL\Types\Types;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
@@ -41,4 +42,9 @@ final class Version20201023090929 extends AbstractMigration
|
||||
$shortUrls->dropColumn('import_original_short_code');
|
||||
$shortUrls->dropIndex('unique_imports');
|
||||
}
|
||||
|
||||
public function isTransactional(): bool
|
||||
{
|
||||
return ! ($this->connection->getDatabasePlatform() instanceof MySQLPlatform);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ namespace ShlinkMigrations;
|
||||
|
||||
use Cake\Chronos\Chronos;
|
||||
use Doctrine\DBAL\Driver\Result;
|
||||
use Doctrine\DBAL\Platforms\MySQLPlatform;
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\DBAL\Types\Types;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
@@ -60,10 +61,7 @@ final class Version20201102113208 extends AbstractMigration
|
||||
->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string|int|null
|
||||
*/
|
||||
private function resolveOneApiKeyId(Result $result)
|
||||
private function resolveOneApiKeyId(Result $result): string|int|null
|
||||
{
|
||||
$results = [];
|
||||
while ($row = $result->fetchAssociative()) {
|
||||
@@ -86,4 +84,9 @@ final class Version20201102113208 extends AbstractMigration
|
||||
$shortUrls->removeForeignKey('FK_' . self::API_KEY_COLUMN);
|
||||
$shortUrls->dropColumn(self::API_KEY_COLUMN);
|
||||
}
|
||||
|
||||
public function isTransactional(): bool
|
||||
{
|
||||
return ! ($this->connection->getDatabasePlatform() instanceof MySQLPlatform);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace ShlinkMigrations;
|
||||
|
||||
use Doctrine\DBAL\Platforms\MySQLPlatform;
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\DBAL\Types\Types;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
@@ -49,4 +50,9 @@ final class Version20210102174433 extends AbstractMigration
|
||||
$schema->getTable(self::TABLE_NAME)->dropIndex('UQ_role_plus_api_key');
|
||||
$schema->dropTable(self::TABLE_NAME);
|
||||
}
|
||||
|
||||
public function isTransactional(): bool
|
||||
{
|
||||
return ! ($this->connection->getDatabasePlatform() instanceof MySQLPlatform);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace ShlinkMigrations;
|
||||
|
||||
use Doctrine\DBAL\Platforms\MySQLPlatform;
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
|
||||
@@ -23,4 +24,9 @@ final class Version20210118153932 extends AbstractMigration
|
||||
public function down(Schema $schema): void
|
||||
{
|
||||
}
|
||||
|
||||
public function isTransactional(): bool
|
||||
{
|
||||
return ! ($this->connection->getDatabasePlatform() instanceof MySQLPlatform);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace ShlinkMigrations;
|
||||
|
||||
use Doctrine\DBAL\Platforms\MySQLPlatform;
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\DBAL\Types\Types;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
@@ -33,4 +34,9 @@ final class Version20210202181026 extends AbstractMigration
|
||||
$shortUrls->dropColumn(self::TITLE);
|
||||
$shortUrls->dropColumn('title_was_auto_resolved');
|
||||
}
|
||||
|
||||
public function isTransactional(): bool
|
||||
{
|
||||
return ! ($this->connection->getDatabasePlatform() instanceof MySQLPlatform);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,11 +4,12 @@ declare(strict_types=1);
|
||||
|
||||
namespace ShlinkMigrations;
|
||||
|
||||
use Doctrine\DBAL\Platforms\MySQLPlatform;
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\DBAL\Types\Types;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
use Shlinkio\Shlink\Core\Entity\Visit;
|
||||
use Shlinkio\Shlink\Core\Model\Visitor;
|
||||
use Shlinkio\Shlink\Core\Visit\Model\Visitor;
|
||||
use Shlinkio\Shlink\Core\Visit\Model\VisitType;
|
||||
|
||||
final class Version20210207100807 extends AbstractMigration
|
||||
{
|
||||
@@ -26,7 +27,7 @@ final class Version20210207100807 extends AbstractMigration
|
||||
]);
|
||||
$visits->addColumn('type', Types::STRING, [
|
||||
'length' => 255,
|
||||
'default' => Visit::TYPE_VALID_SHORT_URL,
|
||||
'default' => VisitType::VALID_SHORT_URL->value,
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -40,4 +41,9 @@ final class Version20210207100807 extends AbstractMigration
|
||||
$visits->dropColumn('visited_url');
|
||||
$visits->dropColumn('type');
|
||||
}
|
||||
|
||||
public function isTransactional(): bool
|
||||
{
|
||||
return ! ($this->connection->getDatabasePlatform() instanceof MySQLPlatform);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace ShlinkMigrations;
|
||||
|
||||
use Doctrine\DBAL\Platforms\MySQLPlatform;
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\DBAL\Types\Types;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
@@ -34,4 +35,9 @@ final class Version20210306165711 extends AbstractMigration
|
||||
|
||||
$apiKeys->dropColumn(self::COLUMN);
|
||||
}
|
||||
|
||||
public function isTransactional(): bool
|
||||
{
|
||||
return ! ($this->connection->getDatabasePlatform() instanceof MySQLPlatform);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace ShlinkMigrations;
|
||||
|
||||
use Doctrine\DBAL\Platforms\MySQLPlatform;
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\DBAL\Types\Types;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
@@ -23,4 +24,9 @@ final class Version20210522051601 extends AbstractMigration
|
||||
$this->skipIf(! $shortUrls->hasColumn('crawlable'));
|
||||
$shortUrls->dropColumn('crawlable');
|
||||
}
|
||||
|
||||
public function isTransactional(): bool
|
||||
{
|
||||
return ! ($this->connection->getDatabasePlatform() instanceof MySQLPlatform);
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user