mirror of
https://github.com/shlinkio/shlink.git
synced 2026-02-28 20:23:12 +08:00
Compare commits
926 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7fcc4ebd57 | ||
|
|
b246815529 | ||
|
|
ad1334f289 | ||
|
|
49bccf9a06 | ||
|
|
1a8bf54e8b | ||
|
|
96bb0321eb | ||
|
|
37f0abf86f | ||
|
|
f9119a38b3 | ||
|
|
8465a9da31 | ||
|
|
b6b0d09647 | ||
|
|
3d2932782d | ||
|
|
fa2fede604 | ||
|
|
3aded3bc5f | ||
|
|
2d4cc912b7 | ||
|
|
c0881f9292 | ||
|
|
302a77b9dd | ||
|
|
2b544ad141 | ||
|
|
36d5e057d0 | ||
|
|
96eb6a80e1 | ||
|
|
9c5f5a46b5 | ||
|
|
886f63d3e4 | ||
|
|
7748dd7cef | ||
|
|
2e0f8067aa | ||
|
|
4fadd523f1 | ||
|
|
abe54c67d8 | ||
|
|
f3f35218c3 | ||
|
|
b9eb9cb6d9 | ||
|
|
40c014b663 | ||
|
|
70f1db4e94 | ||
|
|
2fff38d51a | ||
|
|
32de74b23e | ||
|
|
c96d24cc0b | ||
|
|
824ee55d38 | ||
|
|
86e701dccc | ||
|
|
2151b97bec | ||
|
|
18312b0624 | ||
|
|
a67cf4fb85 | ||
|
|
50100c251e | ||
|
|
1070482629 | ||
|
|
d205405dcc | ||
|
|
16d9c3b93f | ||
|
|
52ecef0311 | ||
|
|
f172146f27 | ||
|
|
ee986912d4 | ||
|
|
0e04968bc2 | ||
|
|
0cf1d8d375 | ||
|
|
339121fbb1 | ||
|
|
f99ca464de | ||
|
|
5c90a7c7a7 | ||
|
|
434b56fa8c | ||
|
|
78b484e657 | ||
|
|
9d36534230 | ||
|
|
bd6243b2ac | ||
|
|
416857e129 | ||
|
|
8cd81d0441 | ||
|
|
bf0d9ab7d9 | ||
|
|
bfeb915cd2 | ||
|
|
a830420d75 | ||
|
|
b904c6d00d | ||
|
|
050050a9eb | ||
|
|
9e48dc4137 | ||
|
|
2bd9bb233c | ||
|
|
e92d437456 | ||
|
|
fd6151040e | ||
|
|
b4e3dd7b4e | ||
|
|
8667544b3a | ||
|
|
664569a52b | ||
|
|
3c9da80962 | ||
|
|
583985e7ce | ||
|
|
b17bcb6c93 | ||
|
|
4886825564 | ||
|
|
79cd3ba912 | ||
|
|
21a3d4b66b | ||
|
|
562b0a0868 | ||
|
|
25243a10ec | ||
|
|
3fdba53995 | ||
|
|
34d8b396a4 | ||
|
|
c560e1fda2 | ||
|
|
3634236214 | ||
|
|
6de0cba0b0 | ||
|
|
35f5f4851e | ||
|
|
3a8f4de3bb | ||
|
|
d67321f187 | ||
|
|
7c52d0ec19 | ||
|
|
ea08ab9758 | ||
|
|
9c06803a31 | ||
|
|
9222dcbc2e | ||
|
|
75b91dc26b | ||
|
|
4334ea295d | ||
|
|
03eeef7f52 | ||
|
|
0747137679 | ||
|
|
0152f6fa1a | ||
|
|
9f2ede0b84 | ||
|
|
748786d599 | ||
|
|
18f0fb556a | ||
|
|
685b3f86b3 | ||
|
|
524914fd35 | ||
|
|
f7d09bf173 | ||
|
|
4b113e5781 | ||
|
|
5616579131 | ||
|
|
8142801f1f | ||
|
|
35eeaf4282 | ||
|
|
8ad8b08aa4 | ||
|
|
839ca31821 | ||
|
|
03a92e5556 | ||
|
|
99fd5f937e | ||
|
|
d7ffcd903d | ||
|
|
a28e7987e6 | ||
|
|
f17c46bbed | ||
|
|
27008505e5 | ||
|
|
f9ba322547 | ||
|
|
661efcb51f | ||
|
|
5928f28699 | ||
|
|
1183d65184 | ||
|
|
fd53e49508 | ||
|
|
c484e32641 | ||
|
|
c1741c99be | ||
|
|
a03179743d | ||
|
|
83b9160ab1 | ||
|
|
83757ed390 | ||
|
|
843e943251 | ||
|
|
a0a1d3de72 | ||
|
|
e5f262869c | ||
|
|
a070a68a57 | ||
|
|
3479bbbb36 | ||
|
|
e1a1a0652f | ||
|
|
3e9b775114 | ||
|
|
57c91aca3c | ||
|
|
05a64b8d9e | ||
|
|
30780f9c5f | ||
|
|
3455df9214 | ||
|
|
27aa8f9875 | ||
|
|
05451e3d1a | ||
|
|
b9b3295b52 | ||
|
|
f62ed66e26 | ||
|
|
e2a9a989ab | ||
|
|
4af27650cd | ||
|
|
76a603104d | ||
|
|
03825469ca | ||
|
|
058cdf7a82 | ||
|
|
7f43890713 | ||
|
|
57070ef155 | ||
|
|
fc5904e743 | ||
|
|
46c0620236 | ||
|
|
1bf56b658b | ||
|
|
8cc4d3e6d5 | ||
|
|
3080c49caf | ||
|
|
ed94ec39c4 | ||
|
|
6bcdd5e6c8 | ||
|
|
cf3d763731 | ||
|
|
e558bb17cb | ||
|
|
5d76a55c46 | ||
|
|
4401824716 | ||
|
|
df23f20d31 | ||
|
|
6c37905c15 | ||
|
|
4685572def | ||
|
|
3cf1657d54 | ||
|
|
60d3c09da5 | ||
|
|
5055ddf995 | ||
|
|
d83d2f82bd | ||
|
|
5266743a0c | ||
|
|
fffb2872ef | ||
|
|
3b56fc3760 | ||
|
|
5213faa0a1 | ||
|
|
6f4e5175da | ||
|
|
f502eb0195 | ||
|
|
509c9fe2e8 | ||
|
|
13e795d25d | ||
|
|
a28ef1f176 | ||
|
|
0c5eec7e95 | ||
|
|
310032e303 | ||
|
|
32b3c72bdf | ||
|
|
c1eee2246b | ||
|
|
0d7d53ab5b | ||
|
|
2f1de4a162 | ||
|
|
cdd36b6712 | ||
|
|
6f0afe269d | ||
|
|
09321eaa93 | ||
|
|
850259290a | ||
|
|
1bafe54a75 | ||
|
|
89e373f775 | ||
|
|
74854b3dac | ||
|
|
4e5ab21a47 | ||
|
|
a0510d6a69 | ||
|
|
6ddb60d047 | ||
|
|
ad592a563c | ||
|
|
b3b67b051d | ||
|
|
8607d58e18 | ||
|
|
34e60ec5b8 | ||
|
|
d044e1a5b7 | ||
|
|
9096318968 | ||
|
|
ba6e8c4092 | ||
|
|
98b6dba05d | ||
|
|
84c4631124 | ||
|
|
a7d308c585 | ||
|
|
af0ed6135e | ||
|
|
115ca0da0f | ||
|
|
673b545a83 | ||
|
|
d030fd1aa6 | ||
|
|
7c1e40be88 | ||
|
|
b739619532 | ||
|
|
372b83d92f | ||
|
|
4e3b5419d5 | ||
|
|
c34d5a35e2 | ||
|
|
a959b5bf02 | ||
|
|
45ac2c3c51 | ||
|
|
f6bddc6f24 | ||
|
|
6b8fc3228e | ||
|
|
8cf1a95df5 | ||
|
|
b3ea2969c5 | ||
|
|
054bbb8d5a | ||
|
|
19c1b29f59 | ||
|
|
264b8c2a9e | ||
|
|
ec33b95f97 | ||
|
|
4437d5305f | ||
|
|
f20f01e22e | ||
|
|
1ee30fe5dc | ||
|
|
4dc026d7fc | ||
|
|
1e862a8ee8 | ||
|
|
5ece2d1939 | ||
|
|
146e9100be | ||
|
|
0c854edc6b | ||
|
|
07d031e7b9 | ||
|
|
705dc2ec39 | ||
|
|
3b9221c7d2 | ||
|
|
576d602ed0 | ||
|
|
9df8bd63d4 | ||
|
|
99c4802367 | ||
|
|
94dc6f2053 | ||
|
|
d4005da35c | ||
|
|
cbe2c362d5 | ||
|
|
8bf79db66a | ||
|
|
b87964f716 | ||
|
|
b0a574f578 | ||
|
|
92dc3019de | ||
|
|
d8f92cb2be | ||
|
|
bf24660ddb | ||
|
|
b66268867a | ||
|
|
9abaa243e0 | ||
|
|
7030138ff4 | ||
|
|
906dfe60f8 | ||
|
|
01f60614ef | ||
|
|
eeb5306883 | ||
|
|
24c3a3e84c | ||
|
|
8b9663aea0 | ||
|
|
b59f4e2805 | ||
|
|
6293d57fde | ||
|
|
39ac2efe26 | ||
|
|
1f449e8ce1 | ||
|
|
ad906000c7 | ||
|
|
5361f33cc1 | ||
|
|
1937f3ea22 | ||
|
|
f4e9d0c8fe | ||
|
|
9b767ee9f3 | ||
|
|
43cb91bf52 | ||
|
|
f784ee5b28 | ||
|
|
cd6f067fe5 | ||
|
|
6d366188c9 | ||
|
|
c4ca59dcb0 | ||
|
|
74675ad314 | ||
|
|
b5e4da847a | ||
|
|
232bf5a68b | ||
|
|
67958a78d3 | ||
|
|
b8cdc29d8f | ||
|
|
30e4ddb950 | ||
|
|
a61a7db275 | ||
|
|
0f5e4e7fa2 | ||
|
|
eb17eae781 | ||
|
|
740a65f880 | ||
|
|
5bd7b53e0a | ||
|
|
9538f474de | ||
|
|
8f2e78c946 | ||
|
|
2f09ff456c | ||
|
|
c8d950e04d | ||
|
|
0f6a0da7a4 | ||
|
|
4300fb225f | ||
|
|
159021e87c | ||
|
|
9dc6ea9eeb | ||
|
|
42e84e526e | ||
|
|
700ee40109 | ||
|
|
4909bd9550 | ||
|
|
e0d20bf8ff | ||
|
|
09a5284675 | ||
|
|
1112f3acdd | ||
|
|
05e3071db2 | ||
|
|
403773bc17 | ||
|
|
636df2a736 | ||
|
|
baf3093893 | ||
|
|
8d3a49a319 | ||
|
|
eced1af21d | ||
|
|
49c3c9bec1 | ||
|
|
2ffaabe594 | ||
|
|
f31dc6c6e5 | ||
|
|
f067d0e831 | ||
|
|
a892f72425 | ||
|
|
25f64a2fc4 | ||
|
|
fd1fe90731 | ||
|
|
495643f4f1 | ||
|
|
8da6b336f5 | ||
|
|
d0bb86ca8f | ||
|
|
1085809fa5 | ||
|
|
7b1857dcda | ||
|
|
6f38790d47 | ||
|
|
a81ac85af6 | ||
|
|
8f4d5b6fce | ||
|
|
8468a48eaa | ||
|
|
fc0885e5d5 | ||
|
|
e1a9e347c3 | ||
|
|
1b0e3b686d | ||
|
|
a09208582e | ||
|
|
df1de020d1 | ||
|
|
9b363368a2 | ||
|
|
9fac69675a | ||
|
|
1d2cfde7f7 | ||
|
|
452612ee00 | ||
|
|
8d74e0c3ff | ||
|
|
0a1786c89a | ||
|
|
bc07d77d06 | ||
|
|
6e38457655 | ||
|
|
d7a3aeb0a2 | ||
|
|
76541d5563 | ||
|
|
28b5d8445e | ||
|
|
d17533fd0f | ||
|
|
01d62b7aea | ||
|
|
bd97804ca6 | ||
|
|
7b0ccc9f69 | ||
|
|
fdb98fa2a9 | ||
|
|
fb684bd788 | ||
|
|
05acf4eb2a | ||
|
|
56d0383170 | ||
|
|
b31236958b | ||
|
|
3ffa46fb26 | ||
|
|
217003381a | ||
|
|
234190f493 | ||
|
|
209e3e9e14 | ||
|
|
872241f497 | ||
|
|
cb7a66c59b | ||
|
|
924383ccc8 | ||
|
|
65d1301195 | ||
|
|
57c0490d84 | ||
|
|
b927e44107 | ||
|
|
6433a67d52 | ||
|
|
1cc2cfaec7 | ||
|
|
3fa24c5d81 | ||
|
|
a5c96f41b3 | ||
|
|
9fac291df4 | ||
|
|
971b7967de | ||
|
|
b3a4adeba4 | ||
|
|
b732f1df0d | ||
|
|
4395732c5e | ||
|
|
6720d12ab8 | ||
|
|
456765e55b | ||
|
|
a6009c89d3 | ||
|
|
d767c415d1 | ||
|
|
d88f535444 | ||
|
|
0c7dd18b7c | ||
|
|
0e535123ae | ||
|
|
8ce23b80bd | ||
|
|
d96023d063 | ||
|
|
d734d1a3b3 | ||
|
|
095f075ca9 | ||
|
|
ef70e44a17 | ||
|
|
27a6f35534 | ||
|
|
47ea4218d0 | ||
|
|
1fd677df5a | ||
|
|
7c349e42fd | ||
|
|
da88ec6807 | ||
|
|
cb715c0877 | ||
|
|
97a362617d | ||
|
|
24e708b7e1 | ||
|
|
583a684b03 | ||
|
|
fe8465261f | ||
|
|
334cc231dc | ||
|
|
848d574f68 | ||
|
|
8f929c0ee3 | ||
|
|
15bd839940 | ||
|
|
0323e0d17d | ||
|
|
5fa4fa0225 | ||
|
|
986c165815 | ||
|
|
53243d1764 | ||
|
|
4aed8e6b59 | ||
|
|
16653d60ed | ||
|
|
c9be89647c | ||
|
|
406f947096 | ||
|
|
64916dafac | ||
|
|
02ca843944 | ||
|
|
3520ab6b18 | ||
|
|
30314fd532 | ||
|
|
4a3e495be7 | ||
|
|
ccfd993042 | ||
|
|
bfd2f5b7cf | ||
|
|
b7cc460844 | ||
|
|
c17c4c1319 | ||
|
|
5967dd97c5 | ||
|
|
0c26198b55 | ||
|
|
a304cca3b6 | ||
|
|
564b65c8ca | ||
|
|
9de0cf5c03 | ||
|
|
1349079f59 | ||
|
|
38016b3ba3 | ||
|
|
8db9962282 | ||
|
|
dca3fb35c7 | ||
|
|
8484449d66 | ||
|
|
6b8ca3e611 | ||
|
|
73fd348490 | ||
|
|
04389fc8b0 | ||
|
|
b0bb77ca81 | ||
|
|
22598e75e8 | ||
|
|
0f8dd1effb | ||
|
|
2c4a8543db | ||
|
|
7aa246b550 | ||
|
|
1e294fe1bc | ||
|
|
dcfb12f454 | ||
|
|
685ee51e1f | ||
|
|
8407fee96d | ||
|
|
7c881377a9 | ||
|
|
acf2961f9e | ||
|
|
f5faeb8f68 | ||
|
|
8985a6932f | ||
|
|
c04f0af56f | ||
|
|
1341d4fe57 | ||
|
|
bc3fc59b1e | ||
|
|
e04838eaa2 | ||
|
|
5d5d89afb9 | ||
|
|
749671c230 | ||
|
|
e79c41d753 | ||
|
|
a575f2eced | ||
|
|
1aba77c752 | ||
|
|
b68e262eac | ||
|
|
f78fa58cf1 | ||
|
|
3916b06e7c | ||
|
|
7fa1f1c63c | ||
|
|
7ed85e8916 | ||
|
|
94e1e6a7b6 | ||
|
|
3cba3f7a4b | ||
|
|
bfd2ce782c | ||
|
|
f99053d251 | ||
|
|
bdc93a45b5 | ||
|
|
a771743756 | ||
|
|
aff1df32f2 | ||
|
|
3562afc2bd | ||
|
|
ac08ed7cf9 | ||
|
|
9cb316bdfa | ||
|
|
6682b52159 | ||
|
|
f5878a5e7b | ||
|
|
406de16a0d | ||
|
|
a73a59f184 | ||
|
|
cca667cf46 | ||
|
|
e6a63a9b85 | ||
|
|
22630c7656 | ||
|
|
c9ec3b3b42 | ||
|
|
a6727c5382 | ||
|
|
9fe2111d62 | ||
|
|
173bfbd300 | ||
|
|
999beef349 | ||
|
|
c6fdd8a59f | ||
|
|
0ec7e8c41b | ||
|
|
89e4ed5573 | ||
|
|
4c76df91ce | ||
|
|
a1c7e7d5da | ||
|
|
f28540a53e | ||
|
|
e0e522c3f5 | ||
|
|
37e286df48 | ||
|
|
bc99ee6ebe | ||
|
|
7e8126a421 | ||
|
|
af4ee8f7ec | ||
|
|
af40e8de5c | ||
|
|
d086131630 | ||
|
|
bccc177414 | ||
|
|
0dfadcbb4a | ||
|
|
4380b62715 | ||
|
|
91698034e7 | ||
|
|
014eb2a924 | ||
|
|
96357a57d2 | ||
|
|
c7cfdffaf6 | ||
|
|
46a27a9d0a | ||
|
|
35950a6294 | ||
|
|
c104eee2b1 | ||
|
|
f0972c6220 | ||
|
|
42a5145895 | ||
|
|
8d412e7d4c | ||
|
|
f45e34cfcf | ||
|
|
320c8e2d6b | ||
|
|
988de0b96e | ||
|
|
25a785dfa7 | ||
|
|
c993bbd993 | ||
|
|
479760c0ee | ||
|
|
e186237410 | ||
|
|
4084e3f0d8 | ||
|
|
dddf64031f | ||
|
|
8f1477e893 | ||
|
|
4866fe241e | ||
|
|
6613cb5c60 | ||
|
|
0f48dd567f | ||
|
|
b24511b7b5 | ||
|
|
df40199134 | ||
|
|
935562acc9 | ||
|
|
feb67e76f0 | ||
|
|
fdbe93f0fb | ||
|
|
f27058e255 | ||
|
|
6ddbbb4ba0 | ||
|
|
ef32f2c129 | ||
|
|
760bb2db2a | ||
|
|
68f38fd9fe | ||
|
|
5c6829fb62 | ||
|
|
91c48919c6 | ||
|
|
72313800fa | ||
|
|
478d5a16fd | ||
|
|
b8909d8043 | ||
|
|
c2c659b0fe | ||
|
|
20c3bde036 | ||
|
|
e77e37076f | ||
|
|
734fdf83c1 | ||
|
|
2906d42f97 | ||
|
|
0135f205df | ||
|
|
781c6e94a0 | ||
|
|
1d64dc8a26 | ||
|
|
34ff831473 | ||
|
|
3734160cb4 | ||
|
|
21234cacfb | ||
|
|
eb4dc85006 | ||
|
|
249b8a4768 | ||
|
|
1a1868c7f4 | ||
|
|
487659d5b4 | ||
|
|
f46de4d3e1 | ||
|
|
6314315db7 | ||
|
|
a22beeed08 | ||
|
|
840e377245 | ||
|
|
6fa255386b | ||
|
|
f563e777cc | ||
|
|
a63447b12b | ||
|
|
0f81c3ab92 | ||
|
|
425f254453 | ||
|
|
a9d9ec5bf9 | ||
|
|
0c5c752ffe | ||
|
|
4b556cd79f | ||
|
|
3d32a90f8e | ||
|
|
0b4c334163 | ||
|
|
312fc0984b | ||
|
|
30bf1c2641 | ||
|
|
2d1d7357a3 | ||
|
|
c70077c525 | ||
|
|
d2fad0128f | ||
|
|
62133c994f | ||
|
|
091ea974eb | ||
|
|
955ae00036 | ||
|
|
7d4de590e5 | ||
|
|
292937b962 | ||
|
|
08bd4f131c | ||
|
|
38cc83a4ee | ||
|
|
687a1cc9c7 | ||
|
|
1bcd03b150 | ||
|
|
e2abe23895 | ||
|
|
5c5dde48de | ||
|
|
d9f11e190f | ||
|
|
1ab2d7a240 | ||
|
|
580050cb7d | ||
|
|
eab5659163 | ||
|
|
397b350cfc | ||
|
|
c0130c997a | ||
|
|
fd7f1b32dd | ||
|
|
0e286d8261 | ||
|
|
ce7d2d1fb0 | ||
|
|
2175b8a7bb | ||
|
|
6c0893cdf8 | ||
|
|
25927a296d | ||
|
|
ee4db44fe8 | ||
|
|
b8cb38ae5c | ||
|
|
899bfdce2b | ||
|
|
456960e1f0 | ||
|
|
04e03e9b6e | ||
|
|
a7283da016 | ||
|
|
672321abab | ||
|
|
2059b4050b | ||
|
|
171b43c517 | ||
|
|
ccb7c8f8d9 | ||
|
|
abbc66ac07 | ||
|
|
2d18ef5cee | ||
|
|
79c132219b | ||
|
|
04d4d4a8d7 | ||
|
|
a918113ba0 | ||
|
|
810b25ff14 | ||
|
|
c4fd8d5120 | ||
|
|
772494f46f | ||
|
|
594e7da256 | ||
|
|
49668547d7 | ||
|
|
4c46aaead8 | ||
|
|
d61f5faf59 | ||
|
|
5756609531 | ||
|
|
ea1b285d52 | ||
|
|
bc61b55b94 | ||
|
|
48f6a96da8 | ||
|
|
967f1657d2 | ||
|
|
f90a323374 | ||
|
|
d289c62532 | ||
|
|
05695e8cd6 | ||
|
|
d6a7a6ce66 | ||
|
|
05c7672de3 | ||
|
|
ce515767ce | ||
|
|
76d8fd1023 | ||
|
|
558e259b84 | ||
|
|
f467bed24c | ||
|
|
fa753ad6fb | ||
|
|
22d61fead7 | ||
|
|
c4af1471f0 | ||
|
|
87ba7a7179 | ||
|
|
e7c5cf0846 | ||
|
|
1aaedb8d90 | ||
|
|
284de28f76 | ||
|
|
687d8d91a9 | ||
|
|
771087c6c6 | ||
|
|
1fd3e6365e | ||
|
|
28989296eb | ||
|
|
fd8d73af38 | ||
|
|
144a5415da | ||
|
|
d58e24bce5 | ||
|
|
0f86123ccb | ||
|
|
3f65ef998c | ||
|
|
29d49dfbf4 | ||
|
|
701d17f6f2 | ||
|
|
642431c43e | ||
|
|
3c5b47784d | ||
|
|
64d7fe8bbf | ||
|
|
32070b1fa7 | ||
|
|
8b3324e143 | ||
|
|
f40a5a029c | ||
|
|
eac82a602c | ||
|
|
d1312e0934 | ||
|
|
58dbee10c5 | ||
|
|
f8207994dc | ||
|
|
2030401859 | ||
|
|
8966cf9910 | ||
|
|
4eb4df9ca2 | ||
|
|
32861b1c72 | ||
|
|
7248ca2e9b | ||
|
|
a6ec93f883 | ||
|
|
a28c1d17c5 | ||
|
|
fb705b44a4 | ||
|
|
a32bab9fd0 | ||
|
|
6396e7f964 | ||
|
|
c898cef277 | ||
|
|
baeba54b06 | ||
|
|
f5ee5bf7fb | ||
|
|
73605414f9 | ||
|
|
6045c371e1 | ||
|
|
97a9289d5f | ||
|
|
1983fc9b67 | ||
|
|
bb40d84212 | ||
|
|
46a35c553e | ||
|
|
080943e810 | ||
|
|
62fb3863c6 | ||
|
|
2db03a163d | ||
|
|
9e3dd82efe | ||
|
|
9f1989bfef | ||
|
|
c0bdd8fc77 | ||
|
|
8a23c90e46 | ||
|
|
9095e5b057 | ||
|
|
52c18115af | ||
|
|
737137b19f | ||
|
|
7b78bee135 | ||
|
|
accda36a7b | ||
|
|
69dd9eb067 | ||
|
|
a562bc661d | ||
|
|
258f12f684 | ||
|
|
4dc8d77a5a | ||
|
|
7c5825d1bc | ||
|
|
6ba4d8e947 | ||
|
|
3faf6e967f | ||
|
|
a7a5667301 | ||
|
|
d4924897b2 | ||
|
|
f2d39ca55a | ||
|
|
743d052f55 | ||
|
|
17dbab5ee8 | ||
|
|
8cb5e07c7b | ||
|
|
e9972783d2 | ||
|
|
84f6080a38 | ||
|
|
1b5c1e4e52 | ||
|
|
d7e89ebdae | ||
|
|
aa413dab6d | ||
|
|
b876870bd8 | ||
|
|
05e56cc845 | ||
|
|
d6c158ce98 | ||
|
|
1d4ef4e9a4 | ||
|
|
4d2684be52 | ||
|
|
6947805b5c | ||
|
|
d0e0aea0f1 | ||
|
|
b0f250ed8a | ||
|
|
45254606d4 | ||
|
|
03ee46d903 | ||
|
|
c4afc7a923 | ||
|
|
afa2a5b0f0 | ||
|
|
b40057d423 | ||
|
|
282ffef200 | ||
|
|
22b02de405 | ||
|
|
f0330e9ae3 | ||
|
|
0c26490e3f | ||
|
|
cfaecd93e4 | ||
|
|
ccbc6c7a75 | ||
|
|
2fc2ad98aa | ||
|
|
16590b2dbb | ||
|
|
f40349479e | ||
|
|
9f60c8dffe | ||
|
|
5abd9d1a40 | ||
|
|
0ae5a53d86 | ||
|
|
15a70d0157 | ||
|
|
ededb68ef1 | ||
|
|
09add5fbff | ||
|
|
e30f49a791 | ||
|
|
64737b741b | ||
|
|
d4d65bdf37 | ||
|
|
90732a4fad | ||
|
|
c5015f5828 | ||
|
|
aa77c944d8 | ||
|
|
b8faa6714a | ||
|
|
f48f98f4d7 | ||
|
|
79b2a0839f | ||
|
|
6094d17718 | ||
|
|
d2ed7d6417 | ||
|
|
a705ef21a9 | ||
|
|
67e465c479 | ||
|
|
ed3883b52c | ||
|
|
71ea0bcb5e | ||
|
|
dd2cffeee9 | ||
|
|
1ceabf3bc3 | ||
|
|
17fcd637f2 | ||
|
|
d44bc4b182 | ||
|
|
4760406221 | ||
|
|
0aae0d888c | ||
|
|
1bc01057f3 | ||
|
|
c1906606c6 | ||
|
|
1363194909 | ||
|
|
d945e0c31b | ||
|
|
0af7b75af5 | ||
|
|
36a42cb064 | ||
|
|
4db0acc0e7 | ||
|
|
8f4800aa47 | ||
|
|
4745a37549 | ||
|
|
8fc949898b | ||
|
|
d4758b0e91 | ||
|
|
a07e4b17be | ||
|
|
b9dd975bc6 | ||
|
|
9964d3e24b | ||
|
|
58e8c8e182 | ||
|
|
c7339f6cfa | ||
|
|
1aa78f766a | ||
|
|
bf56e6adaf | ||
|
|
e915b7e499 | ||
|
|
de0470d200 | ||
|
|
3d7cf6992e | ||
|
|
06db082e3f | ||
|
|
4a383cecaf | ||
|
|
9a0f9207be | ||
|
|
0e3a0a1eec | ||
|
|
fd6d180eba | ||
|
|
d152e2ef9a | ||
|
|
b530cf4461 | ||
|
|
bbe85cde31 | ||
|
|
2c3cbe7146 | ||
|
|
2358308f4d | ||
|
|
58bff4fa73 | ||
|
|
098f7afc70 | ||
|
|
4070b1e23d | ||
|
|
d9d4c8a70c | ||
|
|
05abe49d8b | ||
|
|
a71245b883 | ||
|
|
057f88a36a | ||
|
|
32fcdd9d94 | ||
|
|
313927827d | ||
|
|
358b2b661e | ||
|
|
3eddacdff8 | ||
|
|
95d4cde649 | ||
|
|
d1d947bf12 | ||
|
|
40815e5b38 | ||
|
|
8fc1d23e03 | ||
|
|
5ec8c229a1 | ||
|
|
2412ec2195 | ||
|
|
bfb96b0ae8 | ||
|
|
f64920e510 | ||
|
|
664dc333ac | ||
|
|
521f6f2b18 | ||
|
|
6986d03c53 | ||
|
|
e6e38e3ca2 | ||
|
|
951d08f914 | ||
|
|
8e1e8ba7de | ||
|
|
877b098b09 | ||
|
|
e046eddda9 | ||
|
|
084b1169d7 | ||
|
|
f7ceeff05a | ||
|
|
e0d41a2b8a | ||
|
|
6b9f9f0f44 | ||
|
|
025135b8c6 | ||
|
|
77d810b735 | ||
|
|
e1222de05b | ||
|
|
459f807e67 | ||
|
|
32df1370a6 | ||
|
|
f18f8c89ec | ||
|
|
787b791651 | ||
|
|
2eca0da852 | ||
|
|
9e49604ce2 | ||
|
|
5f85c61d6a | ||
|
|
cd58855e1f | ||
|
|
13c64b0db0 | ||
|
|
55e021ba20 | ||
|
|
26fd61a3ed | ||
|
|
46482522bb | ||
|
|
98e3e22896 | ||
|
|
15d49e97c0 | ||
|
|
d5e7ce38ac | ||
|
|
162d0560db | ||
|
|
1de05047ca | ||
|
|
2af5de1199 | ||
|
|
e66a724d2b | ||
|
|
9f4c2ac8d7 | ||
|
|
44f0011445 | ||
|
|
545094cddf | ||
|
|
99f45d8853 | ||
|
|
c25b5f9938 | ||
|
|
db1304c11a | ||
|
|
57714b373c | ||
|
|
5be7f839f3 | ||
|
|
aa441eb58b | ||
|
|
e6b6a40fa6 | ||
|
|
f6dde6f4c1 | ||
|
|
36ab475578 | ||
|
|
a74fe62da6 | ||
|
|
1e4de7fec4 | ||
|
|
47117d1fb7 | ||
|
|
cb8ef408a4 | ||
|
|
e5f21a88fa | ||
|
|
0458c4f798 | ||
|
|
75f6160432 | ||
|
|
5337eb48e7 | ||
|
|
86c30ee731 | ||
|
|
d68dc38959 | ||
|
|
0525639329 | ||
|
|
0d9c7282df | ||
|
|
3b95925217 | ||
|
|
fa595f7aa3 | ||
|
|
ff80f32f72 | ||
|
|
e55dbef2fc | ||
|
|
ebf2e459e8 | ||
|
|
1b5081ae21 | ||
|
|
d5736756f7 | ||
|
|
757cf2e193 | ||
|
|
3a75ac0486 | ||
|
|
3c3ef6fa05 | ||
|
|
3282bfd03b | ||
|
|
0813df6b29 | ||
|
|
df74a04085 | ||
|
|
8323b87076 | ||
|
|
48f01921e1 | ||
|
|
ae9d99257e | ||
|
|
0183c8a4b7 | ||
|
|
9a2ca35e6e | ||
|
|
2edb48e314 | ||
|
|
a81fd497d4 | ||
|
|
49cca5cd69 | ||
|
|
f92cff6241 | ||
|
|
1b4343ffc2 | ||
|
|
d5392a5f59 | ||
|
|
a65ce649ac | ||
|
|
d5dc6cea99 | ||
|
|
5ecfe9f0f0 | ||
|
|
0f5fb066d1 | ||
|
|
8e61639598 | ||
|
|
e88468d867 | ||
|
|
bc46e2f509 | ||
|
|
2241279bb6 | ||
|
|
25ffbed756 | ||
|
|
8784843a7a | ||
|
|
a964e2b3c9 | ||
|
|
7f7efd45ab | ||
|
|
af8f5afef8 | ||
|
|
dcfaed437c | ||
|
|
47e2322e33 | ||
|
|
00e7d57245 | ||
|
|
d53a3222d0 | ||
|
|
80fe3a73e2 | ||
|
|
7ab993b764 | ||
|
|
622edd2ed1 | ||
|
|
1f5faee356 | ||
|
|
076b0cf867 | ||
|
|
d4168bebc6 | ||
|
|
13c3629cd6 | ||
|
|
1eff9801e8 | ||
|
|
92858aebd6 | ||
|
|
1910724a98 | ||
|
|
ff8441fa95 | ||
|
|
9d8fb055b1 | ||
|
|
9651b3d692 | ||
|
|
9d10c8627a | ||
|
|
929d7670cb | ||
|
|
5714a8f884 | ||
|
|
159529937d | ||
|
|
394d9ff4d2 | ||
|
|
07165f344f | ||
|
|
4f2146dd9c | ||
|
|
5b9784cd9e | ||
|
|
9d9b61cf14 | ||
|
|
9d7db96e4b | ||
|
|
3d0bca2781 | ||
|
|
ffb54c4f7a | ||
|
|
a01031303f | ||
|
|
7808f6d182 | ||
|
|
a0c3b9412f | ||
|
|
c32e2053c3 | ||
|
|
a33151248d | ||
|
|
038ba3b006 | ||
|
|
f3c92f4110 | ||
|
|
17779dbbc6 | ||
|
|
c2dd5b8c47 | ||
|
|
fcb9121e5a | ||
|
|
0af1004860 | ||
|
|
917f668cf3 | ||
|
|
436499b7c4 | ||
|
|
66af9866f0 | ||
|
|
3703dedad9 | ||
|
|
37502151ef | ||
|
|
3816a10de3 | ||
|
|
bdda6067ab | ||
|
|
0f62af241f | ||
|
|
987919e87a | ||
|
|
0c03a4b7ff | ||
|
|
5d6d13c95f | ||
|
|
563021bdc1 | ||
|
|
2d6d35a398 |
24
.dockerignore
Normal file
24
.dockerignore
Normal file
@@ -0,0 +1,24 @@
|
||||
config/autoload/*local*
|
||||
data/infra
|
||||
data/cache/*
|
||||
data/log/*
|
||||
data/locks/*
|
||||
data/proxies/*
|
||||
data/migrations_template.txt
|
||||
data/GeoLite2-City.*
|
||||
data/database.sqlite
|
||||
data/shlink-tests.db
|
||||
**/.gitignore
|
||||
CHANGELOG.md
|
||||
composer.lock
|
||||
vendor
|
||||
docs
|
||||
indocker
|
||||
docker-*
|
||||
php*
|
||||
infection.json
|
||||
phpstan.neon
|
||||
**/test*
|
||||
build*
|
||||
.github
|
||||
hooks
|
||||
15
.env.dist
15
.env.dist
@@ -1,15 +0,0 @@
|
||||
# Application
|
||||
APP_ENV=
|
||||
SECRET_KEY=
|
||||
SHORTENED_URL_SCHEMA=
|
||||
SHORTENED_URL_HOSTNAME=
|
||||
SHORTCODE_CHARS=
|
||||
|
||||
# Language
|
||||
DEFAULT_LOCALE=
|
||||
CLI_LOCALE=
|
||||
|
||||
# Database
|
||||
DB_USER=
|
||||
DB_PASSWORD=
|
||||
DB_NAME=
|
||||
11
.gitattributes
vendored
11
.gitattributes
vendored
@@ -1,13 +1,12 @@
|
||||
/config/test export-ignore
|
||||
/data/infra export-ignore
|
||||
/docs export-ignore
|
||||
/module/CLI/test export-ignore
|
||||
/module/CLI/test-resources export-ignore
|
||||
/module/Common/test export-ignore
|
||||
/module/Common/test-func export-ignore
|
||||
/module/Core/test export-ignore
|
||||
/module/Core/test-func export-ignore
|
||||
/module/Core/test-db export-ignore
|
||||
/module/Rest/test export-ignore
|
||||
.env.dist export-ignore
|
||||
/module/Rest/test-api export-ignore
|
||||
.gitattributes export-ignore
|
||||
.gitignore export-ignore
|
||||
.phpstorm.meta.php export-ignore
|
||||
@@ -17,9 +16,9 @@ build.sh export-ignore
|
||||
CHANGELOG.md export-ignore
|
||||
docker-compose.override.yml.dist export-ignore
|
||||
docker-compose.yml export-ignore
|
||||
func_tests_bootstrap.php export-ignore
|
||||
indocker export-ignore
|
||||
phpcs.xml export-ignore
|
||||
phpunit.xml.dist export-ignore
|
||||
phpunit-func.xml export-ignore
|
||||
phpunit-api.xml export-ignore
|
||||
phpunit-db.xml export-ignore
|
||||
phpstan.neon
|
||||
|
||||
2
.github/FUNDING.yml
vendored
Normal file
2
.github/FUNDING.yml
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
github: ['acelaya']
|
||||
custom: ['https://acel.me/donate']
|
||||
6
.github/ISSUE_TEMPLATE.md
vendored
Normal file
6
.github/ISSUE_TEMPLATE.md
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
<!--
|
||||
Before opening an issue, just take into account that this is a completely free of charge open source project.
|
||||
I'm always happy to help and provide support, but some understanding will be required.
|
||||
I do this in my own free time, so expect some delays when implementing new features and fixing bugs, and don't take it personal if an issue gets eventually closed.
|
||||
Try to be polite, and understand it is impossible for an OSS project to cover all use cases.
|
||||
-->
|
||||
37
.github/ISSUE_TEMPLATE/Bug.md
vendored
Normal file
37
.github/ISSUE_TEMPLATE/Bug.md
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Something on shlink is broken or not working as documented?
|
||||
labels: bug
|
||||
---
|
||||
|
||||
<!--
|
||||
Before opening an issue, just take into account that this is a completely free of charge open source project.
|
||||
I'm always happy to help and provide support, but some understanding will be required.
|
||||
I do this in my own free time, so expect some delays when implementing new features and fixing bugs, and don't take it personal if an issue gets eventually closed.
|
||||
Try to be polite, and understand it is impossible for an OSS project to cover all use cases.
|
||||
|
||||
With that said, please fill in the information requested next. More information might be requested next (like logs or system configs).
|
||||
-->
|
||||
|
||||
#### How Shlink is set-up
|
||||
|
||||
* 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
|
||||
* Database engine used: MySQL|MariaDB|PostgreSQL|SQLite (x.y.z)
|
||||
|
||||
#### Summary
|
||||
|
||||
<!-- Provide a summary describing the problem you are experiencing. -->
|
||||
|
||||
#### Current behavior
|
||||
|
||||
<!-- How is it actually behaving (and it shouldn't)? -->
|
||||
|
||||
#### Expected behavior
|
||||
|
||||
<!-- How did you expected to behave? -->
|
||||
|
||||
#### How to reproduce
|
||||
|
||||
<!-- Provide steps to reproduce the bug. -->
|
||||
18
.github/ISSUE_TEMPLATE/Feature_Request.md
vendored
Normal file
18
.github/ISSUE_TEMPLATE/Feature_Request.md
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Do you find shlink is missing some important feature that would make it more useful?
|
||||
labels: feature
|
||||
---
|
||||
|
||||
<!--
|
||||
Before opening an issue, just take into account that this is a completely free of charge open source project.
|
||||
I'm always happy to help and provide support, but some understanding will be required.
|
||||
I do this in my own free time, so expect some delays when implementing new features and fixing bugs, and don't take it personal if an issue gets eventually closed.
|
||||
Try to be polite, and understand it is impossible for an OSS project to cover all use cases.
|
||||
|
||||
With that said, please fill in the information requested next. More information might be requested next (like logs or system configs).
|
||||
-->
|
||||
|
||||
#### Summary
|
||||
|
||||
<!-- Describe the new feature you would like to request. -->
|
||||
25
.github/ISSUE_TEMPLATE/Question_Support.md
vendored
Normal file
25
.github/ISSUE_TEMPLATE/Question_Support.md
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
---
|
||||
name: Question - Support
|
||||
about: Do you have a problem setting up or using shlink?
|
||||
labels: question
|
||||
---
|
||||
|
||||
<!--
|
||||
Before opening an issue, just take into account that this is a completely free of charge open source project.
|
||||
I'm always happy to help and provide support, but some understanding will be required.
|
||||
I do this in my own free time, so expect some delays when implementing new features and fixing bugs, and don't take it personal if an issue gets eventually closed.
|
||||
Try to be polite, and understand it is impossible for an OSS project to cover all use cases.
|
||||
|
||||
With that said, please fill in the information requested next. More information might be requested next (like logs or system configs).
|
||||
-->
|
||||
|
||||
#### How Shlink is set-up
|
||||
|
||||
* 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
|
||||
* Database engine used: MySQL|MariaDB|PostgreSQL|SQLite (x.y.z)
|
||||
|
||||
#### Summary
|
||||
|
||||
<!-- Describe the issue you are facing here. -->
|
||||
9
.gitignore
vendored
9
.gitignore
vendored
@@ -1,8 +1,13 @@
|
||||
.idea
|
||||
build
|
||||
!hooks/build
|
||||
composer.lock
|
||||
composer.phar
|
||||
vendor/
|
||||
.env
|
||||
data/database.sqlite
|
||||
docs/swagger-ui
|
||||
data/shlink-tests.db
|
||||
data/GeoLite2-City.mmdb
|
||||
data/GeoLite2-City.mmdb.*
|
||||
docs/swagger-ui*
|
||||
docker-compose.override.yml
|
||||
.phpunit.result.cache
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
namespace PHPSTORM_META;
|
||||
|
||||
use Psr\Container\ContainerInterface;
|
||||
use Zend\ServiceManager\ServiceManager;
|
||||
use Laminas\ServiceManager\ServiceLocatorInterface;
|
||||
|
||||
/**
|
||||
* PhpStorm Container Interop code completion
|
||||
@@ -17,7 +17,7 @@ $STATIC_METHOD_TYPES = [
|
||||
ContainerInterface::get('') => [
|
||||
'' == '@',
|
||||
],
|
||||
ServiceManager::build('') => [
|
||||
ServiceLocatorInterface::build('') => [
|
||||
'' == '@',
|
||||
],
|
||||
];
|
||||
|
||||
@@ -1,6 +1,13 @@
|
||||
tools:
|
||||
external_code_coverage: true
|
||||
external_code_coverage:
|
||||
timeout: 600
|
||||
checks:
|
||||
php:
|
||||
code_rating: true
|
||||
duplication: true
|
||||
php:
|
||||
code_rating: true
|
||||
duplication: true
|
||||
build:
|
||||
nodes:
|
||||
analysis:
|
||||
tests:
|
||||
override:
|
||||
- php-scrutinizer-run
|
||||
|
||||
52
.travis.yml
52
.travis.yml
@@ -5,24 +5,54 @@ branches:
|
||||
- /.*/
|
||||
|
||||
php:
|
||||
- 7.1
|
||||
- 7.2
|
||||
- '7.4'
|
||||
|
||||
services:
|
||||
- mysql
|
||||
- postgresql
|
||||
- docker
|
||||
|
||||
cache:
|
||||
directories:
|
||||
- $HOME/.composer/cache/files
|
||||
|
||||
before_install:
|
||||
- phpenv config-add data/infra/travis-php/memcached.ini
|
||||
- phpenv config-add data/infra/travis-php/apcu.ini
|
||||
- echo 'extension = apcu.so' >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini
|
||||
- yes | pecl install swoole
|
||||
- phpenv config-rm xdebug.ini || return 0
|
||||
|
||||
install:
|
||||
- composer self-update
|
||||
- composer install --no-interaction --prefer-dist
|
||||
|
||||
before_script:
|
||||
- composer self-update
|
||||
- composer install --no-interaction
|
||||
- mysql -e 'CREATE DATABASE shlink_test;'
|
||||
- psql -c 'create database shlink_test;' -U postgres
|
||||
- mkdir build
|
||||
- export DOCKERFILE_CHANGED=$(git diff ${TRAVIS_COMMIT_RANGE:-origin/master} --name-only | grep Dockerfile)
|
||||
|
||||
script:
|
||||
- mkdir build
|
||||
- composer check
|
||||
- composer ci
|
||||
- if [[ ! -z "$DOCKERFILE_CHANGED" && "${TRAVIS_PHP_VERSION}" == "7.4" ]]; then docker build -t shlink-docker-image:temp . ; fi
|
||||
|
||||
after_script:
|
||||
- vendor/bin/phpcov merge build --clover build/clover.xml
|
||||
after_success:
|
||||
- rm -f build/clover.xml
|
||||
- wget https://phar.phpunit.de/phpcov-6.0.1.phar
|
||||
- phpdbg -qrr phpcov-6.0.1.phar merge build --clover build/clover.xml
|
||||
- wget https://scrutinizer-ci.com/ocular.phar
|
||||
- php ocular.phar code-coverage:upload --format=php-clover build/clover.xml
|
||||
|
||||
sudo: false
|
||||
# Before deploying, build dist file for current travis tag
|
||||
before_deploy:
|
||||
- rm -f ocular.phar
|
||||
- ./build.sh ${TRAVIS_TAG#?}
|
||||
|
||||
deploy:
|
||||
- provider: releases
|
||||
api_key:
|
||||
secure: a9dbZchocqeuOViwUeNH54bQR5Sz7rEYXx5b9WPFtnFn9LGKKUaLbA2U91UQ9QKPrcTpsALubUYbw2CnNmvCwzaY+R8lCD3gkU4ohsEnbpnw3deOeixI74sqBHJAuCH9FSaRDGILoBMtUKx2xlzIymFxkIsgIukkGbdkWHDlRWY3oTUUuw1SQ2Xk9KDsbJQtjIc1+G/O6gHaV4qv/R9W8NPmJExKTNDrAZbC1vIUnxqp4UpVo1hst8qPd1at94CndDYM5rG+7imGbdtxTxzamt819qdTO1OfvtctKawNAm7YXZrrWft6c7gI6j6SI4hxd+ZrrPBqbaRFHkZHjnNssO/yn4SaOHFFzccmu0MzvpPCf0qWZwd3sGHVYer1MnR2mHYqU84QPlW3nrHwJjkrpq3+q0JcBY6GsJs+RskHNtkMTKV05Iz6QUI5YZGwTpuXaRm036SmavjGc4IDlMaYCk/NmbB9BKpthJxLdUpczOHpnjXXHziotWD6cfEnbjU3byfD8HY5WrxSjsNT7SKmXN3hRof7bk985ewQVjGT42O3NbnfnqjQQWr/B7/zFTpLR4f526Bkq12CdCyf5lvrbq+POkLVdJ+uFfR7ds248Ue/jBQy6kM1tWmKF9QiwisFlA84eQ4CW3I93Rp97URv+AQa9zmbD0Ve3Udp+g6nF5I=
|
||||
file: "./build/shlink_${TRAVIS_TAG#?}_dist.zip"
|
||||
skip_cleanup: true
|
||||
on:
|
||||
tags: true
|
||||
php: '7.4'
|
||||
|
||||
862
CHANGELOG.md
862
CHANGELOG.md
@@ -1,5 +1,867 @@
|
||||
# CHANGELOG
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com), and this project adheres to [Semantic Versioning](https://semver.org).
|
||||
|
||||
## 2.0.1 - 2020-01-10
|
||||
|
||||
#### Added
|
||||
|
||||
* *Nothing*
|
||||
|
||||
#### Changed
|
||||
|
||||
* *Nothing*
|
||||
|
||||
#### Deprecated
|
||||
|
||||
* *Nothing*
|
||||
|
||||
#### Removed
|
||||
|
||||
* *Nothing*
|
||||
|
||||
#### Fixed
|
||||
|
||||
* [#607](https://github.com/shlinkio/shlink/issues/607) Added missing info on UPGRADE.md doc.
|
||||
* [#610](https://github.com/shlinkio/shlink/issues/610) Fixed use of hardcoded quotes on a database migration which makes it fail on postgres.
|
||||
* [#605](https://github.com/shlinkio/shlink/issues/605) Fixed crashes occurring when migrating from old Shlink versions with nullable DB columns that are assigned to non-nullable entity typed props.
|
||||
|
||||
|
||||
## 2.0.0 - 2020-01-08
|
||||
|
||||
#### Added
|
||||
|
||||
* [#429](https://github.com/shlinkio/shlink/issues/429) Added support for PHP 7.4
|
||||
* [#529](https://github.com/shlinkio/shlink/issues/529) Created an UPGRADING.md file explaining how to upgrade from v1.x to v2.x
|
||||
* [#594](https://github.com/shlinkio/shlink/issues/594) Updated external shlink packages, including installer v4.0, which adds the option to ask for the redis cluster config.
|
||||
|
||||
#### Changed
|
||||
|
||||
* [#592](https://github.com/shlinkio/shlink/issues/592) Updated coding styles to use [shlinkio/php-coding-standard](https://github.com/shlinkio/php-coding-standard) v2.1.0.
|
||||
* [#530](https://github.com/shlinkio/shlink/issues/530) Migrated project from deprecated `zendframework` components to the new `laminas` and `mezzio` ones.
|
||||
|
||||
#### Deprecated
|
||||
|
||||
* *Nothing*
|
||||
|
||||
#### Removed
|
||||
|
||||
* [#429](https://github.com/shlinkio/shlink/issues/429) Dropped support for PHP 7.2 and 7.3
|
||||
|
||||
* [#229](https://github.com/shlinkio/shlink/issues/229) Remove everything which was deprecated, including:
|
||||
|
||||
* Preview generation feature completely removed.
|
||||
* Authentication against REST API using JWT is no longer supported.
|
||||
|
||||
See [UPGRADE](UPGRADE.md) doc in order to get details on how to migrate to this version.
|
||||
|
||||
#### Fixed
|
||||
|
||||
* [#600](https://github.com/shlinkio/shlink/issues/600) Fixed health action so that it works with and without version in the path.
|
||||
|
||||
|
||||
## 1.21.1 - 2020-01-02
|
||||
|
||||
#### Added
|
||||
|
||||
* *Nothing*
|
||||
|
||||
#### Changed
|
||||
|
||||
* *Nothing*
|
||||
|
||||
#### Deprecated
|
||||
|
||||
* *Nothing*
|
||||
|
||||
#### Removed
|
||||
|
||||
* *Nothing*
|
||||
|
||||
#### Fixed
|
||||
|
||||
* [#596](https://github.com/shlinkio/shlink/issues/596) Fixed error when trying to download GeoLite2 database due to changes on how to get the database files.
|
||||
|
||||
|
||||
## 1.21.0 - 2019-12-29
|
||||
|
||||
#### Added
|
||||
|
||||
* [#118](https://github.com/shlinkio/shlink/issues/118) API errors now implement the [problem details](https://tools.ietf.org/html/rfc7807) standard.
|
||||
|
||||
In order to make it backwards compatible, two things have been done:
|
||||
|
||||
* Both the old `error` and `message` properties have been kept on error response, containing the same values as the `type` and `detail` properties respectively.
|
||||
* The API `v2` has been enabled. If an error occurs when calling the API with this version, the `error` and `message` properties will not be returned.
|
||||
|
||||
> After Shlink v2 is released, both API versions will behave like API v2.
|
||||
|
||||
* [#575](https://github.com/shlinkio/shlink/issues/575) Added support to filter short URL lists by date ranges.
|
||||
|
||||
* The `GET /short-urls` endpoint now accepts the `startDate` and `endDate` query params.
|
||||
* The `short-urls:list` command now allows `--startDate` and `--endDate` flags to be optionally provided.
|
||||
|
||||
* [#338](https://github.com/shlinkio/shlink/issues/338) Added support to asynchronously notify external services via webhook, only when shlink is served with swoole.
|
||||
|
||||
Configured webhooks will receive a POST request every time a URL receives a visit, including information about the short URL and the visit.
|
||||
|
||||
The payload will look like this:
|
||||
|
||||
```json
|
||||
{
|
||||
"shortUrl": {},
|
||||
"visit": {}
|
||||
}
|
||||
```
|
||||
|
||||
> The `shortUrl` and `visit` props have the same shape as it is defined in the [API spec](https://api-spec.shlink.io).
|
||||
|
||||
#### Changed
|
||||
|
||||
* [#492](https://github.com/shlinkio/shlink/issues/492) Updated to monolog 2, together with other dependencies, like Symfony 5 and infection-php.
|
||||
* [#527](https://github.com/shlinkio/shlink/issues/527) Increased minimum required mutation score for unit tests to 80%.
|
||||
* [#557](https://github.com/shlinkio/shlink/issues/557) Added a few php.ini configs for development and production docker images.
|
||||
|
||||
#### Deprecated
|
||||
|
||||
* *Nothing*
|
||||
|
||||
#### Removed
|
||||
|
||||
* *Nothing*
|
||||
|
||||
#### Fixed
|
||||
|
||||
* [#570](https://github.com/shlinkio/shlink/issues/570) Fixed shlink version generated for docker images when building from `develop` branch.
|
||||
|
||||
|
||||
## 1.20.3 - 2019-12-23
|
||||
|
||||
#### Added
|
||||
|
||||
* *Nothing*
|
||||
|
||||
#### Changed
|
||||
|
||||
* *Nothing*
|
||||
|
||||
#### Deprecated
|
||||
|
||||
* *Nothing*
|
||||
|
||||
#### Removed
|
||||
|
||||
* *Nothing*
|
||||
|
||||
#### Fixed
|
||||
|
||||
* [#585](https://github.com/shlinkio/shlink/issues/585) Fixed `PHP Fatal error: Uncaught Error: Class 'Shlinkio\Shlink\LocalLockFactory' not found` happening when running some CLI commands.
|
||||
|
||||
|
||||
## 1.20.2 - 2019-12-06
|
||||
|
||||
#### Added
|
||||
|
||||
* *Nothing*
|
||||
|
||||
#### Changed
|
||||
|
||||
* *Nothing*
|
||||
|
||||
#### Deprecated
|
||||
|
||||
* *Nothing*
|
||||
|
||||
#### Removed
|
||||
|
||||
* *Nothing*
|
||||
|
||||
#### Fixed
|
||||
|
||||
* [#561](https://github.com/shlinkio/shlink/issues/561) Fixed `db:migrate` command failing because yaml extension is not installed, which makes config file not to be readable.
|
||||
* [#562](https://github.com/shlinkio/shlink/issues/562) Fixed internal server error being returned when renaming a tag to another tag's name. Now a meaningful API error with status 409 is returned.
|
||||
* [#555](https://github.com/shlinkio/shlink/issues/555) Fixed internal server error being returned when invalid dates are provided for new short URLs. Now a 400 is returned, as intended.
|
||||
|
||||
|
||||
## 1.20.1 - 2019-11-17
|
||||
|
||||
#### Added
|
||||
|
||||
* [#519](https://github.com/shlinkio/shlink/issues/519) Documented how to customize web workers and task workers for the docker image.
|
||||
|
||||
#### Changed
|
||||
|
||||
* *Nothing*
|
||||
|
||||
#### Deprecated
|
||||
|
||||
* *Nothing*
|
||||
|
||||
#### Removed
|
||||
|
||||
* *Nothing*
|
||||
|
||||
#### Fixed
|
||||
|
||||
* [#512](https://github.com/shlinkio/shlink/issues/512) Fixed query params not being properly forwarded from short URL to long one.
|
||||
* [#540](https://github.com/shlinkio/shlink/issues/540) Fixed errors thrown when creating short URLs if the original URL has an internationalized domain name and URL validation is enabled.
|
||||
* [#528](https://github.com/shlinkio/shlink/issues/528) Ensured `db:create` and `db:migrate` commands do not silently fail when run as part of `install` or `update`.
|
||||
* [#518](https://github.com/shlinkio/shlink/issues/518) Fixed service which updates Geolite db file to use a local lock instead of a shared one, since every shlink instance holds its own db instance.
|
||||
|
||||
|
||||
## 1.20.0 - 2019-11-02
|
||||
|
||||
#### Added
|
||||
|
||||
* [#491](https://github.com/shlinkio/shlink/issues/491) Added improved short code generation logic.
|
||||
|
||||
Now, short codes are truly random, which removes the guessability factor existing in previous versions.
|
||||
|
||||
Generated short codes have 5 characters, and shlink makes sure they keep unique, while making it backwards-compatible.
|
||||
|
||||
* [#418](https://github.com/shlinkio/shlink/issues/418) and [#419](https://github.com/shlinkio/shlink/issues/419) Added support to redirect any 404 error to a custom URL.
|
||||
|
||||
It was already possible to configure this but only for invalid short URLs. Shlink now also support configuring redirects for the base URL and any other kind of "not found" error.
|
||||
|
||||
The three URLs can be different, and it is already possible to pass them to the docker image via configuration or env vars.
|
||||
|
||||
The installer also asks for these two new configuration options.
|
||||
|
||||
* [#497](https://github.com/shlinkio/shlink/issues/497) Officially added support for MariaDB.
|
||||
|
||||
#### Changed
|
||||
|
||||
* [#458](https://github.com/shlinkio/shlink/issues/458) Updated coding styles to use [shlinkio/php-coding-standard](https://github.com/shlinkio/php-coding-standard) v2.0.0.
|
||||
|
||||
#### Deprecated
|
||||
|
||||
* *Nothing*
|
||||
|
||||
#### Removed
|
||||
|
||||
* *Nothing*
|
||||
|
||||
#### Fixed
|
||||
|
||||
* [#507](https://github.com/shlinkio/shlink/issues/507) Fixed error with too long original URLs by increasing size to the maximum value (2048) based on [the standard](https://stackoverflow.com/a/417184).
|
||||
* [#502](https://github.com/shlinkio/shlink/issues/502) Fixed error when providing the port as part of the domain on short URLs.
|
||||
* [#509](https://github.com/shlinkio/shlink/issues/509) Fixed error when trying to generate a QR code for a short URL which uses a custom domain.
|
||||
* [#522](https://github.com/shlinkio/shlink/issues/522) Highly mitigated errors thrown when lots of short URLs are created concurrently including new and existing tags.
|
||||
|
||||
|
||||
## 1.19.0 - 2019-10-05
|
||||
|
||||
#### Added
|
||||
|
||||
* [#482](https://github.com/shlinkio/shlink/issues/482) Added support to serve shlink under a sub path.
|
||||
|
||||
The `router.base_path` config option can be defined now to set the base path from which shlink is served.
|
||||
|
||||
```php
|
||||
return [
|
||||
'router' => [
|
||||
'base_path' => '/foo/bar',
|
||||
],
|
||||
];
|
||||
```
|
||||
|
||||
This option will also be available on shlink-installer 1.3.0, so the installer will ask for it. It can also be provided for the docker image as the `BASE_PATH` env var.
|
||||
|
||||
* [#479](https://github.com/shlinkio/shlink/issues/479) Added preliminary support for multiple domains.
|
||||
|
||||
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.
|
||||
|
||||
When resolving a short URL to redirect end users, the following rules are applied:
|
||||
|
||||
* If the domain used for the request plus the short code/slug are found, the user is redirected to that long URL and the visit is tracked.
|
||||
* If the domain is not known but the short code/slug is defined for default domain, the user is redirected there and the visit is tracked.
|
||||
* In any other case, no redirection happens and no visit is tracked (if a fall back redirection is configured for not-found URLs, it will still happen).
|
||||
|
||||
#### Changed
|
||||
|
||||
* [#486](https://github.com/shlinkio/shlink/issues/486) Updated to [shlink-installer](https://github.com/shlinkio/shlink-installer) v2, which supports asking for base path in which shlink is served.
|
||||
|
||||
#### Deprecated
|
||||
|
||||
* *Nothing*
|
||||
|
||||
#### Removed
|
||||
|
||||
* [#435](https://github.com/shlinkio/shlink/issues/435) Removed translations for error pages. All error pages are in english now.
|
||||
|
||||
#### Fixed
|
||||
|
||||
* *Nothing*
|
||||
|
||||
|
||||
## 1.18.1 - 2019-08-24
|
||||
|
||||
#### Added
|
||||
|
||||
* *Nothing*
|
||||
|
||||
#### Changed
|
||||
|
||||
* [#450](https://github.com/shlinkio/shlink/issues/450) Added PHP 7.4 to the build matrix, as an allowed-to-fail env.
|
||||
* [#441](https://github.com/shlinkio/shlink/issues/441) and [#443](https://github.com/shlinkio/shlink/issues/443) Split some logic into independent modules.
|
||||
* [#451](https://github.com/shlinkio/shlink/issues/451) Updated to infection 0.13.
|
||||
* [#467](https://github.com/shlinkio/shlink/issues/467) Moved docker image config to main Shlink repo.
|
||||
|
||||
#### Deprecated
|
||||
|
||||
* [#428](https://github.com/shlinkio/shlink/issues/428) Deprecated preview-generation feature. It will keep working but it will be removed in Shlink v2.0.0
|
||||
|
||||
#### Removed
|
||||
|
||||
* [#468](https://github.com/shlinkio/shlink/issues/468) Removed APCu extension from docker image.
|
||||
|
||||
#### Fixed
|
||||
|
||||
* [#449](https://github.com/shlinkio/shlink/issues/449) Fixed error when trying to save too big referrers on PostgreSQL.
|
||||
|
||||
|
||||
## 1.18.0 - 2019-08-08
|
||||
|
||||
#### Added
|
||||
|
||||
* [#411](https://github.com/shlinkio/shlink/issues/411) Added new `meta` property on the `ShortUrl` REST API model.
|
||||
|
||||
These endpoints are affected and include the new property when suitable:
|
||||
|
||||
* `GET /short-urls` - List short URLs.
|
||||
* `GET /short-urls/shorten` - Create a short URL (for integrations).
|
||||
* `GET /short-urls/{shortCode}` - Get one short URL.
|
||||
* `POST /short-urls` - Create short URL.
|
||||
|
||||
The property includes the values `validSince`, `validUntil` and `maxVisits` in a single object. All of them are nullable.
|
||||
|
||||
```json
|
||||
{
|
||||
"validSince": "2016-01-01T00:00:00+02:00",
|
||||
"validUntil": null,
|
||||
"maxVisits": 100
|
||||
}
|
||||
```
|
||||
|
||||
* [#285](https://github.com/shlinkio/shlink/issues/285) Visit location resolution is now done asynchronously but in real time thanks to swoole task management.
|
||||
|
||||
Now, when a short URL is visited, a task is enqueued to locate it. The user is immediately redirected to the long URL, and in the background, the visit is located, making stats to be available a couple of seconds after the visit without the requirement of cronjobs being run constantly.
|
||||
|
||||
Sadly, this feature is not enabled when serving shlink via apache/nginx, where you should still rely on cronjobs.
|
||||
|
||||
* [#384](https://github.com/shlinkio/shlink/issues/384) Improved how remote IP addresses are detected.
|
||||
|
||||
This new set of headers is now also inspected looking for the IP address:
|
||||
|
||||
* CF-Connecting-IP
|
||||
* True-Client-IP
|
||||
* X-Real-IP
|
||||
|
||||
* [#440](https://github.com/shlinkio/shlink/pull/440) Created `db:create` command, which improves how the shlink database is created, with these benefits:
|
||||
|
||||
* It sets up a lock which prevents the command to be run concurrently.
|
||||
* It checks of the database does not exist, and creates it in that case.
|
||||
* It checks if the database tables already exist, exiting gracefully in that case.
|
||||
|
||||
* [#442](https://github.com/shlinkio/shlink/pull/442) Created `db:migrate` command, which improves doctrine's migrations command by generating a lock, preventing it to be run concurrently.
|
||||
|
||||
#### Changed
|
||||
|
||||
* [#430](https://github.com/shlinkio/shlink/issues/430) Updated to [shlinkio/php-coding-standard](https://github.com/shlinkio/php-coding-standard) 1.2.2
|
||||
* [#305](https://github.com/shlinkio/shlink/issues/305) Implemented changes which will allow Shlink to be truly clusterizable.
|
||||
* [#262](https://github.com/shlinkio/shlink/issues/262) Increased mutation score to 75%.
|
||||
|
||||
#### Deprecated
|
||||
|
||||
* *Nothing*
|
||||
|
||||
#### Removed
|
||||
|
||||
* *Nothing*
|
||||
|
||||
#### Fixed
|
||||
|
||||
* [#416](https://github.com/shlinkio/shlink/issues/416) Fixed error thrown when trying to locate visits after the GeoLite2 DB is downloaded for the first time.
|
||||
* [#424](https://github.com/shlinkio/shlink/issues/424) Updated wkhtmltoimage to version 0.12.5
|
||||
* [#427](https://github.com/shlinkio/shlink/issues/427) and [#434](https://github.com/shlinkio/shlink/issues/434) Fixed shlink being unusable after a database error on swoole contexts.
|
||||
|
||||
|
||||
## 1.17.0 - 2019-05-13
|
||||
|
||||
#### Added
|
||||
|
||||
* [#377](https://github.com/shlinkio/shlink/issues/377) Updated `visit:locate` command (formerly `visit:process`) to automatically update the GeoLite2 database if it is too old or it does not exist.
|
||||
|
||||
This simplifies processing visits in a container-based infrastructure, since a fresh container is capable of getting an updated version of the file by itself.
|
||||
|
||||
It also removes the need of asynchronously and programmatically updating the file, which deprecates the `visit:update-db` command.
|
||||
|
||||
* [#373](https://github.com/shlinkio/shlink/issues/373) Added support for a simplified config. Specially useful to use with the docker container.
|
||||
|
||||
#### Changed
|
||||
|
||||
* [#56](https://github.com/shlinkio/shlink/issues/56) Simplified supported cache, requiring APCu always.
|
||||
|
||||
#### Deprecated
|
||||
|
||||
* [#406](https://github.com/shlinkio/shlink/issues/406) Deprecated `PUT /short-urls/{shortCode}` REST endpoint in favor of `PATCH /short-urls/{shortCode}`.
|
||||
|
||||
#### Removed
|
||||
|
||||
* [#385](https://github.com/shlinkio/shlink/issues/385) Dropped support for PHP 7.1
|
||||
* [#379](https://github.com/shlinkio/shlink/issues/379) Removed copyright from error templates.
|
||||
|
||||
#### Fixed
|
||||
|
||||
* *Nothing*
|
||||
|
||||
|
||||
## 1.16.3 - 2019-03-30
|
||||
|
||||
#### Added
|
||||
|
||||
* *Nothing*
|
||||
|
||||
#### Changed
|
||||
|
||||
* [#153](https://github.com/shlinkio/shlink/issues/153) Updated to [doctrine/migrations](https://github.com/doctrine/migrations) version 2.0.0
|
||||
* [#376](https://github.com/shlinkio/shlink/issues/376) Allowed `visit:update-db` command to not return an error exit code even if download fails, by passing the `-i` flag.
|
||||
* [#341](https://github.com/shlinkio/shlink/issues/341) Improved database tests so that they are executed against all supported database engines.
|
||||
|
||||
#### Deprecated
|
||||
|
||||
* *Nothing*
|
||||
|
||||
#### Removed
|
||||
|
||||
* *Nothing*
|
||||
|
||||
#### Fixed
|
||||
|
||||
* [#382](https://github.com/shlinkio/shlink/issues/382) Fixed existing short URLs not properly checked when providing the `findIfExists` flag.
|
||||
|
||||
|
||||
## 1.16.2 - 2019-03-05
|
||||
|
||||
#### Added
|
||||
|
||||
* *Nothing*
|
||||
|
||||
#### Changed
|
||||
|
||||
* *Nothing*
|
||||
|
||||
#### Deprecated
|
||||
|
||||
* *Nothing*
|
||||
|
||||
#### Removed
|
||||
|
||||
* *Nothing*
|
||||
|
||||
#### Fixed
|
||||
|
||||
* [#368](https://github.com/shlinkio/shlink/issues/368) Fixed error produced when running a `SELECT COUNT(...)` with `ORDER BY` in PostgreSQL databases.
|
||||
|
||||
|
||||
## 1.16.1 - 2019-02-26
|
||||
|
||||
#### Added
|
||||
|
||||
* *Nothing*
|
||||
|
||||
#### Changed
|
||||
|
||||
* [#363](https://github.com/shlinkio/shlink/issues/363) Updated to `shlinkio/php-coding-standard` version 1.1.0
|
||||
|
||||
#### Deprecated
|
||||
|
||||
* *Nothing*
|
||||
|
||||
#### Removed
|
||||
|
||||
* *Nothing*
|
||||
|
||||
#### Fixed
|
||||
|
||||
* [#362](https://github.com/shlinkio/shlink/issues/362) Fixed all visits without an IP address being processed every time the `visit:process` command is executed.
|
||||
|
||||
|
||||
## 1.16.0 - 2019-02-23
|
||||
|
||||
#### Added
|
||||
|
||||
* [#304](https://github.com/shlinkio/shlink/issues/304) Added health endpoint to check healthiness of the service. Useful in container-based infrastructures.
|
||||
|
||||
Call [GET /rest/health] in order to get a response like this:
|
||||
|
||||
```http
|
||||
HTTP/1.1 200 OK
|
||||
Content-Type: application/health+json
|
||||
Content-Length: 681
|
||||
|
||||
{
|
||||
"status": "pass",
|
||||
"version": "1.16.0",
|
||||
"links": {
|
||||
"about": "https://shlink.io",
|
||||
"project": "https://github.com/shlinkio/shlink"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The status code can be `200 OK` in case of success or `503 Service Unavailable` in case of error, while the `status` property will be one of `pass` or `fail`, as defined in the [Health check RFC](https://inadarei.github.io/rfc-healthcheck/).
|
||||
|
||||
* [#279](https://github.com/shlinkio/shlink/issues/279) Added new `findIfExists` flag to the `[POST /short-url]` REST endpoint and the `short-urls:generate` CLI command. It can be used to return existing short URLs when found, instead of creating new ones.
|
||||
|
||||
Thanks to this flag you won't need to remember if you created a short URL for a long one. It will just create it if needed or return the existing one if possible.
|
||||
|
||||
The behavior might be a little bit counterintuitive when combined with other params. This is how the endpoint behaves when providing this new flag:
|
||||
|
||||
* Only the long URL is provided: It will return the newest match or create a new short URL if none is found.
|
||||
* Long url and custom slug are provided: It will return the short URL when both params match, return an error when the slug is in use for another long URL, or create a new short URL otherwise.
|
||||
* Any of the above but including other params (tags, validSince, validUntil, maxVisits): It will behave the same as the previous two cases, but it will try to exactly match existing results using all the params. If any of them does not match, it will try to create a new short URL.
|
||||
|
||||
* [#336](https://github.com/shlinkio/shlink/issues/336) Added an API test suite which performs API calls to an actual instance of the web service.
|
||||
|
||||
#### Changed
|
||||
|
||||
* [#342](https://github.com/shlinkio/shlink/issues/342) The installer no longer asks for a charset to be provided, and instead, it shuffles the base62 charset.
|
||||
* [#320](https://github.com/shlinkio/shlink/issues/320) Replaced query builder by plain DQL for all queries which do not need to be dynamically generated.
|
||||
* [#330](https://github.com/shlinkio/shlink/issues/330) No longer allow failures on PHP 7.3 envs during project CI build.
|
||||
* [#335](https://github.com/shlinkio/shlink/issues/335) Renamed functional test suite to database test suite, since that better describes what it actually does.
|
||||
* [#346](https://github.com/shlinkio/shlink/issues/346) Extracted installer as an independent tool.
|
||||
* [#261](https://github.com/shlinkio/shlink/issues/261) Increased mutation score to 70%.
|
||||
|
||||
#### Deprecated
|
||||
|
||||
* [#351](https://github.com/shlinkio/shlink/issues/351) Deprecated `config:generate-charset` and `config:generate-secret` CLI commands.
|
||||
|
||||
#### Removed
|
||||
|
||||
* *Nothing*
|
||||
|
||||
#### Fixed
|
||||
|
||||
* [#317](https://github.com/shlinkio/shlink/issues/317) Fixed error while trying to generate previews because of global config file being deleted by mistake by build script.
|
||||
* [#307](https://github.com/shlinkio/shlink/issues/307) Fixed memory leak while trying to process huge amounts of visits due to the query not being properly paginated.
|
||||
|
||||
|
||||
## 1.15.1 - 2018-12-16
|
||||
|
||||
#### Added
|
||||
|
||||
* [#162](https://github.com/shlinkio/shlink/issues/162) Added non-rest endpoints to swagger definition.
|
||||
|
||||
#### Changed
|
||||
|
||||
* [#312](https://github.com/shlinkio/shlink/issues/312) Now all config files both in `php` and `json` format are loaded from `config/params` folder, easing users to provided customizations to docker image.
|
||||
* [#226](https://github.com/shlinkio/shlink/issues/226) Updated how table are rendered in CLI commands, making use of new features in Symfony 4.2.
|
||||
* [#321](https://github.com/shlinkio/shlink/issues/321) Extracted entities mappings from entities to external config files.
|
||||
* [#308](https://github.com/shlinkio/shlink/issues/308) Automated docker image building.
|
||||
|
||||
#### Deprecated
|
||||
|
||||
* *Nothing*
|
||||
|
||||
#### Removed
|
||||
|
||||
* [#301](https://github.com/shlinkio/shlink/issues/301) Removed custom `AccessLogFactory` in favor of the implementation included in [zendframework/zend-expressive-swoole](https://github.com/zendframework/zend-expressive-swoole) v2.2.0
|
||||
|
||||
#### 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.
|
||||
|
||||
|
||||
## 1.15.0 - 2018-12-02
|
||||
|
||||
#### Added
|
||||
|
||||
* [#208](https://github.com/shlinkio/shlink/issues/208) Added initial support to run shlink using [swoole](https://www.swoole.co.uk/), a non-blocking IO server which improves the performance of shlink from 4 to 10 times.
|
||||
|
||||
Run shlink with `./vendor/bin/zend-expressive-swoole start` to start-up the service, which will be exposed in port `8080`.
|
||||
|
||||
Adding the `-d` flag, it will be started as a background service. Then you can use the `./vendor/bin/zend-expressive-swoole stop` command in order to stop it.
|
||||
|
||||
* [#266](https://github.com/shlinkio/shlink/issues/266) Added pagination to `GET /short-urls/{shortCode}/visits` endpoint.
|
||||
|
||||
In order to make it backwards compatible, it keeps returning all visits by default, but it now allows to provide the `page` and `itemsPerPage` query parameters in order to configure the number of items to get.
|
||||
|
||||
#### Changed
|
||||
|
||||
* [#267](https://github.com/shlinkio/shlink/issues/267) API responses and the CLI interface is no longer translated and uses english always. Only not found error templates are still translated.
|
||||
* [#289](https://github.com/shlinkio/shlink/issues/289) Extracted coding standard rules to a separated package.
|
||||
* [#273](https://github.com/shlinkio/shlink/issues/273) Improved code coverage in repository classes.
|
||||
|
||||
#### Deprecated
|
||||
|
||||
* *Nothing*
|
||||
|
||||
#### Removed
|
||||
|
||||
* *Nothing*
|
||||
|
||||
#### Fixed
|
||||
|
||||
* [#278](https://github.com/shlinkio/shlink/pull/278) Added missing `X-Api-Key` header to the list of valid cross domain headers.
|
||||
* [#295](https://github.com/shlinkio/shlink/pull/295) Fixed custom slugs so that they are case sensitive and do not try to lowercase provided values.
|
||||
|
||||
|
||||
## 1.14.1 - 2018-11-17
|
||||
|
||||
#### Added
|
||||
|
||||
* *Nothing*
|
||||
|
||||
#### Changed
|
||||
|
||||
* [#260](https://github.com/shlinkio/shlink/issues/260) Increased mutation score to 65%.
|
||||
|
||||
#### Deprecated
|
||||
|
||||
* *Nothing*
|
||||
|
||||
#### Removed
|
||||
|
||||
* *Nothing*
|
||||
|
||||
#### Fixed
|
||||
|
||||
* [#271](https://github.com/shlinkio/shlink/issues/271) Fixed memory leak produced when processing high amounts of visits at the same time.
|
||||
* [#272](https://github.com/shlinkio/shlink/issues/272) Fixed errors produced when trying to process visits multiple times in parallel, by using a lock which ensures only one instance is run at a time.
|
||||
|
||||
|
||||
## 1.14.0 - 2018-11-16
|
||||
|
||||
#### Added
|
||||
|
||||
* [#236](https://github.com/shlinkio/shlink/issues/236) Added option to define a redirection to a custom URL when a user hits an invalid short URL.
|
||||
|
||||
It only affects URLs matched as "short URL" where the short code is invalid, not any 404 that happens in the app. For example, a request to the path `/foo/bar` will keep returning a 404.
|
||||
|
||||
This new option will be asked by the installer both for new shlink installations and for any previous shlink version which is updated.
|
||||
|
||||
* [#189](https://github.com/shlinkio/shlink/issues/189) and [#240](https://github.com/shlinkio/shlink/issues/240) Added new [GeoLite2](https://dev.maxmind.com/geoip/geoip2/geolite2/)-based geolocation service which is faster and more reliable than previous one.
|
||||
|
||||
It does not have API limit problems, since it uses a local database file.
|
||||
|
||||
Previous service is still used as a fallback in case GeoLite DB does not contain any IP address.
|
||||
|
||||
#### 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`.
|
||||
* [#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.
|
||||
* [#253](https://github.com/shlinkio/shlink/issues/253) Increased `user_agent` column length in `visits` table to 512.
|
||||
* [#256](https://github.com/shlinkio/shlink/issues/256) Updated to Infection v0.11.
|
||||
* [#202](https://github.com/shlinkio/shlink/issues/202) Added missing response examples to OpenAPI docs.
|
||||
|
||||
#### Deprecated
|
||||
|
||||
* *Nothing*
|
||||
|
||||
#### Removed
|
||||
|
||||
* *Nothing*
|
||||
|
||||
#### Fixed
|
||||
|
||||
* [#223](https://github.com/shlinkio/shlink/issues/223) Fixed PHPStan errors produced with symfony/console 4.1.5
|
||||
|
||||
|
||||
## 1.13.2 - 2018-10-18
|
||||
|
||||
#### Added
|
||||
|
||||
* [#233](https://github.com/shlinkio/shlink/issues/233) Added PHP 7.3 to build matrix allowing its failure.
|
||||
|
||||
#### Changed
|
||||
|
||||
* [#235](https://github.com/shlinkio/shlink/issues/235) Improved update instructions (thanks to [tivyhosting](https://github.com/tivyhosting)).
|
||||
|
||||
#### Deprecated
|
||||
|
||||
* *Nothing*
|
||||
|
||||
#### Removed
|
||||
|
||||
* *Nothing*
|
||||
|
||||
#### Fixed
|
||||
|
||||
* [#237](https://github.com/shlinkio/shlink/issues/233) Solved errors when trying to geo-locate `null` IP addresses.
|
||||
|
||||
Also improved how visitor IP addresses are discovered, thanks to [akrabat/ip-address-middleware](https://github.com/akrabat/ip-address-middleware) package.
|
||||
|
||||
|
||||
## 1.13.1 - 2018-10-16
|
||||
|
||||
#### Added
|
||||
|
||||
* *Nothing*
|
||||
|
||||
#### Changed
|
||||
|
||||
* *Nothing*
|
||||
|
||||
#### Deprecated
|
||||
|
||||
* *Nothing*
|
||||
|
||||
#### Removed
|
||||
|
||||
* *Nothing*
|
||||
|
||||
#### Fixed
|
||||
|
||||
* [#231](https://github.com/shlinkio/shlink/issues/197) Fixed error when processing visits.
|
||||
|
||||
|
||||
## 1.13.0 - 2018-10-06
|
||||
|
||||
#### Added
|
||||
|
||||
* [#197](https://github.com/shlinkio/shlink/issues/197) Added [cakephp/chronos](https://book.cakephp.org/3.0/en/chronos.html) library for date manipulations.
|
||||
* [#214](https://github.com/shlinkio/shlink/issues/214) Improved build script, which allows builds to be done without "jumping" outside the project directory, and generates smaller dist files.
|
||||
|
||||
It also allows automating the dist file generation in travis-ci builds.
|
||||
|
||||
* [#207](https://github.com/shlinkio/shlink/issues/207) Added two new config options which are asked during installation process. The config options already existed in previous shlink version, but you had to manually set their values.
|
||||
|
||||
These are the new options:
|
||||
|
||||
* Visits threshold to allow short URLs to be deleted.
|
||||
* Check the visits threshold when trying to delete a short URL via REST API.
|
||||
|
||||
#### Changed
|
||||
|
||||
* [#211](https://github.com/shlinkio/shlink/issues/211) Extracted installer to its own module, which will simplify moving it to a separated package in the future.
|
||||
* [#200](https://github.com/shlinkio/shlink/issues/200) and [#201](https://github.com/shlinkio/shlink/issues/201) Renamed REST Action classes and CLI Command classes to use the concept of `ShortUrl` instead of the concept of `ShortCode` when referring to the entity, and left the `short code` concept to the identifier which is used as a unique code for a specific `Short URL`.
|
||||
* [#181](https://github.com/shlinkio/shlink/issues/181) When importing the configuration from a previous shlink installation, it no longer asks to import every block. Instead, it is capable of detecting only new config options introduced in the new version, and ask only for those.
|
||||
|
||||
If no new options are found and you have selected to import config, no further questions will be asked and shlink will just import the old config.
|
||||
|
||||
#### Deprecated
|
||||
|
||||
* [#205](https://github.com/shlinkio/shlink/issues/205) Deprecated `[POST /authenticate]` endpoint, and allowed any API request to be automatically authenticated using the `X-Api-Key` header with a valid API key.
|
||||
|
||||
This effectively deprecates the `Authorization: Bearer <JWT>` authentication form, but it will keep working.
|
||||
|
||||
* As of [#200](https://github.com/shlinkio/shlink/issues/200) and [#201](https://github.com/shlinkio/shlink/issues/201) REST urls have changed from `/short-codes/...` to `/short-urls/...`, and the command namespaces have changed from `short-code:...` to `short-url:...`.
|
||||
|
||||
In both cases, backwards compatibility has been retained and the old ones are aliases for the new ones, but the old ones are considered deprecated.
|
||||
|
||||
#### Removed
|
||||
|
||||
* *Nothing*
|
||||
|
||||
#### Fixed
|
||||
|
||||
* [#203](https://github.com/shlinkio/shlink/issues/203) Fixed some warnings thrown while unzipping distributable files.
|
||||
* [#206](https://github.com/shlinkio/shlink/issues/206) An error is now thrown during installation if any required param is left empty, making the installer display a message and ask again until a value is set.
|
||||
|
||||
|
||||
## 1.12.0 - 2018-09-15
|
||||
|
||||
#### Added
|
||||
|
||||
* [#187](https://github.com/shlinkio/shlink/issues/187) Included an API endpoint and a CLI command to delete short URLs.
|
||||
|
||||
Due to the implicit danger of this operation, the deletion includes a safety check. URLs cannot be deleted if they have more than a specific amount of visits.
|
||||
|
||||
The visits threshold is set to **15** by default and currently it has to be manually changed. In future versions the installation/update process will ask you about the value of the visits threshold.
|
||||
|
||||
In order to change it, open the `config/autoload/delete_short_urls.global.php` file, which has this structure:
|
||||
|
||||
```php
|
||||
return [
|
||||
|
||||
'delete_short_urls' => [
|
||||
'visits_threshold' => 15,
|
||||
'check_visits_threshold' => true,
|
||||
],
|
||||
|
||||
];
|
||||
```
|
||||
|
||||
Properties are self explanatory. Change `check_visits_threshold` to `false` to completely disable this safety check, and change the value of `visits_threshold` to allow short URLs with a different number of visits to be deleted.
|
||||
|
||||
Once changed, delete the `data/cache/app_config.php` file (if any) to let shlink know about the new values.
|
||||
|
||||
This check is implicit for the API endpoint, but can be "disabled" for the CLI command, which will ask you when trying to delete a URL which has reached to threshold in order to force the deletion.
|
||||
|
||||
* [#183](https://github.com/shlinkio/shlink/issues/183) and [#190](https://github.com/shlinkio/shlink/issues/190) Included important documentation improvements in the repository itself. You no longer need to go to the website in order to see how to install or use shlink.
|
||||
* [#186](https://github.com/shlinkio/shlink/issues/186) Added a small robots.txt file that prevents 404 errors to be logged due to search engines trying to index the domain where shlink is located. Thanks to [@robwent](https://github.com/robwent) for the contribution.
|
||||
|
||||
#### Changed
|
||||
|
||||
* [#145](https://github.com/shlinkio/shlink/issues/145) Shlink now obfuscates IP addresses from visitors by replacing the latest octet by `0`, which does not affect geolocation and allows it to fulfil the GDPR.
|
||||
|
||||
Other known services follow this same approach, like [Google Analytics](https://support.google.com/analytics/answer/2763052?hl=en) or [Matomo](https://matomo.org/docs/privacy/#step-1-automatically-anonymize-visitor-ips)
|
||||
|
||||
* [#182](https://github.com/shlinkio/shlink/issues/182) The short URL creation API endpoints now return the same model used for lists and details endpoints.
|
||||
|
||||
#### Deprecated
|
||||
|
||||
* *Nothing*
|
||||
|
||||
#### Removed
|
||||
|
||||
* *Nothing*
|
||||
|
||||
#### Fixed
|
||||
|
||||
* [#188](https://github.com/shlinkio/shlink/issues/188) Shlink now allows multiple short URLs to be created that resolve to the same long URL.
|
||||
|
||||
|
||||
## 1.11.0 - 2018-08-13
|
||||
|
||||
#### Added
|
||||
|
||||
* [#170](https://github.com/shlinkio/shlink/issues/170) and [#171](https://github.com/shlinkio/shlink/issues/171) Updated `[GET /short-codes]` and `[GET /short-codes/{shortCode}]` endpoints to return more meaningful information and make their response consistent.
|
||||
|
||||
The short URLs are now represented by this object in both cases:
|
||||
|
||||
```json
|
||||
{
|
||||
"shortCode": "12Kb3",
|
||||
"shortUrl": "https://doma.in/12Kb3",
|
||||
"longUrl": "https://shlink.io",
|
||||
"dateCreated": "2016-05-01T20:34:16+02:00",
|
||||
"visitsCount": 1029,
|
||||
"tags": [
|
||||
"shlink"
|
||||
],
|
||||
"originalUrl": "https://shlink.io"
|
||||
}
|
||||
```
|
||||
|
||||
The `originalUrl` property is considered deprecated and has been kept for backward compatibility purposes. It holds the same value as the `longUrl` property.
|
||||
|
||||
#### Changed
|
||||
|
||||
* *Nothing*
|
||||
|
||||
#### Deprecated
|
||||
|
||||
* The `originalUrl` property in `[GET /short-codes]` and `[GET /short-codes/{shortCode}]` endpoints is now deprecated and replaced by the `longUrl` property.
|
||||
|
||||
#### Removed
|
||||
|
||||
* *Nothing*
|
||||
|
||||
#### Fixed
|
||||
|
||||
* *Nothing*
|
||||
|
||||
|
||||
## 1.10.2 - 2018-08-04
|
||||
|
||||
#### Added
|
||||
|
||||
57
Dockerfile
Normal file
57
Dockerfile
Normal file
@@ -0,0 +1,57 @@
|
||||
FROM php:7.4.1-alpine3.10
|
||||
LABEL maintainer="Alejandro Celaya <alejandro@alejandrocelaya.com>"
|
||||
|
||||
ARG SHLINK_VERSION=2.0.0
|
||||
ENV SHLINK_VERSION ${SHLINK_VERSION}
|
||||
ENV SWOOLE_VERSION 4.4.12
|
||||
ENV COMPOSER_VERSION 1.9.1
|
||||
|
||||
WORKDIR /etc/shlink
|
||||
|
||||
RUN \
|
||||
# Install mysl and calendar
|
||||
docker-php-ext-install -j"$(nproc)" pdo_mysql calendar && \
|
||||
# Install sqlite
|
||||
apk add --no-cache sqlite-libs sqlite-dev && \
|
||||
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 swoole
|
||||
# First line fixes an error when installing pecl extensions. Found in https://github.com/docker-library/php/issues/233
|
||||
RUN apk add --no-cache --virtual .phpize-deps ${PHPIZE_DEPS} && \
|
||||
pecl install swoole-${SWOOLE_VERSION} && \
|
||||
docker-php-ext-enable swoole && \
|
||||
apk del .phpize-deps
|
||||
|
||||
# Install shlink
|
||||
COPY . .
|
||||
RUN rm -rf ./docker && \
|
||||
wget https://getcomposer.org/download/${COMPOSER_VERSION}/composer.phar && \
|
||||
php composer.phar install --no-dev --optimize-autoloader --prefer-dist --no-progress --no-interaction && \
|
||||
php composer.phar clear-cache && \
|
||||
rm composer.*
|
||||
|
||||
# Add shlink to the path to ease running it after container is created
|
||||
RUN ln -s /etc/shlink/bin/cli /usr/local/bin/shlink
|
||||
RUN sed -i "s/%SHLINK_VERSION%/${SHLINK_VERSION}/g" config/autoload/app_options.global.php
|
||||
|
||||
# Expose swoole port
|
||||
EXPOSE 8080
|
||||
|
||||
# Expose params config dir, since the user is expected to provide custom config from there
|
||||
VOLUME /etc/shlink/config/params
|
||||
|
||||
# Copy config specific for the image
|
||||
COPY docker/docker-entrypoint.sh docker-entrypoint.sh
|
||||
COPY docker/config/shlink_in_docker.local.php config/autoload/shlink_in_docker.local.php
|
||||
COPY docker/config/php.ini ${PHP_INI_DIR}/conf.d/
|
||||
|
||||
ENTRYPOINT ["/bin/sh", "./docker-entrypoint.sh"]
|
||||
2
LICENSE
2
LICENSE
@@ -1,6 +1,6 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2017 Alejandro Celaya
|
||||
Copyright (c) 2016-2020 Alejandro Celaya
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
288
README.md
288
README.md
@@ -1,9 +1,283 @@
|
||||
# Shlink
|
||||

|
||||
|
||||
[](https://travis-ci.org/shlinkio/shlink)
|
||||
[](https://scrutinizer-ci.com/g/shlinkio/shlink/?branch=master)
|
||||
[](https://scrutinizer-ci.com/g/shlinkio/shlink/?branch=master)
|
||||
[](https://packagist.org/packages/shlinkio/shlink)
|
||||
[](https://packagist.org/packages/shlinkio/shlink)
|
||||
[](https://travis-ci.org/shlinkio/shlink)
|
||||
[](https://scrutinizer-ci.com/g/shlinkio/shlink/?branch=master)
|
||||
[](https://scrutinizer-ci.com/g/shlinkio/shlink/?branch=master)
|
||||
[](https://packagist.org/packages/shlinkio/shlink)
|
||||
[](https://github.com/shlinkio/shlink/blob/master/LICENSE)
|
||||
[](https://acel.me/donate)
|
||||
|
||||
A PHP-based URL shortener application with analytics and management
|
||||
A PHP-based self-hosted URL shortener that can be used to serve shortened URLs under your own custom domain.
|
||||
|
||||
> This document references Shlink 2.x. If you are using an older version and want to upgrade, follow the [UPGRADE](UPGRADE.md) doc.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
- [Installation](#installation)
|
||||
- [Download](#download)
|
||||
- [Configure](#configure)
|
||||
- [Serve](#serve)
|
||||
- [Bonus](#bonus)
|
||||
- [Update to new version](#update-to-new-version)
|
||||
- [Using a docker image](#using-a-docker-image)
|
||||
- [Using shlink](#using-shlink)
|
||||
- [Shlink CLI Help](#shlink-cli-help)
|
||||
|
||||
## Installation
|
||||
|
||||
> These are the steps needed to install Shlink if you plan to manually host it.
|
||||
>
|
||||
> Alternatively, you can use the official docker image. If that's your intention, jump directly to [Using a docker image](#using-a-docker-image)
|
||||
|
||||
First, make sure the host where you are going to run shlink fulfills these requirements:
|
||||
|
||||
* PHP 7.4 or greater with JSON, APCu, intl, curl, PDO and gd extensions enabled.
|
||||
* MySQL, MariaDB, PostgreSQL or SQLite.
|
||||
* The web server of your choice with PHP integration (Apache or Nginx recommended).
|
||||
|
||||
### Download
|
||||
|
||||
In order to run Shlink, you will need a built version of the project. There are two ways to get it.
|
||||
|
||||
* **Using a dist file**
|
||||
|
||||
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_x.x.x_dist.zip` file you will find there.
|
||||
|
||||
Finally, decompress the file in the location of your choice.
|
||||
|
||||
* **Building from sources**
|
||||
|
||||
If for any reason you want to build the project yourself, follow these steps:
|
||||
|
||||
* 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 only used for the generated dist file).
|
||||
|
||||
After that, you will have a `shlink_x.x.x_dist.zip` dist file inside the `build` directory, that you need to decompress in the location fo 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 [travis](https://travis-ci.org/shlinkio/shlink), attaching the generated dist file to it.
|
||||
|
||||
### Configure
|
||||
|
||||
Despite how you built the project, you now need to configure it, by following these steps:
|
||||
|
||||
* If you are going to use MySQL, MariaDB or PostgreSQL, 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.
|
||||
|
||||
### Serve
|
||||
|
||||
Once Shlink is configured, you need to expose it to the web, either by using a traditional web server + fast CGI approach, or by using a [swoole](https://www.swoole.co.uk/) non-blocking server.
|
||||
|
||||
* **Using a web server:**
|
||||
|
||||
For example, assuming your domain is doma.in and shlink is in the `/path/to/shlink` folder, these would be the basic configurations for Nginx and Apache.
|
||||
|
||||
*Nginx:*
|
||||
|
||||
```nginx
|
||||
server {
|
||||
server_name doma.in;
|
||||
listen 80;
|
||||
root /path/to/shlink/public;
|
||||
index index.php;
|
||||
charset utf-8;
|
||||
|
||||
location / {
|
||||
try_files $uri $uri/ /index.php$is_args$args;
|
||||
}
|
||||
|
||||
location ~ \.php$ {
|
||||
fastcgi_split_path_info ^(.+\.php)(/.+)$;
|
||||
fastcgi_pass unix:/var/run/php/php7.2-fpm.sock;
|
||||
fastcgi_index index.php;
|
||||
include fastcgi.conf;
|
||||
}
|
||||
|
||||
location ~ /\.ht {
|
||||
deny all;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
*Apache:*
|
||||
|
||||
```apache
|
||||
<VirtualHost *:80>
|
||||
ServerName doma.in
|
||||
DocumentRoot "/path/to/shlink/public"
|
||||
|
||||
<Directory "/path/to/shlink/public">
|
||||
Options FollowSymLinks Includes ExecCGI
|
||||
AllowOverride all
|
||||
Order allow,deny
|
||||
Allow from all
|
||||
</Directory>
|
||||
</VirtualHost>
|
||||
```
|
||||
|
||||
* **Using swoole:**
|
||||
|
||||
First you need to install the swoole PHP extension with [pecl](https://pecl.php.net/package/swoole), `pecl install swoole`.
|
||||
|
||||
Once installed, it's actually pretty easy to get shlink up and running with swoole. Run `./vendor/bin/mezzio-swoole start -d` and you will get shlink running on port 8080.
|
||||
|
||||
However, by doing it this way, you are loosing all the access logs, and the service won't be automatically run if the server has to be restarted.
|
||||
|
||||
For that reason, you should create a daemon script, in `/etc/init.d/shlink_swoole`, like this one, replacing `/path/to/shlink` by the path to your shlink installation:
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
### BEGIN INIT INFO
|
||||
# Provides: shlink_swoole
|
||||
# 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
|
||||
### END INIT INFO
|
||||
|
||||
SCRIPT=/path/to/shlink/vendor/bin/mezzio-swoole\ start
|
||||
RUNAS=root
|
||||
|
||||
PIDFILE=/var/run/shlink_swoole.pid
|
||||
LOGDIR=/var/log/shlink
|
||||
LOGFILE=${LOGDIR}/shlink_swoole.log
|
||||
|
||||
start() {
|
||||
if [[ -f "$PIDFILE" ]] && kill -0 $(cat "$PIDFILE"); then
|
||||
echo 'Shlink with swoole already running' >&2
|
||||
return 1
|
||||
fi
|
||||
echo 'Starting shlink with swoole' >&2
|
||||
mkdir -p "$LOGDIR"
|
||||
touch "$LOGFILE"
|
||||
local CMD="$SCRIPT &> \"$LOGFILE\" & echo \$!"
|
||||
su -c "$CMD" $RUNAS > "$PIDFILE"
|
||||
echo 'Shlink started' >&2
|
||||
}
|
||||
|
||||
stop() {
|
||||
if [[ ! -f "$PIDFILE" ]] || ! kill -0 $(cat "$PIDFILE"); then
|
||||
echo 'Shlink with swoole not running' >&2
|
||||
return 1
|
||||
fi
|
||||
echo 'Stopping shlink with swoole' >&2
|
||||
kill -15 $(cat "$PIDFILE") && rm -f "$PIDFILE"
|
||||
echo 'Shlink stopped' >&2
|
||||
}
|
||||
|
||||
case "$1" in
|
||||
start)
|
||||
start
|
||||
;;
|
||||
stop)
|
||||
stop
|
||||
;;
|
||||
restart)
|
||||
stop
|
||||
start
|
||||
;;
|
||||
*)
|
||||
echo "Usage: $0 {start|stop|restart}"
|
||||
esac
|
||||
```
|
||||
|
||||
Then run these commands to enable the service and start it:
|
||||
|
||||
* `sudo chmod +x /etc/init.d/shlink_swoole`
|
||||
* `sudo update-rc.d shlink_swoole defaults`
|
||||
* `sudo update-rc.d shlink_swoole enable`
|
||||
* `/etc/init.d/shlink_swoole start`
|
||||
|
||||
Now again, you can access shlink on port 8080, but this time the service will be automatically run at system start-up, and all access logs will be written in `/var/log/shlink/shlink_swoole.log` (you will probably want to [rotate those logs](https://www.digitalocean.com/community/tutorials/how-to-manage-logfiles-with-logrotate-on-ubuntu-16-04). You can find an example logrotate config file [here](data/infra/examples/shlink-daemon-logrotate.conf)).
|
||||
|
||||
Finally access to [https://app.shlink.io](https://app.shlink.io) and configure your server to start creating short URLs.
|
||||
|
||||
### Bonus
|
||||
|
||||
Geo-locating visits to your short links is a time-consuming task. When serving Shlink with swoole, the geo-location task is automatically run asynchronously just after a visit to a short URL happens.
|
||||
|
||||
However, if you are not serving Shlink with swoole, you will have to schedule the geo-location task to be run regularly in the background (for example, using cron jobs):
|
||||
|
||||
The command you need to run is `/path/to/shlink/bin/cli visit:locate`, and you can optionally provide the `-q` flag to remove any output and avoid your cron logs to be polluted.
|
||||
|
||||
## Update to new version
|
||||
|
||||
When a new Shlink version is available, you don't need to repeat the entire process. Instead, follow these steps:
|
||||
|
||||
1. Rename your existing Shlink directory to something else (ie. `shlink` ---> `shlink-old`).
|
||||
2. Download and extract the new version of Shlink, and set the directory name to that of the old version (ie. `shlink`).
|
||||
3. Run the `bin/update` script in the new version's directory to migrate your configuration over. You will be asked to provide the path to the old instance (ie. `shlink-old`).
|
||||
4. If you are using shlink with swoole, restart the service by running `/etc/init.d/shlink_swoole restart`.
|
||||
|
||||
The `bin/update` will use the location from previous shlink version to import the configuration. It will then update the database and generate some assets shlink needs to work.
|
||||
|
||||
**Important!** It is recommended that you don't skip any version when using this process. The update tool gets better on every version, but older versions might make assumptions.
|
||||
|
||||
## Using a 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](docker/README.md).
|
||||
|
||||
The idea is that you can just generate a container using the image and provide custom config via env vars.
|
||||
|
||||
## 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).
|
||||
|
||||
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.
|
||||
|
||||
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/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 you can host it yourself too.
|
||||
|
||||
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.
|
||||
|
||||
### Shlink CLI Help
|
||||
|
||||
```
|
||||
Usage:
|
||||
command [options] [arguments]
|
||||
|
||||
Options:
|
||||
-h, --help Display this help message
|
||||
-q, --quiet Do not output any message
|
||||
-V, --version Display this application version
|
||||
--ansi Force ANSI output
|
||||
--no-ansi Disable ANSI output
|
||||
-n, --no-interaction Do not ask any interactive question
|
||||
-v|vv|vvv, --verbose Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
|
||||
|
||||
Available commands:
|
||||
help Displays help for a command
|
||||
list Lists commands
|
||||
api-key
|
||||
api-key:disable Disables an API key.
|
||||
api-key:generate Generates a new valid API key.
|
||||
api-key:list Lists all the available API keys.
|
||||
db
|
||||
db:create Creates the database needed for shlink to work. It will do nothing if the database already exists
|
||||
db:migrate Runs database migrations, which will ensure the shlink database is up to date.
|
||||
short-url
|
||||
short-url:delete Deletes a short URL
|
||||
short-url:generate Generates a short URL for provided long URL and returns it
|
||||
short-url:list List all short URLs
|
||||
short-url:parse Returns the long URL behind a short code
|
||||
short-url:visits Returns the detailed visits information for provided short code
|
||||
tag
|
||||
tag:create Creates one or more tags.
|
||||
tag:delete Deletes one or more tags.
|
||||
tag:list Lists existing tags.
|
||||
tag:rename Renames one existing tag.
|
||||
visit
|
||||
visit:locate Resolves visits origin locations.
|
||||
```
|
||||
|
||||
> This product includes GeoLite2 data created by MaxMind, available from [https://www.maxmind.com](https://www.maxmind.com)
|
||||
|
||||
88
UPGRADE.md
Normal file
88
UPGRADE.md
Normal file
@@ -0,0 +1,88 @@
|
||||
# Upgrading
|
||||
|
||||
## From v1.x to v2.x
|
||||
|
||||
### PHP 7.4 required
|
||||
|
||||
This new version takes advantage of several new features introduced in PHP 7.4.
|
||||
|
||||
Thanks to that, the code is more reliable and robust, and easier to maintain and improve.
|
||||
|
||||
However, that means that any previous PHP version is no longer supported.
|
||||
|
||||
### Preview generation
|
||||
|
||||
The ability to generate website previews has been completely removed and has no replacement.
|
||||
|
||||
The feature never properly worked, and it wasn't really useful. Because of that, the feature is no longer available on Shlink 2.x
|
||||
|
||||
Removing this feature has these implications:
|
||||
|
||||
* The `short-url:process-previews` CLI command no longer exists, and an error will be thrown if executed.
|
||||
* The `/{shortCode}/preview` path is no longer valid, and will return a 404 status.
|
||||
|
||||
### Removed paths
|
||||
|
||||
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.
|
||||
|
||||
### Removed command and route aliases
|
||||
|
||||
All the aliases for the CLI commands in the `short-urls` namespace have been removed. If you were using any of those commands with the `shortcode` or `short-code` prefixes, make sure to update them to use the `short-urls` prefix instead.
|
||||
|
||||
The same happens for all REST endpoints starting with `/short-code`. They were previously aliased to `/short-urls` ones, but they will return a 404 now. Make sure to update them accordingly.
|
||||
|
||||
### JWT authentication removed
|
||||
|
||||
Shlink's REST API no longer accepts authentication using a JWT token. The API key has to be passed now in the `x-api-key` header.
|
||||
|
||||
Removing this feature has these implications:
|
||||
|
||||
* Shlink will no longer introspect the `Authorization` header for Bearer tokens.
|
||||
* The `POST /rest/v{version}/authenticate` endpoint no longer exists and will return a 404.
|
||||
|
||||
### API version is now required
|
||||
|
||||
Endpoints need to provide a version in the path now. Previously, not providing a version used to fall back to v1. Now, it will return a 404 status, as no route will match.
|
||||
|
||||
The only exception is the `/rest/health` endpoint, which will continue working without the version.
|
||||
|
||||
### API errors
|
||||
|
||||
Shlink v1.21.0 introduced support for API errors using the Problem Details format, as well as the v2 of the API.
|
||||
|
||||
For backwards compatibility reasons, requests performed to v1 continued to return the old `error` and `message` properties.
|
||||
|
||||
Starting with Shlink v2.0.0, both versions of the API will no longer return those two properties.
|
||||
|
||||
As a replacement, use `type` instead of `error`, and `detail` instead of `message`.
|
||||
|
||||
### Changes in models
|
||||
|
||||
The next REST API models have changed:
|
||||
|
||||
* **ShortUrl**: The `originalUrl` property was deprecated and has been removed. Use `longUrl` instead.
|
||||
* **Visit**: The `remoteAddr` property was deprecated and has been removed. It has no replacement.
|
||||
* **VisitLocation**: The `latitude` and `longitude` properties are no longer strings, but float.
|
||||
|
||||
### URL validation
|
||||
|
||||
Shlink can verify provided long URLs are valid before trying to shorten them. Starting with v2, it no longer does it by default and needs to be explicitly enabled instead of explicitly disabled.
|
||||
|
||||
### Removed config options
|
||||
|
||||
The `not_found_redirect_to` config option and the `NOT_FOUND_REDIRECT_TO` env var are no longer taken into consideration for the docker image.
|
||||
|
||||
Instead, use `invalid_short_url_redirect_to` and `INVALID_SHORT_URL_REDIRECT_TO` respectively.
|
||||
|
||||
### Migrated to Laminas
|
||||
|
||||
The project has been using Zend Framework components since the beginning. Since it has been re-branded as [Laminas](https://getlaminas.org/), this version updates to the new set of components.
|
||||
|
||||
Updating to Laminas components has these implications:
|
||||
|
||||
* If you were manually serving Shlink with swoole, the entry script has to be changed from `/path/to/shlink/vendor/bin/zend-expressive-swoole` to `/path/to/shlink/vendor/bin/mezzio-swoole`
|
||||
9
bin/cli
9
bin/cli
@@ -1,10 +1,7 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Interop\Container\ContainerInterface;
|
||||
use Symfony\Component\Console\Application as CliApp;
|
||||
|
||||
/** @var ContainerInterface $container */
|
||||
$container = include __DIR__ . '/../config/container.php';
|
||||
$container->get(CliApp::class)->run();
|
||||
$run = require __DIR__ . '/../config/run.php';
|
||||
$run(true);
|
||||
|
||||
31
bin/install
31
bin/install
@@ -1,29 +1,12 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
use Shlinkio\Shlink\CLI\Factory\InstallApplicationFactory;
|
||||
use Shlinkio\Shlink\CLI\Install\Plugin\DatabaseConfigCustomizer;
|
||||
use Symfony\Component\Console\Application;
|
||||
use Symfony\Component\Filesystem\Filesystem;
|
||||
use Zend\ServiceManager\AbstractFactory\ConfigAbstractFactory;
|
||||
use Zend\ServiceManager\Factory\InvokableFactory;
|
||||
use Zend\ServiceManager\ServiceManager;
|
||||
namespace Shlinkio\Shlink;
|
||||
|
||||
use function chdir;
|
||||
use function dirname;
|
||||
|
||||
chdir(dirname(__DIR__));
|
||||
|
||||
require __DIR__ . '/../vendor/autoload.php';
|
||||
|
||||
$container = new ServiceManager([
|
||||
'factories' => [
|
||||
Application::class => InstallApplicationFactory::class,
|
||||
Filesystem::class => InvokableFactory::class,
|
||||
],
|
||||
'services' => [
|
||||
'config' => [
|
||||
ConfigAbstractFactory::class => [
|
||||
DatabaseConfigCustomizer::class => [Filesystem::class]
|
||||
],
|
||||
],
|
||||
],
|
||||
]);
|
||||
$container->build(Application::class)->run();
|
||||
[$install] = require __DIR__ . '/../vendor/shlinkio/shlink-installer/bin/run.php';
|
||||
$install();
|
||||
|
||||
18
bin/test/run-api-tests.sh
Executable file
18
bin/test/run-api-tests.sh
Executable file
@@ -0,0 +1,18 @@
|
||||
#!/usr/bin/env sh
|
||||
export APP_ENV=test
|
||||
export DB_DRIVER=mysql
|
||||
|
||||
# Try to stop server just in case it hanged in last execution
|
||||
vendor/bin/mezzio-swoole stop
|
||||
|
||||
echo 'Starting server...'
|
||||
vendor/bin/mezzio-swoole start -d
|
||||
sleep 2
|
||||
|
||||
vendor/bin/phpunit --order-by=random -c phpunit-api.xml --testdox --colors=always $*
|
||||
testsExitCode=$?
|
||||
|
||||
vendor/bin/mezzio-swoole stop
|
||||
|
||||
# Exit this script with the same code as the tests. If tests failed, this script has to fail
|
||||
exit $testsExitCode
|
||||
31
bin/update
31
bin/update
@@ -1,29 +1,12 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
use Shlinkio\Shlink\CLI\Factory\InstallApplicationFactory;
|
||||
use Shlinkio\Shlink\CLI\Install\Plugin\DatabaseConfigCustomizer;
|
||||
use Symfony\Component\Console\Application;
|
||||
use Symfony\Component\Filesystem\Filesystem;
|
||||
use Zend\ServiceManager\AbstractFactory\ConfigAbstractFactory;
|
||||
use Zend\ServiceManager\Factory\InvokableFactory;
|
||||
use Zend\ServiceManager\ServiceManager;
|
||||
namespace Shlinkio\Shlink;
|
||||
|
||||
use function chdir;
|
||||
use function dirname;
|
||||
|
||||
chdir(dirname(__DIR__));
|
||||
|
||||
require __DIR__ . '/../vendor/autoload.php';
|
||||
|
||||
$container = new ServiceManager([
|
||||
'factories' => [
|
||||
Application::class => InstallApplicationFactory::class,
|
||||
Filesystem::class => InvokableFactory::class,
|
||||
],
|
||||
'services' => [
|
||||
'config' => [
|
||||
ConfigAbstractFactory::class => [
|
||||
DatabaseConfigCustomizer::class => [Filesystem::class]
|
||||
],
|
||||
],
|
||||
],
|
||||
]);
|
||||
$container->build(Application::class, ['isUpdate' => true])->run();
|
||||
[, $update] = require __DIR__ . '/../vendor/shlinkio/shlink-installer/bin/run.php';
|
||||
$update();
|
||||
|
||||
Binary file not shown.
46
build.sh
46
build.sh
@@ -1,55 +1,45 @@
|
||||
#!/usr/bin/env bash
|
||||
set -e
|
||||
|
||||
if [ "$#" -ne 1 ]; then
|
||||
if [[ "$#" -ne 1 ]]; then
|
||||
echo "Usage:" >&2
|
||||
echo " $0 {version}" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
version=$1
|
||||
builtcontent=$(readlink -f "../shlink_${version}_dist")
|
||||
builtcontent="./build/shlink_${version}_dist"
|
||||
projectdir=$(pwd)
|
||||
[[ -f ./composer.phar ]] && composerBin='./composer.phar' || composerBin='composer'
|
||||
|
||||
# Copy project content to temp dir
|
||||
echo 'Copying project files...'
|
||||
rm -rf "${builtcontent}"
|
||||
mkdir "${builtcontent}"
|
||||
sudo chmod -R 777 "${projectdir}"/data/infra/{database,nginx}
|
||||
cp -R "${projectdir}"/* "${builtcontent}"
|
||||
mkdir -p "${builtcontent}"
|
||||
rsync -av * "${builtcontent}" \
|
||||
--exclude=*docker* \
|
||||
--exclude=Dockerfile \
|
||||
--exclude-from=./.dockerignore
|
||||
cd "${builtcontent}"
|
||||
|
||||
# Install dependencies
|
||||
rm -rf vendor
|
||||
rm -f composer.lock
|
||||
composer self-update
|
||||
composer install --no-dev --optimize-autoloader --no-progress --no-interaction
|
||||
echo "Installing dependencies with $composerBin..."
|
||||
${composerBin} self-update
|
||||
${composerBin} install --no-dev --optimize-autoloader --no-progress --no-interaction
|
||||
|
||||
# Delete development files
|
||||
echo 'Deleting dev files...'
|
||||
rm build.sh
|
||||
rm CHANGELOG.md
|
||||
rm composer.*
|
||||
rm LICENSE
|
||||
rm indocker
|
||||
rm docker-compose.yml
|
||||
rm docker-compose.override.yml
|
||||
rm docker-compose.override.yml.dist
|
||||
rm func_tests_bootstrap.php
|
||||
rm php*
|
||||
rm README.md
|
||||
rm infection.json
|
||||
rm -rf build
|
||||
rm -ff data/database.sqlite
|
||||
rm -rf data/infra
|
||||
rm -rf data/{cache,log,proxies}/{*,.gitignore}
|
||||
rm -rf config/params/{*,.gitignore}
|
||||
rm -rf config/autoload/{{,*.}local.php{,.dist},.gitignore}
|
||||
|
||||
# Update shlink version in config
|
||||
sed -i "s/%SHLINK_VERSION%/${version}/g" config/autoload/app_options.global.php
|
||||
|
||||
# Compressing file
|
||||
rm -f "${projectdir}"/build/shlink_${version}_dist.zip
|
||||
zip -ry "${projectdir}"/build/shlink_${version}_dist.zip "../shlink_${version}_dist"
|
||||
echo 'Compressing files...'
|
||||
cd "${projectdir}"/build
|
||||
rm -f ./shlink_${version}_dist.zip
|
||||
zip -ry ./shlink_${version}_dist.zip ./shlink_${version}_dist
|
||||
cd "${projectdir}"
|
||||
rm -rf "${builtcontent}"
|
||||
|
||||
echo 'Done!'
|
||||
|
||||
186
composer.json
186
composer.json
@@ -12,102 +12,154 @@
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": "^7.1",
|
||||
"acelaya/ze-content-based-error-handler": "^2.2",
|
||||
"php": "^7.4",
|
||||
"ext-json": "*",
|
||||
"ext-pdo": "*",
|
||||
"akrabat/ip-address-middleware": "^1.0",
|
||||
"cakephp/chronos": "^1.2",
|
||||
"cocur/slugify": "^3.0",
|
||||
"doctrine/cache": "^1.6",
|
||||
"doctrine/migrations": "^1.4",
|
||||
"doctrine/orm": "^2.5",
|
||||
"endroid/qr-code": "^1.7",
|
||||
"doctrine/cache": "^1.9",
|
||||
"doctrine/dbal": "^2.10",
|
||||
"doctrine/migrations": "^2.2",
|
||||
"doctrine/orm": "^2.7",
|
||||
"endroid/qr-code": "^3.6",
|
||||
"firebase/php-jwt": "^4.0",
|
||||
"guzzlehttp/guzzle": "^6.2",
|
||||
"mikehaertl/phpwkhtmltopdf": "^2.2",
|
||||
"monolog/monolog": "^1.21",
|
||||
"roave/security-advisories": "dev-master",
|
||||
"symfony/console": "^4.0",
|
||||
"symfony/filesystem": "^4.0",
|
||||
"symfony/process": "^4.0",
|
||||
"theorchard/monolog-cascade": "^0.4",
|
||||
"zendframework/zend-config": "^3.0",
|
||||
"zendframework/zend-config-aggregator": "^1.0",
|
||||
"zendframework/zend-diactoros": "^1.7",
|
||||
"zendframework/zend-expressive": "^3.0",
|
||||
"zendframework/zend-expressive-fastroute": "^3.0",
|
||||
"zendframework/zend-expressive-helpers": "^5.0",
|
||||
"zendframework/zend-expressive-platesrenderer": "^2.0",
|
||||
"zendframework/zend-i18n": "^2.7",
|
||||
"zendframework/zend-inputfilter": "^2.8",
|
||||
"zendframework/zend-paginator": "^2.6",
|
||||
"zendframework/zend-servicemanager": "^3.2",
|
||||
"zendframework/zend-stdlib": "^3.0"
|
||||
"geoip2/geoip2": "^2.9",
|
||||
"guzzlehttp/guzzle": "^6.5.1",
|
||||
"laminas/laminas-config": "^3.3",
|
||||
"laminas/laminas-config-aggregator": "^1.1",
|
||||
"laminas/laminas-dependency-plugin": "^1.0",
|
||||
"laminas/laminas-diactoros": "^2.1.3",
|
||||
"laminas/laminas-inputfilter": "^2.10",
|
||||
"laminas/laminas-paginator": "^2.8",
|
||||
"laminas/laminas-servicemanager": "^3.4",
|
||||
"laminas/laminas-stdlib": "^3.2",
|
||||
"lstrojny/functional-php": "^1.9",
|
||||
"mezzio/mezzio": "^3.2",
|
||||
"mezzio/mezzio-fastroute": "^3.0",
|
||||
"mezzio/mezzio-helpers": "^5.3",
|
||||
"mezzio/mezzio-platesrenderer": "^2.1",
|
||||
"mezzio/mezzio-problem-details": "^1.1",
|
||||
"mezzio/mezzio-swoole": "^2.4",
|
||||
"monolog/monolog": "^2.0",
|
||||
"nikolaposa/monolog-factory": "^3.0",
|
||||
"ocramius/proxy-manager": "^2.6.0",
|
||||
"phly/phly-event-dispatcher": "^1.0",
|
||||
"predis/predis": "^1.1",
|
||||
"pugx/shortid-php": "^0.5",
|
||||
"shlinkio/shlink-common": "^2.5",
|
||||
"shlinkio/shlink-event-dispatcher": "^1.3",
|
||||
"shlinkio/shlink-installer": "^4.0",
|
||||
"shlinkio/shlink-ip-geolocation": "^1.3",
|
||||
"symfony/console": "^5.0",
|
||||
"symfony/filesystem": "^5.0",
|
||||
"symfony/lock": "^5.0",
|
||||
"symfony/process": "^5.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"filp/whoops": "^2.0",
|
||||
"infection/infection": "^0.9.0",
|
||||
"phpstan/phpstan": "^0.10.0",
|
||||
"phpunit/phpcov": "^5.0",
|
||||
"phpunit/phpunit": "^7.0",
|
||||
"slevomat/coding-standard": "^4.0",
|
||||
"squizlabs/php_codesniffer": "^3.2.3",
|
||||
"symfony/dotenv": "^4.0",
|
||||
"symfony/var-dumper": "^4.0",
|
||||
"zendframework/zend-component-installer": "^2.1",
|
||||
"zendframework/zend-expressive-tooling": "^1.0"
|
||||
"devster/ubench": "^2.0",
|
||||
"eaglewu/swoole-ide-helper": "dev-master",
|
||||
"infection/infection": "^0.15.0",
|
||||
"phpstan/phpstan": "^0.12.3",
|
||||
"phpunit/phpunit": "^8.3",
|
||||
"roave/security-advisories": "dev-master",
|
||||
"shlinkio/php-coding-standard": "~2.1.0",
|
||||
"shlinkio/shlink-test-utils": "^1.3",
|
||||
"symfony/var-dumper": "^5.0"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Shlinkio\\Shlink\\CLI\\": "module/CLI/src",
|
||||
"Shlinkio\\Shlink\\Rest\\": "module/Rest/src",
|
||||
"Shlinkio\\Shlink\\Core\\": "module/Core/src",
|
||||
"Shlinkio\\Shlink\\Common\\": "module/Common/src"
|
||||
"Shlinkio\\Shlink\\Core\\": "module/Core/src"
|
||||
},
|
||||
"files": [
|
||||
"module/Common/functions/functions.php"
|
||||
"module/Core/functions/functions.php"
|
||||
]
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"ShlinkioTest\\Shlink\\CLI\\": "module/CLI/test",
|
||||
"ShlinkioTest\\Shlink\\Rest\\": "module/Rest/test",
|
||||
"ShlinkioApiTest\\Shlink\\Rest\\": "module/Rest/test-api",
|
||||
"ShlinkioTest\\Shlink\\Core\\": [
|
||||
"module/Core/test",
|
||||
"module/Core/test-func"
|
||||
],
|
||||
"ShlinkioTest\\Shlink\\Common\\": [
|
||||
"module/Common/test",
|
||||
"module/Common/test-func"
|
||||
"module/Core/test-db"
|
||||
]
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"check": [
|
||||
"ci": [
|
||||
"@cs",
|
||||
"@stan",
|
||||
"@test",
|
||||
"@func-test",
|
||||
"@infect"
|
||||
"@test:ci",
|
||||
"@infect:ci"
|
||||
],
|
||||
"cs": "phpcs",
|
||||
"cs-fix": "phpcbf",
|
||||
"serve": "php -S 0.0.0.0:8000 -t public/",
|
||||
"test": "phpunit --coverage-php build/coverage-unit.cov",
|
||||
"pretty-test": "phpunit --coverage-html build/coverage",
|
||||
"func-test": "phpunit -c phpunit-func.xml --coverage-php build/coverage-func.cov",
|
||||
"complete-pretty-test": [
|
||||
"@test",
|
||||
"@func-test",
|
||||
"phpcov merge build --html build/html"
|
||||
"cs:fix": "phpcbf",
|
||||
"stan": "phpstan analyse module/*/src/ module/*/config config docker/config --level=5 -c phpstan.neon",
|
||||
"test": [
|
||||
"@test:unit",
|
||||
"@test:db",
|
||||
"@test:api"
|
||||
],
|
||||
"stan": "phpstan analyse module/*/src/ --level=6 -c phpstan.neon",
|
||||
"infect": "infection --threads=4 --min-msi=55 --only-covered --log-verbosity=2",
|
||||
"infect-show": "infection --threads=4 --min-msi=55 --only-covered --log-verbosity=2 --show-mutations",
|
||||
"expressive": "expressive"
|
||||
"test:ci": [
|
||||
"@test:unit:ci",
|
||||
"@test:db:ci",
|
||||
"@test:api"
|
||||
],
|
||||
"test:unit": "phpdbg -qrr vendor/bin/phpunit --order-by=random --colors=always --coverage-php build/coverage-unit.cov --testdox",
|
||||
"test:unit:ci": "@test:unit --coverage-clover=build/clover.xml --coverage-xml=build/coverage-xml --log-junit=build/junit.xml",
|
||||
"test:db": [
|
||||
"@test:db:sqlite",
|
||||
"@test:db:mysql",
|
||||
"@test:db:maria",
|
||||
"@test:db:postgres"
|
||||
],
|
||||
"test:db:ci": [
|
||||
"@test:db:sqlite",
|
||||
"@test:db:mysql",
|
||||
"@test:db:postgres"
|
||||
],
|
||||
"test:db:sqlite": "APP_ENV=test phpdbg -qrr vendor/bin/phpunit --order-by=random --colors=always --coverage-php build/coverage-db.cov --testdox -c phpunit-db.xml",
|
||||
"test:db:mysql": "DB_DRIVER=mysql composer test:db:sqlite",
|
||||
"test:db:maria": "DB_DRIVER=maria composer test:db:sqlite",
|
||||
"test:db:postgres": "DB_DRIVER=postgres composer test:db:sqlite",
|
||||
"test:api": "bin/test/run-api-tests.sh",
|
||||
"test:unit:pretty": "phpdbg -qrr vendor/bin/phpunit --order-by=random --colors=always --coverage-html build/coverage",
|
||||
"infect": "infection --threads=4 --min-msi=80 --log-verbosity=default --only-covered",
|
||||
"infect:ci": "@infect --coverage=build",
|
||||
"infect:show": "@infect --show-mutations",
|
||||
"infect:test": [
|
||||
"@test:unit:ci",
|
||||
"@infect:ci"
|
||||
],
|
||||
"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\"</>",
|
||||
"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:db": "<fg=blue;options=bold>Runs database test suites on a SQLite, MySQL, MariaDB and PostgreSQL</>",
|
||||
"test:db:ci": "<fg=blue;options=bold>Runs database test suites on a SQLite, MySQL and PostgreSQL</>",
|
||||
"test:db:sqlite": "<fg=blue;options=bold>Runs database test suites on a SQLite database</>",
|
||||
"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: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</>",
|
||||
"infect": "<fg=blue;options=bold>Checks unit tests quality applying mutation testing</>",
|
||||
"infect:ci": "<fg=blue;options=bold>Checks unit tests quality applying mutation testing with existing reports and logs</>",
|
||||
"infect:show": "<fg=blue;options=bold>Checks unit tests quality applying mutation testing and shows applied mutators</>",
|
||||
"infect:test": "<fg=blue;options=bold>Checks unit tests quality applying mutation testing</>",
|
||||
"clean:dev": "<fg=blue;options=bold>Deletes artifacts which are gitignored and could affect dev env</>"
|
||||
},
|
||||
"config": {
|
||||
"process-timeout": 0,
|
||||
"sort-packages": true,
|
||||
"platform": {
|
||||
"php": "7.1.8"
|
||||
}
|
||||
"sort-packages": true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
use function Shlinkio\Shlink\Common\env;
|
||||
declare(strict_types=1);
|
||||
|
||||
return [
|
||||
|
||||
'app_options' => [
|
||||
'name' => 'Shlink',
|
||||
'version' => '%SHLINK_VERSION%',
|
||||
'secret_key' => env('SECRET_KEY'),
|
||||
'disable_track_param' => null,
|
||||
],
|
||||
|
||||
];
|
||||
|
||||
20
config/autoload/client-detection.global.php
Normal file
20
config/autoload/client-detection.global.php
Normal file
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
return [
|
||||
|
||||
'ip_address_resolution' => [
|
||||
'headers_to_inspect' => [
|
||||
'CF-Connecting-IP',
|
||||
'True-Client-IP',
|
||||
'X-Real-IP',
|
||||
'Forwarded',
|
||||
'X-Forwarded-For',
|
||||
'X-Forwarded',
|
||||
'X-Cluster-Client-Ip',
|
||||
'Client-Ip',
|
||||
],
|
||||
],
|
||||
|
||||
];
|
||||
12
config/autoload/common.global.php
Normal file
12
config/autoload/common.global.php
Normal file
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Laminas\ConfigAggregator\ConfigAggregator;
|
||||
|
||||
return [
|
||||
|
||||
'debug' => false,
|
||||
ConfigAggregator::ENABLE_CACHE => true,
|
||||
|
||||
];
|
||||
11
config/autoload/common.local.php.dist
Normal file
11
config/autoload/common.local.php.dist
Normal file
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
use Laminas\ConfigAggregator\ConfigAggregator;
|
||||
|
||||
return [
|
||||
|
||||
'debug' => true,
|
||||
ConfigAggregator::ENABLE_CACHE => false,
|
||||
|
||||
];
|
||||
14
config/autoload/delete_short_urls.global.php
Normal file
14
config/autoload/delete_short_urls.global.php
Normal file
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Shlinkio\Shlink;
|
||||
|
||||
return [
|
||||
|
||||
'delete_short_urls' => [
|
||||
'visits_threshold' => 15,
|
||||
'check_visits_threshold' => true,
|
||||
],
|
||||
|
||||
];
|
||||
@@ -1,28 +1,23 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Shlinkio\Shlink\Common\Factory\EmptyResponseImplicitOptionsMiddlewareFactory;
|
||||
use Zend\Expressive;
|
||||
use Zend\Expressive\Container;
|
||||
use Zend\Expressive\Helper;
|
||||
use Zend\Expressive\Router\Middleware\ImplicitOptionsMiddleware;
|
||||
use Zend\ServiceManager\Factory\InvokableFactory;
|
||||
use Mezzio\Container;
|
||||
|
||||
return [
|
||||
|
||||
'dependencies' => [
|
||||
'factories' => [
|
||||
ImplicitOptionsMiddleware::class => EmptyResponseImplicitOptionsMiddlewareFactory::class,
|
||||
|
||||
Helper\UrlHelper::class => Helper\UrlHelperFactory::class,
|
||||
Helper\ServerUrlHelper::class => InvokableFactory::class,
|
||||
],
|
||||
|
||||
'delegators' => [
|
||||
Expressive\Application::class => [
|
||||
Mezzio\Application::class => [
|
||||
Container\ApplicationConfigInjectionDelegator::class,
|
||||
],
|
||||
],
|
||||
|
||||
'lazy_services' => [
|
||||
'proxies_target_dir' => 'data/proxies',
|
||||
'proxies_namespace' => 'ShlinkProxy',
|
||||
'write_proxy_files' => true,
|
||||
],
|
||||
],
|
||||
|
||||
];
|
||||
|
||||
24
config/autoload/dependencies.local.php.dist
Normal file
24
config/autoload/dependencies.local.php.dist
Normal file
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Psr\Container\ContainerInterface;
|
||||
use Psr\Log;
|
||||
|
||||
return [
|
||||
|
||||
'dependencies' => [
|
||||
'lazy_services' => [
|
||||
'write_proxy_files' => false,
|
||||
],
|
||||
|
||||
'initializers' => [
|
||||
function (ContainerInterface $container, $instance): void {
|
||||
if ($instance instanceof Log\LoggerAwareInterface) {
|
||||
$instance->setLogger($container->get(Log\LoggerInterface::class));
|
||||
}
|
||||
},
|
||||
],
|
||||
],
|
||||
|
||||
];
|
||||
@@ -1,7 +1,8 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Shlinkio\Shlink\Common;
|
||||
namespace Shlinkio\Shlink\Common;
|
||||
|
||||
return [
|
||||
|
||||
@@ -10,9 +11,9 @@ return [
|
||||
'proxies_dir' => 'data/proxies',
|
||||
],
|
||||
'connection' => [
|
||||
'user' => Common\env('DB_USER'),
|
||||
'password' => Common\env('DB_PASSWORD'),
|
||||
'dbname' => Common\env('DB_NAME', 'shlink'),
|
||||
'user' => '',
|
||||
'password' => '',
|
||||
'dbname' => 'shlink',
|
||||
'charset' => 'utf8',
|
||||
],
|
||||
],
|
||||
|
||||
@@ -1,8 +1,13 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
return [
|
||||
|
||||
'entity_manager' => [
|
||||
'connection' => [
|
||||
'user' => 'root',
|
||||
'password' => 'root',
|
||||
'driver' => 'pdo_mysql',
|
||||
'host' => 'shlink_db',
|
||||
'driverOptions' => [
|
||||
|
||||
33
config/autoload/error-handler.global.php
Normal file
33
config/autoload/error-handler.global.php
Normal file
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Laminas\Stratigility\Middleware\ErrorHandler;
|
||||
use Mezzio\ProblemDetails\ProblemDetailsMiddleware;
|
||||
use Shlinkio\Shlink\Common\Logger;
|
||||
|
||||
return [
|
||||
|
||||
'problem-details' => [
|
||||
'default_types_map' => [
|
||||
404 => 'NOT_FOUND',
|
||||
500 => 'INTERNAL_SERVER_ERROR',
|
||||
],
|
||||
],
|
||||
|
||||
'error_handler' => [
|
||||
'listeners' => [Logger\ErrorLogger::class],
|
||||
],
|
||||
|
||||
'dependencies' => [
|
||||
'delegators' => [
|
||||
ErrorHandler::class => [
|
||||
Logger\ErrorHandlerListenerAttachingDelegator::class,
|
||||
],
|
||||
ProblemDetailsMiddleware::class => [
|
||||
Logger\ErrorHandlerListenerAttachingDelegator::class,
|
||||
],
|
||||
],
|
||||
],
|
||||
|
||||
];
|
||||
@@ -1,27 +0,0 @@
|
||||
<?php
|
||||
use Zend\Expressive\Container\WhoopsErrorResponseGeneratorFactory;
|
||||
|
||||
return [
|
||||
'dependencies' => [
|
||||
'invokables' => [
|
||||
'Zend\Expressive\Whoops' => Whoops\Run::class,
|
||||
'Zend\Expressive\WhoopsPageHandler' => Whoops\Handler\PrettyPageHandler::class,
|
||||
],
|
||||
],
|
||||
|
||||
'whoops' => [
|
||||
'json_exceptions' => [
|
||||
'display' => true,
|
||||
'show_trace' => true,
|
||||
'ajax_only' => true,
|
||||
],
|
||||
],
|
||||
|
||||
'error_handler' => [
|
||||
'plugins' => [
|
||||
'factories' => [
|
||||
'text/html' => WhoopsErrorResponseGeneratorFactory::class,
|
||||
],
|
||||
],
|
||||
],
|
||||
];
|
||||
15
config/autoload/geolite2.global.php
Normal file
15
config/autoload/geolite2.global.php
Normal file
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
return [
|
||||
|
||||
'geolite2' => [
|
||||
'db_location' => __DIR__ . '/../../data/GeoLite2-City.mmdb',
|
||||
'temp_dir' => sys_get_temp_dir(),
|
||||
'download_from' =>
|
||||
'https://download.maxmind.com/app/geoip_download'
|
||||
. '?edition_id=GeoLite2-City&license_key=G4Lm0C60yJsnkdPi&suffix=tar.gz',
|
||||
],
|
||||
|
||||
];
|
||||
45
config/autoload/installer.global.php
Normal file
45
config/autoload/installer.global.php
Normal file
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Shlinkio\Shlink\Installer\Config\Option;
|
||||
|
||||
return [
|
||||
|
||||
'installer' => [
|
||||
'enabled_options' => [
|
||||
Option\DatabaseDriverConfigOption::class,
|
||||
Option\DatabaseNameConfigOption::class,
|
||||
Option\DatabaseHostConfigOption::class,
|
||||
Option\DatabasePortConfigOption::class,
|
||||
Option\DatabaseUserConfigOption::class,
|
||||
Option\DatabasePasswordConfigOption::class,
|
||||
Option\DatabaseSqlitePathConfigOption::class,
|
||||
Option\DatabaseMySqlOptionsConfigOption::class,
|
||||
Option\ShortDomainHostConfigOption::class,
|
||||
Option\ShortDomainSchemaConfigOption::class,
|
||||
Option\ValidateUrlConfigOption::class,
|
||||
Option\VisitsWebhooksConfigOption::class,
|
||||
Option\BaseUrlRedirectConfigOption::class,
|
||||
Option\InvalidShortUrlRedirectConfigOption::class,
|
||||
Option\Regular404RedirectConfigOption::class,
|
||||
Option\DisableTrackParamConfigOption::class,
|
||||
Option\CheckVisitsThresholdConfigOption::class,
|
||||
Option\VisitsThresholdConfigOption::class,
|
||||
Option\BasePathConfigOption::class,
|
||||
Option\TaskWorkerNumConfigOption::class,
|
||||
Option\WebWorkerNumConfigOption::class,
|
||||
Option\RedisServersConfigOption::class,
|
||||
],
|
||||
|
||||
'installation_commands' => [
|
||||
'db_create_schema' => [
|
||||
'command' => 'bin/cli db:create',
|
||||
],
|
||||
'db_migrate' => [
|
||||
'command' => 'bin/cli db:migrate',
|
||||
],
|
||||
],
|
||||
],
|
||||
|
||||
];
|
||||
@@ -1,8 +0,0 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
|
||||
'debug' => true,
|
||||
'config_cache_enabled' => false,
|
||||
|
||||
];
|
||||
50
config/autoload/locks.global.php
Normal file
50
config/autoload/locks.global.php
Normal file
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
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 Symfony\Component\Lock;
|
||||
|
||||
$localLockFactory = 'Shlinkio\Shlink\LocalLockFactory';
|
||||
|
||||
return [
|
||||
|
||||
'locks' => [
|
||||
'locks_dir' => __DIR__ . '/../../data/locks',
|
||||
],
|
||||
|
||||
'dependencies' => [
|
||||
'factories' => [
|
||||
Lock\Store\FlockStore::class => ConfigAbstractFactory::class,
|
||||
Lock\Store\RedisStore::class => ConfigAbstractFactory::class,
|
||||
Lock\LockFactory::class => ConfigAbstractFactory::class,
|
||||
$localLockFactory => ConfigAbstractFactory::class,
|
||||
],
|
||||
'aliases' => [
|
||||
// With this config, a user could alias 'lock_store' => 'redis_lock_store' to override the default
|
||||
'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,
|
||||
],
|
||||
],
|
||||
],
|
||||
|
||||
ConfigAbstractFactory::class => [
|
||||
Lock\Store\FlockStore::class => ['config.locks.locks_dir'],
|
||||
Lock\Store\RedisStore::class => [RedisFactory::SERVICE_NAME],
|
||||
Lock\LockFactory::class => ['lock_store'],
|
||||
$localLockFactory => ['local_lock_store'],
|
||||
],
|
||||
|
||||
];
|
||||
@@ -1,32 +1,84 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Monolog\Handler\RotatingFileHandler;
|
||||
namespace Shlinkio\Shlink;
|
||||
|
||||
use Monolog\Formatter;
|
||||
use Monolog\Handler;
|
||||
use Monolog\Logger;
|
||||
use Monolog\Processor;
|
||||
use MonologFactory\DiContainerLoggerFactory;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
use const PHP_EOL;
|
||||
|
||||
$processors = [
|
||||
'exception_with_new_line' => [
|
||||
'name' => Common\Logger\Processor\ExceptionWithNewLineProcessor::class,
|
||||
],
|
||||
'psr3' => [
|
||||
'name' => Processor\PsrLogMessageProcessor::class,
|
||||
],
|
||||
];
|
||||
$formatter = [
|
||||
'name' => Formatter\LineFormatter::class,
|
||||
'params' => [
|
||||
'format' => '[%datetime%] %channel%.%level_name% - %message%' . PHP_EOL,
|
||||
'allow_inline_line_breaks' => true,
|
||||
],
|
||||
];
|
||||
|
||||
return [
|
||||
|
||||
'logger' => [
|
||||
'formatters' => [
|
||||
'dashed' => [
|
||||
'format' => '[%datetime%] %channel%.%level_name% - %message% %context%' . PHP_EOL,
|
||||
'include_stacktraces' => true,
|
||||
'Shlink' => [
|
||||
'name' => 'Shlink',
|
||||
'handlers' => [
|
||||
'shlink_handler' => [
|
||||
'name' => Handler\RotatingFileHandler::class,
|
||||
'params' => [
|
||||
'level' => Logger::INFO,
|
||||
'filename' => 'data/log/shlink_log.log',
|
||||
'max_files' => 30,
|
||||
],
|
||||
'formatter' => $formatter,
|
||||
],
|
||||
],
|
||||
'processors' => $processors,
|
||||
],
|
||||
|
||||
'handlers' => [
|
||||
'rotating_file_handler' => [
|
||||
'class' => RotatingFileHandler::class,
|
||||
'level' => Logger::INFO,
|
||||
'filename' => 'data/log/shlink_log.log',
|
||||
'max_files' => 30,
|
||||
'formatter' => 'dashed',
|
||||
'Access' => [
|
||||
'name' => 'Access',
|
||||
'handlers' => [
|
||||
'access_handler' => [
|
||||
'name' => Handler\StreamHandler::class,
|
||||
'params' => [
|
||||
'level' => Logger::INFO,
|
||||
'stream' => 'php://stdout',
|
||||
],
|
||||
'formatter' => $formatter,
|
||||
],
|
||||
],
|
||||
'processors' => $processors,
|
||||
],
|
||||
],
|
||||
|
||||
'loggers' => [
|
||||
'Shlink' => [
|
||||
'handlers' => ['rotating_file_handler'],
|
||||
'dependencies' => [
|
||||
'factories' => [
|
||||
'Logger_Shlink' => [DiContainerLoggerFactory::class, 'Shlink'],
|
||||
'Logger_Access' => [DiContainerLoggerFactory::class, 'Access'],
|
||||
],
|
||||
'aliases' => [
|
||||
'logger' => 'Logger_Shlink',
|
||||
Logger::class => 'Logger_Shlink',
|
||||
LoggerInterface::class => 'Logger_Shlink',
|
||||
],
|
||||
],
|
||||
|
||||
'mezzio-swoole' => [
|
||||
'swoole-http-server' => [
|
||||
'logger' => [
|
||||
'logger-name' => 'Logger_Access',
|
||||
],
|
||||
],
|
||||
],
|
||||
|
||||
@@ -1,12 +1,33 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Monolog\Handler\StreamHandler;
|
||||
use Monolog\Logger;
|
||||
|
||||
$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,
|
||||
],
|
||||
];
|
||||
|
||||
return [
|
||||
|
||||
'logger' => [
|
||||
'handlers' => [
|
||||
'rotating_file_handler' => [
|
||||
'level' => Logger::DEBUG,
|
||||
'Shlink' => [
|
||||
'handlers' => [
|
||||
'shlink_handler' => $handler,
|
||||
],
|
||||
],
|
||||
],
|
||||
|
||||
@@ -1,58 +1,68 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Shlinkio\Shlink\Common\Middleware\LocaleMiddleware;
|
||||
use Shlinkio\Shlink\Core\Response\NotFoundHandler;
|
||||
use Shlinkio\Shlink\Rest\Middleware\BodyParserMiddleware;
|
||||
use Shlinkio\Shlink\Rest\Middleware\CheckAuthenticationMiddleware;
|
||||
use Shlinkio\Shlink\Rest\Middleware\CrossDomainMiddleware;
|
||||
use Shlinkio\Shlink\Rest\Middleware\PathVersionMiddleware;
|
||||
use Zend\Expressive;
|
||||
use Zend\Stratigility\Middleware\ErrorHandler;
|
||||
namespace Shlinkio\Shlink;
|
||||
|
||||
use Laminas\Stratigility\Middleware\ErrorHandler;
|
||||
use Mezzio;
|
||||
use Mezzio\ProblemDetails;
|
||||
|
||||
return [
|
||||
|
||||
'middleware_pipeline' => [
|
||||
'pre-routing' => [
|
||||
'error-handler' => [
|
||||
'middleware' => [
|
||||
Mezzio\Helper\ContentLengthMiddleware::class,
|
||||
ErrorHandler::class,
|
||||
Expressive\Helper\ContentLengthMiddleware::class,
|
||||
LocaleMiddleware::class,
|
||||
],
|
||||
'priority' => 11,
|
||||
],
|
||||
'pre-routing-rest' => [
|
||||
'error-handler-rest' => [
|
||||
'path' => '/rest',
|
||||
'middleware' => [
|
||||
PathVersionMiddleware::class,
|
||||
Rest\Middleware\CrossDomainMiddleware::class,
|
||||
ProblemDetails\ProblemDetailsMiddleware::class,
|
||||
],
|
||||
],
|
||||
|
||||
'pre-routing' => [
|
||||
'middleware' => [
|
||||
Common\Middleware\CloseDbConnectionMiddleware::class,
|
||||
],
|
||||
'priority' => 11,
|
||||
],
|
||||
|
||||
'routing' => [
|
||||
'middleware' => [
|
||||
Expressive\Router\Middleware\RouteMiddleware::class,
|
||||
Mezzio\Router\Middleware\RouteMiddleware::class,
|
||||
],
|
||||
'priority' => 10,
|
||||
],
|
||||
|
||||
'rest' => [
|
||||
'path' => '/rest',
|
||||
'middleware' => [
|
||||
CrossDomainMiddleware::class,
|
||||
Expressive\Router\Middleware\ImplicitOptionsMiddleware::class,
|
||||
BodyParserMiddleware::class,
|
||||
CheckAuthenticationMiddleware::class,
|
||||
Mezzio\Router\Middleware\ImplicitOptionsMiddleware::class,
|
||||
Rest\Middleware\BodyParserMiddleware::class,
|
||||
Rest\Middleware\AuthenticationMiddleware::class,
|
||||
],
|
||||
'priority' => 5,
|
||||
],
|
||||
|
||||
'post-routing' => [
|
||||
'dispatch' => [
|
||||
'middleware' => [
|
||||
Expressive\Router\Middleware\DispatchMiddleware::class,
|
||||
NotFoundHandler::class,
|
||||
Mezzio\Router\Middleware\DispatchMiddleware::class,
|
||||
],
|
||||
],
|
||||
|
||||
'not-found-rest' => [
|
||||
'path' => '/rest',
|
||||
'middleware' => [
|
||||
ProblemDetails\ProblemDetailsNotFoundHandler::class,
|
||||
],
|
||||
],
|
||||
'not-found' => [
|
||||
'middleware' => [
|
||||
Core\ErrorHandler\NotFoundRedirectHandler::class,
|
||||
Core\ErrorHandler\NotFoundTemplateHandler::class,
|
||||
],
|
||||
'priority' => 1,
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
return [
|
||||
|
||||
'phpwkhtmltopdf' => [
|
||||
'images' => [
|
||||
'binary' => 'bin/wkhtmltoimage',
|
||||
'type' => 'jpg',
|
||||
],
|
||||
],
|
||||
|
||||
];
|
||||
@@ -1,10 +0,0 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
return [
|
||||
|
||||
'preview_generation' => [
|
||||
'files_location' => 'data/cache',
|
||||
],
|
||||
|
||||
];
|
||||
13
config/autoload/redirects.global.php
Normal file
13
config/autoload/redirects.global.php
Normal file
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
return [
|
||||
|
||||
'not_found_redirects' => [
|
||||
'invalid_short_url' => null, // Formerly url_shortener.not_found_short_url.redirect_to
|
||||
'regular_404' => null,
|
||||
'base_url' => null,
|
||||
],
|
||||
|
||||
];
|
||||
23
config/autoload/redis.local.php.local
Normal file
23
config/autoload/redis.local.php.local
Normal file
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
return [
|
||||
|
||||
'cache' => [
|
||||
'redis' => [
|
||||
'servers' => 'tcp://shlink_redis:6379',
|
||||
// 'servers' => [
|
||||
// 'tcp://shlink_redis:6379',
|
||||
// ],
|
||||
],
|
||||
],
|
||||
|
||||
'dependencies' => [
|
||||
'aliases' => [
|
||||
// With this config, a user could alias 'lock_store' => 'redis_lock_store' to override the default
|
||||
// 'lock_store' => 'redis_lock_store',
|
||||
],
|
||||
],
|
||||
|
||||
];
|
||||
@@ -1,11 +1,14 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Zend\Expressive\Router\FastRouteRouter;
|
||||
use Mezzio\Router\FastRouteRouter;
|
||||
|
||||
return [
|
||||
|
||||
'router' => [
|
||||
'base_path' => '',
|
||||
|
||||
'fastroute' => [
|
||||
FastRouteRouter::CONFIG_CACHE_ENABLED => true,
|
||||
FastRouteRouter::CONFIG_CACHE_FILE => 'data/cache/fastroute_cached_routes.php',
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?php
|
||||
use Zend\Expressive\Router\FastRouteRouter;
|
||||
use Mezzio\Router\FastRouteRouter;
|
||||
|
||||
return [
|
||||
|
||||
|
||||
21
config/autoload/swoole.global.php
Normal file
21
config/autoload/swoole.global.php
Normal file
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
return [
|
||||
|
||||
'mezzio-swoole' => [
|
||||
'enable_coroutine' => true,
|
||||
|
||||
'swoole-http-server' => [
|
||||
'host' => '0.0.0.0',
|
||||
'process-name' => 'shlink',
|
||||
|
||||
'options' => [
|
||||
'worker_num' => 16,
|
||||
'task_worker_num' => 16,
|
||||
],
|
||||
],
|
||||
],
|
||||
|
||||
];
|
||||
21
config/autoload/swoole.local.php.dist
Normal file
21
config/autoload/swoole.local.php.dist
Normal file
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
use Mezzio\Swoole\HotCodeReload\FileWatcher\InotifyFileWatcher;
|
||||
use Laminas\ServiceManager\Factory\InvokableFactory;
|
||||
|
||||
return [
|
||||
|
||||
'mezzio-swoole' => [
|
||||
'hot-code-reload' => [
|
||||
'enable' => true,
|
||||
],
|
||||
],
|
||||
|
||||
'dependencies' => [
|
||||
'factories' => [
|
||||
InotifyFileWatcher::class => InvokableFactory::class,
|
||||
],
|
||||
],
|
||||
|
||||
];
|
||||
@@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
return [
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
use Shlinkio\Shlink\Common;
|
||||
|
||||
return [
|
||||
|
||||
'translator' => [
|
||||
'locale' => Common\env('DEFAULT_LOCALE', 'en'),
|
||||
],
|
||||
|
||||
];
|
||||
@@ -1,18 +1,16 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
use Shlinkio\Shlink\Core\Service\UrlShortener;
|
||||
use function Shlinkio\Shlink\Common\env;
|
||||
declare(strict_types=1);
|
||||
|
||||
return [
|
||||
|
||||
'url_shortener' => [
|
||||
'domain' => [
|
||||
'schema' => env('SHORTENED_URL_SCHEMA', 'http'),
|
||||
'hostname' => env('SHORTENED_URL_HOSTNAME'),
|
||||
'schema' => 'https',
|
||||
'hostname' => '',
|
||||
],
|
||||
'shortcode_chars' => env('SHORTCODE_CHARS', UrlShortener::DEFAULT_CHARS),
|
||||
'validate_url' => true,
|
||||
'validate_url' => false,
|
||||
'visits_webhooks' => [],
|
||||
],
|
||||
|
||||
];
|
||||
|
||||
14
config/autoload/url-shortener.local.php.dist
Normal file
14
config/autoload/url-shortener.local.php.dist
Normal file
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
return [
|
||||
|
||||
'url_shortener' => [
|
||||
'domain' => [
|
||||
'schema' => 'http',
|
||||
'hostname' => 'localhost:8080',
|
||||
],
|
||||
],
|
||||
|
||||
];
|
||||
@@ -1,9 +0,0 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
return [
|
||||
|
||||
'debug' => false,
|
||||
'config_cache_enabled' => true,
|
||||
|
||||
];
|
||||
@@ -1,35 +1,16 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Doctrine\ORM\EntityManager;
|
||||
use Doctrine\ORM\Tools\Console\ConsoleRunner;
|
||||
use Interop\Container\ContainerInterface;
|
||||
use Zend\ServiceManager\ServiceManager;
|
||||
use Laminas\ServiceManager\ServiceManager;
|
||||
use Psr\Container\ContainerInterface;
|
||||
|
||||
$isTest = false;
|
||||
foreach ($_SERVER['argv'] as $i => $arg) {
|
||||
if ($arg === '--test') {
|
||||
unset($_SERVER['argv'][$i]);
|
||||
$isTest = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return (function () {
|
||||
/** @var ContainerInterface|ServiceManager $container */
|
||||
$container = include __DIR__ . '/container.php';
|
||||
$em = $container->get(EntityManager::class);
|
||||
|
||||
/** @var ContainerInterface|ServiceManager $container */
|
||||
$container = include __DIR__ . '/container.php';
|
||||
|
||||
// If in testing env, override DB connection to use an in-memory sqlite database
|
||||
if ($isTest) {
|
||||
$container->setAllowOverride(true);
|
||||
$config = $container->get('config');
|
||||
$config['entity_manager']['connection'] = [
|
||||
'driver' => 'pdo_sqlite',
|
||||
'path' => realpath(sys_get_temp_dir()) . '/shlink-tests.db',
|
||||
];
|
||||
$container->setService('config', $config);
|
||||
}
|
||||
|
||||
/** @var EntityManager $em */
|
||||
$em = $container->get(EntityManager::class);
|
||||
|
||||
return ConsoleRunner::createHelperSet($em);
|
||||
return ConsoleRunner::createHelperSet($em);
|
||||
})();
|
||||
|
||||
@@ -1,36 +1,36 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Acelaya\ExpressiveErrorHandler;
|
||||
use Shlinkio\Shlink\CLI;
|
||||
use Shlinkio\Shlink\Common;
|
||||
use Shlinkio\Shlink\Core;
|
||||
use Shlinkio\Shlink\Rest;
|
||||
use Zend\ConfigAggregator;
|
||||
use Zend\Expressive;
|
||||
namespace Shlinkio\Shlink;
|
||||
|
||||
/**
|
||||
* Configuration files are loaded in a specific order. First ``global.php``, then ``*.global.php``.
|
||||
* then ``local.php`` and finally ``*.local.php``. This way local settings overwrite global settings.
|
||||
*
|
||||
* The configuration can be cached. This can be done by setting ``config_cache_enabled`` to ``true``.
|
||||
*
|
||||
* Obviously, if you use closures in your config you can't cache it.
|
||||
*/
|
||||
use Laminas\ConfigAggregator;
|
||||
use Laminas\ZendFrameworkBridge;
|
||||
use Mezzio;
|
||||
use Mezzio\ProblemDetails;
|
||||
|
||||
use function Shlinkio\Shlink\Common\env;
|
||||
|
||||
return (new ConfigAggregator\ConfigAggregator([
|
||||
Expressive\ConfigProvider::class,
|
||||
Expressive\Router\ConfigProvider::class,
|
||||
Expressive\Router\FastRouteRouter\ConfigProvider::class,
|
||||
Expressive\Plates\ConfigProvider::class,
|
||||
Expressive\Helper\ConfigProvider::class,
|
||||
\class_exists(Expressive\Swoole\ConfigProvider::class)
|
||||
? Expressive\Swoole\ConfigProvider::class
|
||||
: new ConfigAggregator\ArrayProvider([]),
|
||||
ExpressiveErrorHandler\ConfigProvider::class,
|
||||
Mezzio\ConfigProvider::class,
|
||||
Mezzio\Router\ConfigProvider::class,
|
||||
Mezzio\Router\FastRouteRouter\ConfigProvider::class,
|
||||
Mezzio\Plates\ConfigProvider::class,
|
||||
Mezzio\Swoole\ConfigProvider::class,
|
||||
ProblemDetails\ConfigProvider::class,
|
||||
Common\ConfigProvider::class,
|
||||
IpGeolocation\ConfigProvider::class,
|
||||
Core\ConfigProvider::class,
|
||||
CLI\ConfigProvider::class,
|
||||
Rest\ConfigProvider::class,
|
||||
new ConfigAggregator\ZendConfigProvider('config/{autoload/{{,*.}global,{,*.}local},params/generated_config}.php'),
|
||||
], 'data/cache/app_config.php'))->getMergedConfig();
|
||||
EventDispatcher\ConfigProvider::class,
|
||||
new ConfigAggregator\PhpFileProvider('config/autoload/{{,*.}global,{,*.}local}.php'),
|
||||
env('APP_ENV') === 'test'
|
||||
? new ConfigAggregator\PhpFileProvider('config/test/*.global.php')
|
||||
: new ConfigAggregator\LaminasConfigProvider('config/params/{generated_config.php,*.config.{php,json}}'),
|
||||
], 'data/cache/app_config.php', [
|
||||
ZendFrameworkBridge\ConfigPostProcessor::class,
|
||||
Core\Config\SimplifiedConfigParser::class,
|
||||
Core\Config\BasePathPrefixer::class,
|
||||
Core\Config\DeprecatedConfigParser::class,
|
||||
]))->getMergedConfig();
|
||||
|
||||
@@ -1,23 +1,24 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Symfony\Component\Dotenv\Dotenv;
|
||||
use Zend\ServiceManager\ServiceManager;
|
||||
use Laminas\ServiceManager\ServiceManager;
|
||||
use Symfony\Component\Lock;
|
||||
|
||||
chdir(dirname(__DIR__));
|
||||
|
||||
require 'vendor/autoload.php';
|
||||
|
||||
// If the Dotenv class exists, load env vars and enable errors
|
||||
if (class_exists(Dotenv::class)) {
|
||||
error_reporting(E_ALL);
|
||||
ini_set('display_errors', '1');
|
||||
$dotenv = new Dotenv();
|
||||
$dotenv->load(__DIR__ . '/../.env');
|
||||
// This class alias tricks the ConfigAbstractFactory to return Lock\Factory instances even with a different service name
|
||||
if (! class_exists('Shlinkio\Shlink\LocalLockFactory')) {
|
||||
class_alias(Lock\LockFactory::class, 'Shlinkio\Shlink\LocalLockFactory');
|
||||
}
|
||||
|
||||
// Build container
|
||||
$config = require __DIR__ . '/config.php';
|
||||
$container = new ServiceManager($config['dependencies']);
|
||||
$container->setService('config', $config);
|
||||
return $container;
|
||||
return (function () {
|
||||
$config = require __DIR__ . '/config.php';
|
||||
$container = new ServiceManager($config['dependencies']);
|
||||
$container->setService('config', $config);
|
||||
|
||||
return $container;
|
||||
})();
|
||||
|
||||
15
config/run.php
Normal file
15
config/run.php
Normal file
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Mezzio\Application;
|
||||
use Psr\Container\ContainerInterface;
|
||||
use Symfony\Component\Console\Application as CliApp;
|
||||
|
||||
return function (bool $isCli = false): void {
|
||||
/** @var ContainerInterface $container */
|
||||
$container = include __DIR__ . '/container.php';
|
||||
$app = $container->get($isCli ? CliApp::class : Application::class);
|
||||
|
||||
$app->run();
|
||||
};
|
||||
18
config/test/bootstrap_api_tests.php
Normal file
18
config/test/bootstrap_api_tests.php
Normal file
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Shlinkio\Shlink\TestUtils;
|
||||
|
||||
use Doctrine\ORM\EntityManager;
|
||||
use Psr\Container\ContainerInterface;
|
||||
|
||||
/** @var ContainerInterface $container */
|
||||
$container = require __DIR__ . '/../container.php';
|
||||
$testHelper = $container->get(Helper\TestHelper::class);
|
||||
$config = $container->get('config');
|
||||
$em = $container->get(EntityManager::class);
|
||||
|
||||
$testHelper->createTestDb();
|
||||
ApiTest\ApiTestCase::setApiClient($container->get('shlink_test_api_client'));
|
||||
ApiTest\ApiTestCase::setSeedFixturesCallback(fn () => $testHelper->seedFixtures($em, $config['data_fixtures'] ?? []));
|
||||
12
config/test/bootstrap_db_tests.php
Normal file
12
config/test/bootstrap_db_tests.php
Normal file
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Shlinkio\Shlink\TestUtils;
|
||||
|
||||
use Psr\Container\ContainerInterface;
|
||||
|
||||
/** @var ContainerInterface $container */
|
||||
$container = require __DIR__ . '/../container.php';
|
||||
$container->get(Helper\TestHelper::class)->createTestDb();
|
||||
DbTest\DatabaseTestCase::setEntityManager($container->get('em'));
|
||||
104
config/test/test_config.global.php
Normal file
104
config/test/test_config.global.php
Normal file
@@ -0,0 +1,104 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Shlinkio\Shlink;
|
||||
|
||||
use GuzzleHttp\Client;
|
||||
use Laminas\ConfigAggregator\ConfigAggregator;
|
||||
use Laminas\ServiceManager\Factory\InvokableFactory;
|
||||
use PDO;
|
||||
|
||||
use function Shlinkio\Shlink\Common\env;
|
||||
use function sprintf;
|
||||
use function sys_get_temp_dir;
|
||||
|
||||
$swooleTestingHost = '127.0.0.1';
|
||||
$swooleTestingPort = 9999;
|
||||
|
||||
$buildDbConnection = function (): array {
|
||||
$driver = env('DB_DRIVER', 'sqlite');
|
||||
$isCi = env('TRAVIS', false);
|
||||
$getMysqlHost = fn (string $driver) => sprintf('shlink_db%s', $driver === 'mysql' ? '' : '_maria');
|
||||
|
||||
$driverConfigMap = [
|
||||
'sqlite' => [
|
||||
'driver' => 'pdo_sqlite',
|
||||
'path' => sys_get_temp_dir() . '/shlink-tests.db',
|
||||
],
|
||||
'mysql' => [
|
||||
'driver' => 'pdo_mysql',
|
||||
'host' => $isCi ? '127.0.0.1' : $getMysqlHost($driver),
|
||||
'user' => 'root',
|
||||
'password' => $isCi ? '' : 'root',
|
||||
'dbname' => 'shlink_test',
|
||||
'charset' => 'utf8',
|
||||
'driverOptions' => [
|
||||
PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8',
|
||||
],
|
||||
],
|
||||
'postgres' => [
|
||||
'driver' => 'pdo_pgsql',
|
||||
'host' => $isCi ? '127.0.0.1' : 'shlink_db_postgres',
|
||||
'user' => 'postgres',
|
||||
'password' => $isCi ? '' : 'root',
|
||||
'dbname' => 'shlink_test',
|
||||
'charset' => 'utf8',
|
||||
],
|
||||
];
|
||||
$driverConfigMap['maria'] = $driverConfigMap['mysql'];
|
||||
|
||||
return $driverConfigMap[$driver] ?? [];
|
||||
};
|
||||
|
||||
return [
|
||||
|
||||
'debug' => true,
|
||||
ConfigAggregator::ENABLE_CACHE => false,
|
||||
|
||||
'url_shortener' => [
|
||||
'domain' => [
|
||||
'schema' => 'http',
|
||||
'hostname' => 'doma.in',
|
||||
],
|
||||
'validate_url' => true,
|
||||
],
|
||||
|
||||
'mezzio-swoole' => [
|
||||
'enable_coroutine' => false,
|
||||
'swoole-http-server' => [
|
||||
'host' => $swooleTestingHost,
|
||||
'port' => $swooleTestingPort,
|
||||
'process-name' => 'shlink_test',
|
||||
'options' => [
|
||||
'pid_file' => sys_get_temp_dir() . '/shlink-test-swoole.pid',
|
||||
'worker_num' => 1,
|
||||
'task_worker_num' => 1,
|
||||
'enable_coroutine' => false,
|
||||
],
|
||||
],
|
||||
],
|
||||
|
||||
'dependencies' => [
|
||||
'services' => [
|
||||
'shlink_test_api_client' => new Client([
|
||||
'base_uri' => sprintf('http://%s:%s/', $swooleTestingHost, $swooleTestingPort),
|
||||
'http_errors' => false,
|
||||
]),
|
||||
],
|
||||
'factories' => [
|
||||
TestUtils\Helper\TestHelper::class => InvokableFactory::class,
|
||||
],
|
||||
],
|
||||
|
||||
'entity_manager' => [
|
||||
'connection' => $buildDbConnection(),
|
||||
],
|
||||
|
||||
'data_fixtures' => [
|
||||
'paths' => [
|
||||
__DIR__ . '/../../module/Rest/test-api/Fixtures',
|
||||
],
|
||||
],
|
||||
|
||||
];
|
||||
2
data/infra/database_maria/.gitignore
vendored
Executable file
2
data/infra/database_maria/.gitignore
vendored
Executable file
@@ -0,0 +1,2 @@
|
||||
*
|
||||
!.gitignore
|
||||
2
data/infra/database_pg/.gitignore
vendored
Executable file
2
data/infra/database_pg/.gitignore
vendored
Executable file
@@ -0,0 +1,2 @@
|
||||
*
|
||||
!.gitignore
|
||||
@@ -1,6 +0,0 @@
|
||||
FROM mysql:5.7
|
||||
MAINTAINER Alejandro Celaya <alejandro@alejandrocelaya.com>
|
||||
|
||||
# Enable remote access (default is localhost only, we change this
|
||||
# otherwise our database would not be reachable from outside the container)
|
||||
RUN sed -i -e"s/^bind-address\s*=\s*127.0.0.1/bind-address = 0.0.0.0/" /etc/mysql/my.cnf
|
||||
11
data/infra/examples/apache-vhost.conf
Normal file
11
data/infra/examples/apache-vhost.conf
Normal file
@@ -0,0 +1,11 @@
|
||||
<VirtualHost *:80>
|
||||
ServerName doma.in
|
||||
DocumentRoot "/path/to/shlink/public"
|
||||
|
||||
<Directory "/path/to/shlink/public">
|
||||
Options FollowSymLinks Includes ExecCGI
|
||||
AllowOverride all
|
||||
Order allow,deny
|
||||
Allow from all
|
||||
</Directory>
|
||||
</VirtualHost>
|
||||
22
data/infra/examples/nginx-vhost.conf
Normal file
22
data/infra/examples/nginx-vhost.conf
Normal file
@@ -0,0 +1,22 @@
|
||||
server {
|
||||
server_name doma.in;
|
||||
listen 80;
|
||||
root /path/to/shlink/public;
|
||||
index index.php;
|
||||
charset utf-8;
|
||||
|
||||
location / {
|
||||
try_files $uri $uri/ /index.php$is_args$args;
|
||||
}
|
||||
|
||||
location ~ \.php$ {
|
||||
fastcgi_split_path_info ^(.+\.php)(/.+)$;
|
||||
fastcgi_pass unix:/var/run/php/php7.4-fpm.sock;
|
||||
fastcgi_index index.php;
|
||||
include fastcgi.conf;
|
||||
}
|
||||
|
||||
location ~ /\.ht {
|
||||
deny all;
|
||||
}
|
||||
}
|
||||
13
data/infra/examples/shlink-daemon-logrotate.conf
Normal file
13
data/infra/examples/shlink-daemon-logrotate.conf
Normal file
@@ -0,0 +1,13 @@
|
||||
/var/log/shlink/shlink_swoole.log {
|
||||
su root root
|
||||
daily
|
||||
missingok
|
||||
rotate 120
|
||||
compress
|
||||
delaycompress
|
||||
notifempty
|
||||
create 0640 root root
|
||||
postrotate
|
||||
/etc/init.d/shlink_swoole restart
|
||||
endscript
|
||||
}
|
||||
54
data/infra/examples/shlink-daemon.sh
Normal file
54
data/infra/examples/shlink-daemon.sh
Normal file
@@ -0,0 +1,54 @@
|
||||
#!/bin/bash
|
||||
### BEGIN INIT INFO
|
||||
# Provides: shlink_swoole
|
||||
# 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
|
||||
### END INIT INFO
|
||||
|
||||
SCRIPT=/path/to/shlink/vendor/bin/mezzio-swoole\ start
|
||||
RUNAS=root
|
||||
|
||||
PIDFILE=/var/run/shlink_swoole.pid
|
||||
LOGDIR=/var/log/shlink
|
||||
LOGFILE=${LOGDIR}/shlink_swoole.log
|
||||
|
||||
start() {
|
||||
if [[ -f "$PIDFILE" ]] && kill -0 $(cat "$PIDFILE"); then
|
||||
echo 'Shlink with swoole already running' >&2
|
||||
return 1
|
||||
fi
|
||||
echo 'Starting shlink with swoole' >&2
|
||||
mkdir -p "$LOGDIR"
|
||||
touch "$LOGFILE"
|
||||
local CMD="$SCRIPT &> \"$LOGFILE\" & echo \$!"
|
||||
su -c "$CMD" $RUNAS > "$PIDFILE"
|
||||
echo 'Shlink started' >&2
|
||||
}
|
||||
|
||||
stop() {
|
||||
if [[ ! -f "$PIDFILE" ]] || ! kill -0 $(cat "$PIDFILE"); then
|
||||
echo 'Shlink with swoole not running' >&2
|
||||
return 1
|
||||
fi
|
||||
echo 'Stopping shlink with swoole' >&2
|
||||
kill -15 $(cat "$PIDFILE") && rm -f "$PIDFILE"
|
||||
echo 'Shlink stopped' >&2
|
||||
}
|
||||
|
||||
case "$1" in
|
||||
start)
|
||||
start
|
||||
;;
|
||||
stop)
|
||||
stop
|
||||
;;
|
||||
restart)
|
||||
stop
|
||||
start
|
||||
;;
|
||||
*)
|
||||
echo "Usage: $0 {start|stop|restart}"
|
||||
esac
|
||||
@@ -1,5 +0,0 @@
|
||||
FROM nginx:1.11.6-alpine
|
||||
MAINTAINER Alejandro Celaya <alejandro@alejandrocelaya.com>
|
||||
|
||||
# Delete default nginx vhost
|
||||
RUN rm /etc/nginx/conf.d/default.conf
|
||||
@@ -1,54 +1,38 @@
|
||||
FROM php:7.1-fpm-alpine
|
||||
FROM php:7.4.1-fpm-alpine3.10
|
||||
MAINTAINER Alejandro Celaya <alejandro@alejandrocelaya.com>
|
||||
|
||||
ENV APCU_VERSION 5.1.18
|
||||
ENV APCU_BC_VERSION 1.0.5
|
||||
ENV XDEBUG_VERSION 2.9.0
|
||||
|
||||
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 mbstring
|
||||
RUN docker-php-ext-install calendar
|
||||
|
||||
RUN apk add --no-cache --virtual sqlite-libs
|
||||
RUN apk add --no-cache --virtual sqlite-dev
|
||||
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 --virtual icu-dev
|
||||
RUN apk add --no-cache icu-dev
|
||||
RUN docker-php-ext-install intl
|
||||
|
||||
RUN apk add --no-cache --virtual zlib-dev
|
||||
RUN apk add --no-cache libzip-dev zlib-dev
|
||||
RUN docker-php-ext-install zip
|
||||
|
||||
RUN apk add --no-cache --virtual libmcrypt-dev
|
||||
RUN docker-php-ext-install mcrypt
|
||||
|
||||
RUN apk add --no-cache --virtual libpng-dev
|
||||
RUN apk add --no-cache libpng-dev
|
||||
RUN docker-php-ext-install gd
|
||||
|
||||
# Install redis extension
|
||||
ADD https://github.com/phpredis/phpredis/archive/3.1.4.tar.gz /tmp/phpredis.tar.gz
|
||||
RUN mkdir -p /usr/src/php/ext/redis\
|
||||
&& tar xf /tmp/phpredis.tar.gz -C /usr/src/php/ext/redis --strip-components=1
|
||||
# configure and install
|
||||
RUN docker-php-ext-configure redis\
|
||||
&& docker-php-ext-install redis
|
||||
# cleanup
|
||||
RUN rm /tmp/phpredis.tar.gz
|
||||
|
||||
# Install memcached extension
|
||||
RUN apk add --no-cache --virtual cyrus-sasl-dev
|
||||
RUN apk add --no-cache --virtual libmemcached-dev
|
||||
ADD https://github.com/php-memcached-dev/php-memcached/archive/php7.tar.gz /tmp/memcached.tar.gz
|
||||
RUN mkdir -p /usr/src/php/ext/memcached\
|
||||
&& tar xf /tmp/memcached.tar.gz -C /usr/src/php/ext/memcached --strip-components=1
|
||||
# configure and install
|
||||
RUN docker-php-ext-configure memcached\
|
||||
&& docker-php-ext-install memcached
|
||||
# cleanup
|
||||
RUN rm /tmp/memcached.tar.gz
|
||||
RUN apk add --no-cache postgresql-dev
|
||||
RUN docker-php-ext-install pdo_pgsql
|
||||
|
||||
# Install APCu extension
|
||||
ADD https://pecl.php.net/get/apcu-5.1.3.tgz /tmp/apcu.tar.gz
|
||||
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
|
||||
# configure and install
|
||||
@@ -58,7 +42,7 @@ RUN docker-php-ext-configure apcu\
|
||||
RUN rm /tmp/apcu.tar.gz
|
||||
|
||||
# Install APCu-BC extension
|
||||
ADD https://pecl.php.net/get/apcu_bc-1.0.3.tgz /tmp/apcu_bc.tar.gz
|
||||
ADD https://pecl.php.net/get/apcu_bc-$APCU_BC_VERSION.tgz /tmp/apcu_bc.tar.gz
|
||||
RUN mkdir -p /usr/src/php/ext/apcu-bc\
|
||||
&& tar xf /tmp/apcu_bc.tar.gz -C /usr/src/php/ext/apcu-bc --strip-components=1
|
||||
# configure and install
|
||||
@@ -72,7 +56,7 @@ RUN rm /usr/local/etc/php/conf.d/docker-php-ext-apcu.ini
|
||||
RUN echo extension=apcu.so > /usr/local/etc/php/conf.d/20-php-ext-apcu.ini
|
||||
|
||||
# Install xdebug
|
||||
ADD https://pecl.php.net/get/xdebug-2.5.0 /tmp/xdebug.tar.gz
|
||||
ADD https://pecl.php.net/get/xdebug-$XDEBUG_VERSION /tmp/xdebug.tar.gz
|
||||
RUN mkdir -p /usr/src/php/ext/xdebug\
|
||||
&& tar xf /tmp/xdebug.tar.gz -C /usr/src/php/ext/xdebug --strip-components=1
|
||||
# configure and install
|
||||
|
||||
@@ -1 +1,6 @@
|
||||
date.timezone = Europe/Madrid
|
||||
display_errors=On
|
||||
error_reporting=-1
|
||||
memory_limit=-1
|
||||
log_errors_max_len=0
|
||||
zend.assertions=1
|
||||
assert.exception=1
|
||||
|
||||
95
data/infra/swoole.Dockerfile
Normal file
95
data/infra/swoole.Dockerfile
Normal file
@@ -0,0 +1,95 @@
|
||||
FROM php:7.4.1-alpine3.10
|
||||
MAINTAINER Alejandro Celaya <alejandro@alejandrocelaya.com>
|
||||
|
||||
ENV APCU_VERSION 5.1.18
|
||||
ENV APCU_BC_VERSION 1.0.5
|
||||
ENV INOTIFY_VERSION 2.0.0
|
||||
ENV SWOOLE_VERSION 4.4.12
|
||||
|
||||
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
|
||||
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
|
||||
|
||||
# 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
|
||||
# configure and install
|
||||
RUN docker-php-ext-configure apcu\
|
||||
&& docker-php-ext-install apcu
|
||||
# cleanup
|
||||
RUN rm /tmp/apcu.tar.gz
|
||||
|
||||
# Install APCu-BC extension
|
||||
ADD https://pecl.php.net/get/apcu_bc-$APCU_BC_VERSION.tgz /tmp/apcu_bc.tar.gz
|
||||
RUN mkdir -p /usr/src/php/ext/apcu-bc\
|
||||
&& tar xf /tmp/apcu_bc.tar.gz -C /usr/src/php/ext/apcu-bc --strip-components=1
|
||||
# configure and install
|
||||
RUN docker-php-ext-configure apcu-bc\
|
||||
&& docker-php-ext-install apcu-bc
|
||||
# cleanup
|
||||
RUN rm /tmp/apcu_bc.tar.gz
|
||||
|
||||
# Load APCU.ini before APC.ini
|
||||
RUN rm /usr/local/etc/php/conf.d/docker-php-ext-apcu.ini
|
||||
RUN echo extension=apcu.so > /usr/local/etc/php/conf.d/20-php-ext-apcu.ini
|
||||
|
||||
# Install inotify extension
|
||||
ADD https://pecl.php.net/get/inotify-$INOTIFY_VERSION.tgz /tmp/inotify.tar.gz
|
||||
RUN mkdir -p /usr/src/php/ext/inotify\
|
||||
&& tar xf /tmp/inotify.tar.gz -C /usr/src/php/ext/inotify --strip-components=1
|
||||
# configure and install
|
||||
RUN docker-php-ext-configure inotify\
|
||||
&& docker-php-ext-install inotify
|
||||
# cleanup
|
||||
RUN rm /tmp/inotify.tar.gz
|
||||
|
||||
# Install swoole
|
||||
# First line fixes an error when installing pecl extensions. Found in https://github.com/docker-library/php/issues/233
|
||||
RUN apk add --no-cache --virtual .phpize-deps $PHPIZE_DEPS && \
|
||||
pecl install swoole-${SWOOLE_VERSION} && \
|
||||
docker-php-ext-enable swoole && \
|
||||
apk del .phpize-deps
|
||||
|
||||
# Install composer
|
||||
RUN php -r "readfile('https://getcomposer.org/installer');" | php
|
||||
RUN chmod +x composer.phar
|
||||
RUN mv composer.phar /usr/local/bin/composer
|
||||
|
||||
# Make home directory writable by anyone
|
||||
RUN chmod 777 /home
|
||||
|
||||
VOLUME /home/shlink
|
||||
WORKDIR /home/shlink
|
||||
|
||||
# Expose swoole 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
|
||||
# This forces the app to be started every second until the exit code is 0
|
||||
until php ./vendor/bin/mezzio-swoole start; do sleep 1 ; done
|
||||
@@ -1 +0,0 @@
|
||||
extension="apcu.so"
|
||||
@@ -1 +0,0 @@
|
||||
extension="memcached.so"
|
||||
2
data/locks/.gitignore
vendored
Normal file
2
data/locks/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
*
|
||||
!.gitignore
|
||||
@@ -1,23 +1,27 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace ShlinkMigrations;
|
||||
|
||||
use Doctrine\DBAL\Migrations\AbstractMigration;
|
||||
use Doctrine\DBAL\DBALException;
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\DBAL\Schema\SchemaException;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
|
||||
/**
|
||||
* Auto-generated Migration: Please modify to your needs!
|
||||
*/
|
||||
class Version20160819142757 extends AbstractMigration
|
||||
{
|
||||
const MYSQL = 'mysql';
|
||||
const SQLITE = 'sqlite';
|
||||
private const MYSQL = 'mysql';
|
||||
private const SQLITE = 'sqlite';
|
||||
|
||||
/**
|
||||
* @param Schema $schema
|
||||
* @throws DBALException
|
||||
* @throws SchemaException
|
||||
*/
|
||||
public function up(Schema $schema)
|
||||
public function up(Schema $schema): void
|
||||
{
|
||||
$db = $this->connection->getDatabasePlatform()->getName();
|
||||
$table = $schema->getTable('short_urls');
|
||||
@@ -31,9 +35,9 @@ class Version20160819142757 extends AbstractMigration
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Schema $schema
|
||||
* @throws DBALException
|
||||
*/
|
||||
public function down(Schema $schema)
|
||||
public function down(Schema $schema): void
|
||||
{
|
||||
$db = $this->connection->getDatabasePlatform()->getName();
|
||||
}
|
||||
|
||||
@@ -1,21 +1,19 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace ShlinkMigrations;
|
||||
|
||||
use Doctrine\DBAL\Migrations\AbstractMigration;
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\DBAL\Types\Type;
|
||||
use Doctrine\DBAL\Types\Types;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
|
||||
/**
|
||||
* Auto-generated Migration: Please modify to your needs!
|
||||
*/
|
||||
class Version20160820191203 extends AbstractMigration
|
||||
{
|
||||
/**
|
||||
* @param Schema $schema
|
||||
*/
|
||||
public function up(Schema $schema)
|
||||
public function up(Schema $schema): void
|
||||
{
|
||||
// Check if the tables already exist
|
||||
$tables = $schema->getTables();
|
||||
@@ -29,15 +27,15 @@ class Version20160820191203 extends AbstractMigration
|
||||
$this->createShortUrlsInTagsTable($schema);
|
||||
}
|
||||
|
||||
protected function createTagsTable(Schema $schema)
|
||||
private function createTagsTable(Schema $schema): void
|
||||
{
|
||||
$table = $schema->createTable('tags');
|
||||
$table->addColumn('id', Type::BIGINT, [
|
||||
$table->addColumn('id', Types::BIGINT, [
|
||||
'unsigned' => true,
|
||||
'autoincrement' => true,
|
||||
'notnull' => true,
|
||||
]);
|
||||
$table->addColumn('name', Type::STRING, [
|
||||
$table->addColumn('name', Types::STRING, [
|
||||
'length' => 255,
|
||||
'notnull' => true,
|
||||
]);
|
||||
@@ -46,14 +44,14 @@ class Version20160820191203 extends AbstractMigration
|
||||
$table->setPrimaryKey(['id']);
|
||||
}
|
||||
|
||||
protected function createShortUrlsInTagsTable(Schema $schema)
|
||||
private function createShortUrlsInTagsTable(Schema $schema): void
|
||||
{
|
||||
$table = $schema->createTable('short_urls_in_tags');
|
||||
$table->addColumn('short_url_id', Type::BIGINT, [
|
||||
$table->addColumn('short_url_id', Types::BIGINT, [
|
||||
'unsigned' => true,
|
||||
'notnull' => true,
|
||||
]);
|
||||
$table->addColumn('tag_id', Type::BIGINT, [
|
||||
$table->addColumn('tag_id', Types::BIGINT, [
|
||||
'unsigned' => true,
|
||||
'notnull' => true,
|
||||
]);
|
||||
@@ -70,10 +68,7 @@ class Version20160820191203 extends AbstractMigration
|
||||
$table->setPrimaryKey(['short_url_id', 'tag_id']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Schema $schema
|
||||
*/
|
||||
public function down(Schema $schema)
|
||||
public function down(Schema $schema): void
|
||||
{
|
||||
$schema->dropTable('short_urls_in_tags');
|
||||
$schema->dropTable('tags');
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace ShlinkMigrations;
|
||||
|
||||
use Doctrine\DBAL\Migrations\AbstractMigration;
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\DBAL\Schema\SchemaException;
|
||||
use Doctrine\DBAL\Types\Type;
|
||||
use Doctrine\DBAL\Types\Types;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
|
||||
/**
|
||||
* Auto-generated Migration: Please modify to your needs!
|
||||
@@ -14,29 +15,27 @@ use Doctrine\DBAL\Types\Type;
|
||||
class Version20171021093246 extends AbstractMigration
|
||||
{
|
||||
/**
|
||||
* @param Schema $schema
|
||||
* @throws SchemaException
|
||||
*/
|
||||
public function up(Schema $schema)
|
||||
public function up(Schema $schema): void
|
||||
{
|
||||
$shortUrls = $schema->getTable('short_urls');
|
||||
if ($shortUrls->hasColumn('valid_since')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$shortUrls->addColumn('valid_since', Type::DATETIME, [
|
||||
$shortUrls->addColumn('valid_since', Types::DATETIME, [
|
||||
'notnull' => false,
|
||||
]);
|
||||
$shortUrls->addColumn('valid_until', Type::DATETIME, [
|
||||
$shortUrls->addColumn('valid_until', Types::DATETIME, [
|
||||
'notnull' => false,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Schema $schema
|
||||
* @throws SchemaException
|
||||
*/
|
||||
public function down(Schema $schema)
|
||||
public function down(Schema $schema): void
|
||||
{
|
||||
$shortUrls = $schema->getTable('short_urls');
|
||||
if (! $shortUrls->hasColumn('valid_since')) {
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace ShlinkMigrations;
|
||||
|
||||
use Doctrine\DBAL\Migrations\AbstractMigration;
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\DBAL\Schema\SchemaException;
|
||||
use Doctrine\DBAL\Types\Type;
|
||||
use Doctrine\DBAL\Types\Types;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
|
||||
/**
|
||||
* Auto-generated Migration: Please modify to your needs!
|
||||
@@ -14,27 +15,25 @@ use Doctrine\DBAL\Types\Type;
|
||||
class Version20171022064541 extends AbstractMigration
|
||||
{
|
||||
/**
|
||||
* @param Schema $schema
|
||||
* @throws SchemaException
|
||||
*/
|
||||
public function up(Schema $schema)
|
||||
public function up(Schema $schema): void
|
||||
{
|
||||
$shortUrls = $schema->getTable('short_urls');
|
||||
if ($shortUrls->hasColumn('max_visits')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$shortUrls->addColumn('max_visits', Type::INTEGER, [
|
||||
$shortUrls->addColumn('max_visits', Types::INTEGER, [
|
||||
'unsigned' => true,
|
||||
'notnull' => false,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Schema $schema
|
||||
* @throws SchemaException
|
||||
*/
|
||||
public function down(Schema $schema)
|
||||
public function down(Schema $schema): void
|
||||
{
|
||||
$shortUrls = $schema->getTable('short_urls');
|
||||
if (! $shortUrls->hasColumn('max_visits')) {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace ShlinkMigrations;
|
||||
@@ -16,7 +17,6 @@ final class Version20180801183328 extends AbstractMigration
|
||||
private const OLD_SIZE = 10;
|
||||
|
||||
/**
|
||||
* @param Schema $schema
|
||||
* @throws SchemaException
|
||||
*/
|
||||
public function up(Schema $schema): void
|
||||
@@ -25,7 +25,6 @@ final class Version20180801183328 extends AbstractMigration
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Schema $schema
|
||||
* @throws SchemaException
|
||||
*/
|
||||
public function down(Schema $schema): void
|
||||
@@ -34,8 +33,6 @@ final class Version20180801183328 extends AbstractMigration
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Schema $schema
|
||||
* @param int $size
|
||||
* @throws SchemaException
|
||||
*/
|
||||
private function setSize(Schema $schema, int $size): void
|
||||
|
||||
73
data/migrations/Version20180913205455.php
Normal file
73
data/migrations/Version20180913205455.php
Normal file
@@ -0,0 +1,73 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace ShlinkMigrations;
|
||||
|
||||
use Doctrine\DBAL\DBALException;
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
use PDO;
|
||||
use Shlinkio\Shlink\Common\Exception\InvalidArgumentException;
|
||||
use Shlinkio\Shlink\Common\Util\IpAddress;
|
||||
|
||||
/**
|
||||
* Auto-generated Migration: Please modify to your needs!
|
||||
*/
|
||||
final class Version20180913205455 extends AbstractMigration
|
||||
{
|
||||
/**
|
||||
*/
|
||||
public function up(Schema $schema): void
|
||||
{
|
||||
// Nothing to create
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws DBALException
|
||||
*/
|
||||
public function postUp(Schema $schema): void
|
||||
{
|
||||
$qb = $this->connection->createQueryBuilder();
|
||||
$qb->select('id', 'remote_addr')
|
||||
->from('visits');
|
||||
$st = $this->connection->executeQuery($qb->getSQL());
|
||||
|
||||
$qb = $this->connection->createQueryBuilder();
|
||||
$qb->update('visits', 'v')
|
||||
->set('v.remote_addr', ':obfuscatedAddr')
|
||||
->where('v.id=:id');
|
||||
|
||||
while ($row = $st->fetch(PDO::FETCH_ASSOC)) {
|
||||
$addr = $row['remote_addr'] ?? null;
|
||||
if ($addr === null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$qb->setParameters([
|
||||
'id' => $row['id'],
|
||||
'obfuscatedAddr' => $this->determineAddress((string) $addr),
|
||||
])->execute();
|
||||
}
|
||||
}
|
||||
|
||||
private function determineAddress(string $addr): ?string
|
||||
{
|
||||
if ($addr === IpAddress::LOCALHOST) {
|
||||
return $addr;
|
||||
}
|
||||
|
||||
try {
|
||||
return (string) IpAddress::fromString($addr)->getObfuscatedCopy();
|
||||
} catch (InvalidArgumentException $e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*/
|
||||
public function down(Schema $schema): void
|
||||
{
|
||||
// Nothing to rollback
|
||||
}
|
||||
}
|
||||
50
data/migrations/Version20180915110857.php
Normal file
50
data/migrations/Version20180915110857.php
Normal file
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace ShlinkMigrations;
|
||||
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\DBAL\Schema\SchemaException;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
|
||||
/**
|
||||
* Auto-generated Migration: Please modify to your needs!
|
||||
*/
|
||||
final class Version20180915110857 extends AbstractMigration
|
||||
{
|
||||
private const ON_DELETE_MAP = [
|
||||
'visit_locations' => 'SET NULL',
|
||||
'short_urls' => 'CASCADE',
|
||||
];
|
||||
|
||||
/**
|
||||
* @throws SchemaException
|
||||
*/
|
||||
public function up(Schema $schema): void
|
||||
{
|
||||
$visits = $schema->getTable('visits');
|
||||
$foreignKeys = $visits->getForeignKeys();
|
||||
|
||||
// Remove all existing foreign keys and add them again with CASCADE delete
|
||||
foreach ($foreignKeys as $foreignKey) {
|
||||
$visits->removeForeignKey($foreignKey->getName());
|
||||
$foreignTable = $foreignKey->getForeignTableName();
|
||||
|
||||
$visits->addForeignKeyConstraint(
|
||||
$foreignTable,
|
||||
$foreignKey->getLocalColumns(),
|
||||
$foreignKey->getForeignColumns(),
|
||||
[
|
||||
'onDelete' => self::ON_DELETE_MAP[$foreignTable],
|
||||
'onUpdate' => 'RESTRICT',
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public function down(Schema $schema): void
|
||||
{
|
||||
// Nothing to run
|
||||
}
|
||||
}
|
||||
68
data/migrations/Version20181020060559.php
Normal file
68
data/migrations/Version20181020060559.php
Normal file
@@ -0,0 +1,68 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace ShlinkMigrations;
|
||||
|
||||
use Doctrine\DBAL\DBALException;
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\DBAL\Schema\SchemaException;
|
||||
use Doctrine\DBAL\Schema\Table;
|
||||
use Doctrine\DBAL\Types\Types;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
|
||||
/**
|
||||
* Auto-generated Migration: Please modify to your needs!
|
||||
*/
|
||||
final class Version20181020060559 extends AbstractMigration
|
||||
{
|
||||
private const COLUMNS = [
|
||||
'countryCode' => 'country_code',
|
||||
'countryName' => 'country_name',
|
||||
'regionName' => 'region_name',
|
||||
'cityName' => 'city_name',
|
||||
];
|
||||
|
||||
/**
|
||||
* @throws SchemaException
|
||||
*/
|
||||
public function up(Schema $schema): void
|
||||
{
|
||||
$this->createColumns($schema->getTable('visit_locations'), self::COLUMNS);
|
||||
}
|
||||
|
||||
private function createColumns(Table $visitLocations, array $columnNames): void
|
||||
{
|
||||
foreach ($columnNames as $name) {
|
||||
if (! $visitLocations->hasColumn($name)) {
|
||||
$visitLocations->addColumn($name, Types::STRING, ['notnull' => false]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws SchemaException
|
||||
* @throws DBALException
|
||||
*/
|
||||
public function postUp(Schema $schema): void
|
||||
{
|
||||
$visitLocations = $schema->getTable('visit_locations');
|
||||
|
||||
// If the camel case columns do not exist, do nothing
|
||||
if (! $visitLocations->hasColumn('countryCode')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$qb = $this->connection->createQueryBuilder();
|
||||
$qb->update('visit_locations');
|
||||
foreach (self::COLUMNS as $camelCaseName => $snakeCaseName) {
|
||||
$qb->set($snakeCaseName, $camelCaseName);
|
||||
}
|
||||
$qb->execute();
|
||||
}
|
||||
|
||||
public function down(Schema $schema): void
|
||||
{
|
||||
// No down
|
||||
}
|
||||
}
|
||||
41
data/migrations/Version20181020065148.php
Normal file
41
data/migrations/Version20181020065148.php
Normal file
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace ShlinkMigrations;
|
||||
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\DBAL\Schema\SchemaException;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
|
||||
/**
|
||||
* Auto-generated Migration: Please modify to your needs!
|
||||
*/
|
||||
final class Version20181020065148 extends AbstractMigration
|
||||
{
|
||||
private const CAMEL_CASE_COLUMNS = [
|
||||
'countryCode',
|
||||
'countryName',
|
||||
'regionName',
|
||||
'cityName',
|
||||
];
|
||||
|
||||
/**
|
||||
* @throws SchemaException
|
||||
*/
|
||||
public function up(Schema $schema): void
|
||||
{
|
||||
$visitLocations = $schema->getTable('visit_locations');
|
||||
|
||||
foreach (self::CAMEL_CASE_COLUMNS as $name) {
|
||||
if ($visitLocations->hasColumn($name)) {
|
||||
$visitLocations->dropColumn($name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function down(Schema $schema): void
|
||||
{
|
||||
// No down
|
||||
}
|
||||
}
|
||||
37
data/migrations/Version20181110175521.php
Normal file
37
data/migrations/Version20181110175521.php
Normal file
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace ShlinkMigrations;
|
||||
|
||||
use Doctrine\DBAL\Schema\Column;
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\DBAL\Schema\SchemaException;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
|
||||
final class Version20181110175521 extends AbstractMigration
|
||||
{
|
||||
/**
|
||||
* @throws SchemaException
|
||||
*/
|
||||
public function up(Schema $schema): void
|
||||
{
|
||||
$this->getUserAgentColumn($schema)->setLength(512);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws SchemaException
|
||||
*/
|
||||
public function down(Schema $schema): void
|
||||
{
|
||||
$this->getUserAgentColumn($schema)->setLength(256);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws SchemaException
|
||||
*/
|
||||
private function getUserAgentColumn(Schema $schema): Column
|
||||
{
|
||||
return $schema->getTable('visits')->getColumn('user_agent');
|
||||
}
|
||||
}
|
||||
37
data/migrations/Version20190824075137.php
Normal file
37
data/migrations/Version20190824075137.php
Normal file
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace ShlinkMigrations;
|
||||
|
||||
use Doctrine\DBAL\Schema\Column;
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\DBAL\Schema\SchemaException;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
|
||||
final class Version20190824075137 extends AbstractMigration
|
||||
{
|
||||
/**
|
||||
* @throws SchemaException
|
||||
*/
|
||||
public function up(Schema $schema): void
|
||||
{
|
||||
$this->getRefererColumn($schema)->setLength(1024);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws SchemaException
|
||||
*/
|
||||
public function down(Schema $schema): void
|
||||
{
|
||||
$this->getRefererColumn($schema)->setLength(256);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws SchemaException
|
||||
*/
|
||||
private function getRefererColumn(Schema $schema): Column
|
||||
{
|
||||
return $schema->getTable('visits')->getColumn('referer');
|
||||
}
|
||||
}
|
||||
55
data/migrations/Version20190930165521.php
Normal file
55
data/migrations/Version20190930165521.php
Normal file
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace ShlinkMigrations;
|
||||
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\DBAL\Schema\SchemaException;
|
||||
use Doctrine\DBAL\Types\Types;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
|
||||
final class Version20190930165521 extends AbstractMigration
|
||||
{
|
||||
/**
|
||||
* @throws SchemaException
|
||||
*/
|
||||
public function up(Schema $schema): void
|
||||
{
|
||||
$shortUrls = $schema->getTable('short_urls');
|
||||
if ($shortUrls->hasColumn('domain_id')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$domains = $schema->createTable('domains');
|
||||
$domains->addColumn('id', Types::BIGINT, [
|
||||
'unsigned' => true,
|
||||
'autoincrement' => true,
|
||||
'notnull' => true,
|
||||
]);
|
||||
$domains->addColumn('authority', Types::STRING, [
|
||||
'length' => 512,
|
||||
'notnull' => true,
|
||||
]);
|
||||
$domains->addUniqueIndex(['authority']);
|
||||
$domains->setPrimaryKey(['id']);
|
||||
|
||||
$shortUrls->addColumn('domain_id', Types::BIGINT, [
|
||||
'unsigned' => true,
|
||||
'notnull' => false,
|
||||
]);
|
||||
$shortUrls->addForeignKeyConstraint('domains', ['domain_id'], ['id'], [
|
||||
'onDelete' => 'RESTRICT',
|
||||
'onUpdate' => 'RESTRICT',
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws SchemaException
|
||||
*/
|
||||
public function down(Schema $schema): void
|
||||
{
|
||||
$schema->getTable('short_urls')->dropColumn('domain_id');
|
||||
$schema->dropTable('domains');
|
||||
}
|
||||
}
|
||||
49
data/migrations/Version20191001201532.php
Normal file
49
data/migrations/Version20191001201532.php
Normal file
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace ShlinkMigrations;
|
||||
|
||||
use Doctrine\DBAL\Schema\Index;
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\DBAL\Schema\SchemaException;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
|
||||
use function array_reduce;
|
||||
|
||||
final class Version20191001201532 extends AbstractMigration
|
||||
{
|
||||
/**
|
||||
* @throws SchemaException
|
||||
*/
|
||||
public function up(Schema $schema): void
|
||||
{
|
||||
$shortUrls = $schema->getTable('short_urls');
|
||||
if ($shortUrls->hasIndex('unique_short_code_plus_domain')) {
|
||||
return;
|
||||
}
|
||||
|
||||
/** @var Index|null $shortCodesIndex */
|
||||
$shortCodesIndex = array_reduce($shortUrls->getIndexes(), function (?Index $found, Index $current) {
|
||||
[$column] = $current->getColumns();
|
||||
return $column === 'short_code' ? $current : $found;
|
||||
});
|
||||
if ($shortCodesIndex === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
$shortUrls->dropIndex($shortCodesIndex->getName());
|
||||
$shortUrls->addUniqueIndex(['short_code', 'domain_id'], 'unique_short_code_plus_domain');
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws SchemaException
|
||||
*/
|
||||
public function down(Schema $schema): void
|
||||
{
|
||||
$shortUrls = $schema->getTable('short_urls');
|
||||
|
||||
$shortUrls->dropIndex('unique_short_code_plus_domain');
|
||||
$shortUrls->addUniqueIndex(['short_code']);
|
||||
}
|
||||
}
|
||||
37
data/migrations/Version20191020074522.php
Normal file
37
data/migrations/Version20191020074522.php
Normal file
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace ShlinkMigrations;
|
||||
|
||||
use Doctrine\DBAL\Schema\Column;
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\DBAL\Schema\SchemaException;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
|
||||
final class Version20191020074522 extends AbstractMigration
|
||||
{
|
||||
/**
|
||||
* @throws SchemaException
|
||||
*/
|
||||
public function up(Schema $schema): void
|
||||
{
|
||||
$this->getOriginalUrlColumn($schema)->setLength(2048);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws SchemaException
|
||||
*/
|
||||
public function down(Schema $schema): void
|
||||
{
|
||||
$this->getOriginalUrlColumn($schema)->setLength(1024);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws SchemaException
|
||||
*/
|
||||
private function getOriginalUrlColumn(Schema $schema): Column
|
||||
{
|
||||
return $schema->getTable('short_urls')->getColumn('original_url');
|
||||
}
|
||||
}
|
||||
96
data/migrations/Version20200105165647.php
Normal file
96
data/migrations/Version20200105165647.php
Normal file
@@ -0,0 +1,96 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace ShlinkMigrations;
|
||||
|
||||
use Doctrine\DBAL\DBALException;
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\DBAL\Types\Types;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
|
||||
use function Functional\some;
|
||||
|
||||
final class Version20200105165647 extends AbstractMigration
|
||||
{
|
||||
private const COLUMNS = ['lat' => 'latitude', 'lon' => 'longitude'];
|
||||
|
||||
/**
|
||||
* @throws DBALException
|
||||
*/
|
||||
public function preUp(Schema $schema): void
|
||||
{
|
||||
$visitLocations = $schema->getTable('visit_locations');
|
||||
$this->skipIf(some(
|
||||
self::COLUMNS,
|
||||
fn (string $v, string $newColName) => $visitLocations->hasColumn($newColName),
|
||||
), 'New columns already exist');
|
||||
|
||||
foreach (self::COLUMNS as $columnName) {
|
||||
$qb = $this->connection->createQueryBuilder();
|
||||
$qb->update('visit_locations')
|
||||
->set($columnName, ':zeroValue')
|
||||
->where($qb->expr()->orX(
|
||||
$qb->expr()->eq($columnName, ':emptyString'),
|
||||
$qb->expr()->isNull($columnName),
|
||||
))
|
||||
->setParameters([
|
||||
'zeroValue' => '0',
|
||||
'emptyString' => '',
|
||||
])
|
||||
->execute();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws DBALException
|
||||
*/
|
||||
public function up(Schema $schema): void
|
||||
{
|
||||
$visitLocations = $schema->getTable('visit_locations');
|
||||
|
||||
foreach (self::COLUMNS as $newName => $oldName) {
|
||||
$visitLocations->addColumn($newName, Types::FLOAT, [
|
||||
'default' => '0.0',
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws DBALException
|
||||
*/
|
||||
public function postUp(Schema $schema): void
|
||||
{
|
||||
$platformName = $this->connection->getDatabasePlatform()->getName();
|
||||
$castType = $platformName === 'postgres' ? '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();
|
||||
}
|
||||
}
|
||||
|
||||
public function preDown(Schema $schema): void
|
||||
{
|
||||
foreach (self::COLUMNS as $newName => $oldName) {
|
||||
$qb = $this->connection->createQueryBuilder();
|
||||
$qb->update('visit_locations')
|
||||
->set($oldName, $newName)
|
||||
->execute();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws DBALException
|
||||
*/
|
||||
public function down(Schema $schema): void
|
||||
{
|
||||
$visitLocations = $schema->getTable('visit_locations');
|
||||
|
||||
foreach (self::COLUMNS as $colName => $oldName) {
|
||||
$visitLocations->dropColumn($colName);
|
||||
}
|
||||
}
|
||||
}
|
||||
47
data/migrations/Version20200106215144.php
Normal file
47
data/migrations/Version20200106215144.php
Normal file
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace ShlinkMigrations;
|
||||
|
||||
use Doctrine\DBAL\DBALException;
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\DBAL\Types\Types;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
|
||||
use function Functional\none;
|
||||
|
||||
final class Version20200106215144 extends AbstractMigration
|
||||
{
|
||||
private const COLUMNS = ['latitude', 'longitude'];
|
||||
|
||||
/**
|
||||
* @throws DBALException
|
||||
*/
|
||||
public function up(Schema $schema): void
|
||||
{
|
||||
$visitLocations = $schema->getTable('visit_locations');
|
||||
$this->skipIf(none(
|
||||
self::COLUMNS,
|
||||
fn (string $oldColName) => $visitLocations->hasColumn($oldColName),
|
||||
), 'Old columns do not exist');
|
||||
|
||||
foreach (self::COLUMNS as $colName) {
|
||||
$visitLocations->dropColumn($colName);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws DBALException
|
||||
*/
|
||||
public function down(Schema $schema): void
|
||||
{
|
||||
$visitLocations = $schema->getTable('visit_locations');
|
||||
|
||||
foreach (self::COLUMNS as $colName) {
|
||||
$visitLocations->addColumn($colName, Types::STRING, [
|
||||
'notnull' => false,
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
53
data/migrations/Version20200110182849.php
Normal file
53
data/migrations/Version20200110182849.php
Normal file
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace ShlinkMigrations;
|
||||
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
|
||||
use function Functional\each;
|
||||
use function Functional\partial_left;
|
||||
|
||||
final class Version20200110182849 extends AbstractMigration
|
||||
{
|
||||
private const DEFAULT_EMPTY_VALUE = '';
|
||||
private const COLUMN_DEFAULTS_MAP = [
|
||||
'visits' => [
|
||||
'referer',
|
||||
'user_agent',
|
||||
],
|
||||
'visit_locations' => [
|
||||
'timezone',
|
||||
'country_code',
|
||||
'country_name',
|
||||
'region_name',
|
||||
'city_name',
|
||||
],
|
||||
];
|
||||
|
||||
public function up(Schema $schema): void
|
||||
{
|
||||
each(
|
||||
self::COLUMN_DEFAULTS_MAP,
|
||||
fn (array $columns, string $tableName) =>
|
||||
each($columns, partial_left([$this, 'setDefaultValueForColumnInTable'], $tableName)),
|
||||
);
|
||||
}
|
||||
|
||||
public function setDefaultValueForColumnInTable(string $tableName, string $columnName): void
|
||||
{
|
||||
$qb = $this->connection->createQueryBuilder();
|
||||
$qb->update($tableName)
|
||||
->set($columnName, ':emptyValue')
|
||||
->setParameter('emptyValue', self::DEFAULT_EMPTY_VALUE)
|
||||
->where($qb->expr()->isNull($columnName))
|
||||
->execute();
|
||||
}
|
||||
|
||||
public function down(Schema $schema): void
|
||||
{
|
||||
// No need (and no way) to undo this migration
|
||||
}
|
||||
}
|
||||
21
data/migrations_template.txt
Normal file
21
data/migrations_template.txt
Normal file
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace <namespace>;
|
||||
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
|
||||
final class Version<version> extends AbstractMigration
|
||||
{
|
||||
public function up(Schema $schema): void
|
||||
{
|
||||
<up>
|
||||
}
|
||||
|
||||
public function down(Schema $schema): void
|
||||
{
|
||||
<down>
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
version: '2'
|
||||
version: '3'
|
||||
|
||||
services:
|
||||
shlink_php:
|
||||
@@ -6,3 +6,27 @@ services:
|
||||
volumes:
|
||||
- /etc/passwd:/etc/passwd:ro
|
||||
- /etc/group:/etc/group:ro
|
||||
|
||||
shlink_swoole:
|
||||
user: 1000:1000
|
||||
volumes:
|
||||
- /etc/passwd:/etc/passwd:ro
|
||||
- /etc/group:/etc/group:ro
|
||||
|
||||
shlink_db:
|
||||
user: 1000:1000
|
||||
volumes:
|
||||
- /etc/passwd:/etc/passwd:ro
|
||||
- /etc/group:/etc/group:ro
|
||||
|
||||
shlink_db_postgres:
|
||||
user: 1000:1000
|
||||
volumes:
|
||||
- /etc/passwd:/etc/passwd:ro
|
||||
- /etc/group:/etc/group:ro
|
||||
|
||||
shlink_db_maria:
|
||||
user: 1000:1000
|
||||
volumes:
|
||||
- /etc/passwd:/etc/passwd:ro
|
||||
- /etc/group:/etc/group:ro
|
||||
|
||||
@@ -1,17 +1,15 @@
|
||||
version: '2'
|
||||
version: '3'
|
||||
|
||||
services:
|
||||
shlink_nginx:
|
||||
container_name: shlink_nginx
|
||||
build:
|
||||
context: .
|
||||
dockerfile: ./data/infra/nginx.Dockerfile
|
||||
image: nginx:1.17.6-alpine
|
||||
ports:
|
||||
- "8000:80"
|
||||
volumes:
|
||||
- ./:/home/shlink/www
|
||||
- ./docs:/home/shlink/www/public/docs
|
||||
- ./data/infra/vhost.conf:/etc/nginx/conf.d/shlink-vhost.conf
|
||||
- ./data/infra/vhost.conf:/etc/nginx/conf.d/default.conf
|
||||
links:
|
||||
- shlink_php
|
||||
|
||||
@@ -25,12 +23,30 @@ services:
|
||||
- ./data/infra/php.ini:/usr/local/etc/php/php.ini
|
||||
links:
|
||||
- shlink_db
|
||||
- shlink_db_postgres
|
||||
- shlink_db_maria
|
||||
- shlink_redis
|
||||
|
||||
shlink_swoole:
|
||||
container_name: shlink_swoole
|
||||
build:
|
||||
context: .
|
||||
dockerfile: ./data/infra/swoole.Dockerfile
|
||||
ports:
|
||||
- "8080:8080"
|
||||
- "9001:9001"
|
||||
volumes:
|
||||
- ./:/home/shlink
|
||||
- ./data/infra/php.ini:/usr/local/etc/php/php.ini
|
||||
links:
|
||||
- shlink_db
|
||||
- shlink_db_postgres
|
||||
- shlink_db_maria
|
||||
- shlink_redis
|
||||
|
||||
shlink_db:
|
||||
container_name: shlink_db
|
||||
build:
|
||||
context: .
|
||||
dockerfile: ./data/infra/db.Dockerfile
|
||||
image: mysql:5.7
|
||||
ports:
|
||||
- "3307:3306"
|
||||
volumes:
|
||||
@@ -39,3 +55,35 @@ services:
|
||||
environment:
|
||||
MYSQL_ROOT_PASSWORD: root
|
||||
MYSQL_DATABASE: shlink
|
||||
|
||||
shlink_db_postgres:
|
||||
container_name: shlink_db_postgres
|
||||
image: postgres:10.7-alpine
|
||||
ports:
|
||||
- "5433:5432"
|
||||
volumes:
|
||||
- ./:/home/shlink/www
|
||||
- ./data/infra/database_pg:/var/lib/postgresql/data
|
||||
environment:
|
||||
POSTGRES_PASSWORD: root
|
||||
POSTGRES_DB: shlink
|
||||
PGDATA: /var/lib/postgresql/data/pgdata
|
||||
|
||||
shlink_db_maria:
|
||||
container_name: shlink_db_maria
|
||||
image: mariadb:10.2
|
||||
ports:
|
||||
- "3308:3306"
|
||||
volumes:
|
||||
- ./:/home/shlink/www
|
||||
- ./data/infra/database_maria:/var/lib/mysql
|
||||
environment:
|
||||
MYSQL_ROOT_PASSWORD: root
|
||||
MYSQL_DATABASE: shlink
|
||||
MYSQL_INITDB_SKIP_TZINFO: 1
|
||||
|
||||
shlink_redis:
|
||||
container_name: shlink_redis
|
||||
image: redis:5.0-alpine
|
||||
ports:
|
||||
- "6380:6379"
|
||||
|
||||
216
docker/README.md
Normal file
216
docker/README.md
Normal file
@@ -0,0 +1,216 @@
|
||||
# Shlink Docker image
|
||||
|
||||
[](https://hub.docker.com/r/shlinkio/shlink/)
|
||||
[](https://hub.docker.com/r/shlinkio/shlink/)
|
||||
|
||||
This image provides an easy way to set up [shlink](https://shlink.io) on a container-based runtime.
|
||||
|
||||
It exposes a shlink instance served with [swoole](https://www.swoole.co.uk/), which persists data in a local [sqlite](https://www.sqlite.org/index.html) database.
|
||||
|
||||
## Usage
|
||||
|
||||
Shlink docker image exposes port `8080` in order to interact with its HTTP interface.
|
||||
|
||||
It also expects these two env vars to be provided, in order to properly generate short URLs at runtime.
|
||||
|
||||
* `SHORT_DOMAIN_HOST`: The custom short domain used for this shlink instance. For example **doma.in**.
|
||||
* `SHORT_DOMAIN_SCHEMA`: Either **http** or **https**.
|
||||
|
||||
So based on this, to run shlink on a local docker service, you should run a command like this:
|
||||
|
||||
```bash
|
||||
docker run --name shlink -p 8080:8080 -e SHORT_DOMAIN_HOST=doma.in -e SHORT_DOMAIN_SCHEMA=https shlinkio/shlink:stable
|
||||
```
|
||||
|
||||
### Interact with shlink's CLI on a running container.
|
||||
|
||||
Once the shlink container is running, you can interact with the CLI tool by running `shlink` with any of the supported commands.
|
||||
|
||||
For example, if the container is called `shlink_container`, you can generate a new API key with:
|
||||
|
||||
```bash
|
||||
docker exec -it shlink_container shlink api-key:generate
|
||||
```
|
||||
|
||||
Or you can list all tags with:
|
||||
|
||||
```bash
|
||||
docker exec -it shlink_container shlink tag:list
|
||||
```
|
||||
|
||||
Or process remaining visits with:
|
||||
|
||||
```bash
|
||||
docker exec -it shlink_container shlink visit:process
|
||||
```
|
||||
|
||||
All shlink commands will work the same way.
|
||||
|
||||
You can also list all available commands just by running this:
|
||||
|
||||
```bash
|
||||
docker exec -it shlink_container shlink
|
||||
```
|
||||
|
||||
## Use an external DB
|
||||
|
||||
The image comes with a working sqlite database, but in production you will probably want to usa a distributed database.
|
||||
|
||||
It is possible to use a set of env vars to make this shlink instance interact with an external MySQL, MariaDB or PostgreSQL database.
|
||||
|
||||
* `DB_DRIVER`: **[Mandatory]**. Use the value **mysql**, **maria** or **postgres** to prevent the sqlite database to be used.
|
||||
* `DB_NAME`: [Optional]. The database name to be used. Defaults to **shlink**.
|
||||
* `DB_USER`: **[Mandatory]**. The username credential for the database server.
|
||||
* `DB_PASSWORD`: **[Mandatory]**. The password credential for the database server.
|
||||
* `DB_HOST`: **[Mandatory]**. The host name of the server running the database engine.
|
||||
* `DB_PORT`: [Optional]. The port in which the database service is running.
|
||||
* Default value is based on the value provided for `DB_DRIVER`:
|
||||
* **mysql** or **maria** -> `3306`
|
||||
* **postgres** -> `5432`
|
||||
|
||||
> PostgreSQL is supported since v1.16.1 of this image. Do not try to use it with previous versions.
|
||||
|
||||
Taking this into account, you could run shlink on a local docker service like this:
|
||||
|
||||
```bash
|
||||
docker run --name shlink -p 8080:8080 -e SHORT_DOMAIN_HOST=doma.in -e SHORT_DOMAIN_SCHEMA=https -e DB_DRIVER=mysql -e DB_USER=root -e DB_PASSWORD=123abc -e DB_HOST=something.rds.amazonaws.com shlinkio/shlink:stable
|
||||
```
|
||||
|
||||
You could even link to a local database running on a different container:
|
||||
|
||||
```bash
|
||||
docker run --name shlink -p 8080:8080 [...] -e DB_HOST=some_mysql_container --link some_mysql_container shlinkio/shlink:stable
|
||||
```
|
||||
|
||||
> If you have considered using SQLite but sharing the database file with a volume, read [this issue](https://github.com/shlinkio/shlink-docker-image/issues/40) first.
|
||||
|
||||
## Supported env vars
|
||||
|
||||
A few env vars have been already used in previous examples, but this image supports others that can be used to customize its behavior.
|
||||
|
||||
This is the complete list of supported env vars:
|
||||
|
||||
* `SHORT_DOMAIN_HOST`: The custom short domain used for this shlink instance. For example **doma.in**.
|
||||
* `SHORT_DOMAIN_SCHEMA`: Either **http** or **https**.
|
||||
* `DB_DRIVER`: **sqlite** (which is the default value), **mysql**, **maria** or **postgres**.
|
||||
* `DB_NAME`: The database name to be used when using an external database driver. Defaults to **shlink**.
|
||||
* `DB_USER`: The username credential to be used when using an external database driver.
|
||||
* `DB_PASSWORD`: The password credential to be used when using an external database driver.
|
||||
* `DB_HOST`: The host name of the database server when using an external database driver.
|
||||
* `DB_PORT`: The port in which the database service is running when using an external database driver.
|
||||
* Default value is based on the value provided for `DB_DRIVER`:
|
||||
* **mysql** or **maria** -> `3306`
|
||||
* **postgres** -> `5432`
|
||||
* `DISABLE_TRACK_PARAM`: The name of a query param that can be used to visit short URLs avoiding the visit to be tracked. This feature won't be available if not value is provided.
|
||||
* `DELETE_SHORT_URL_THRESHOLD`: The amount of visits on short URLs which will not allow them to be deleted. Defaults to `15`.
|
||||
* `VALIDATE_URLS`: Boolean which tells if shlink should validate a status 20x is returned (after following redirects) when trying to shorten a URL. Defaults to `false`.
|
||||
* `INVALID_SHORT_URL_REDIRECT_TO`: If a URL is provided here, when a user tries to access an invalid short URL, he/she will be redirected to this value. If this env var is not provided, the user will see a generic `404 - not found` page.
|
||||
* `REGULAR_404_REDIRECT_TO`: If a URL is provided here, when a user tries to access a URL not matching any one supported by the router, he/she will be redirected to this value. If this env var is not provided, the user will see a generic `404 - not found` page.
|
||||
* `BASE_URL_REDIRECT_TO`: If a URL is provided here, when a user tries to access Shlink's base URL, he/she will be redirected to this value. If this env var is not provided, the user will see a generic `404 - not found` page.
|
||||
* `BASE_PATH`: The base path from which you plan to serve shlink, in case you don't want to serve it from the root of the domain. Defaults to `''`.
|
||||
* `WEB_WORKER_NUM`: The amount of concurrent http requests this shlink instance will be able to server. Defaults to 16.
|
||||
* `TASK_WORKER_NUM`: The amount of concurrent background tasks this shlink instance will be able to execute. Defaults to 16.
|
||||
* `VISITS_WEBHOOKS`: A comma-separated list of URLs that will receive a `POST` request when a short URL receives a visit.
|
||||
* `REDIS_SERVERS`: A comma-separated list of redis servers where Shlink locks are stored (locks are used to prevent some operations to be run more than once in parallel).
|
||||
|
||||
This is important when running more than one Shlink instance ([Multi instance considerations](#multi-instance-considerations)). If not provided, Shlink stores locks on every instance separately.
|
||||
|
||||
If more than one server is provided, Shlink will expect them to be configured as a [redis cluster](https://redis.io/topics/cluster-tutorial).
|
||||
|
||||
In the future, these redis servers could be used for other caching operations performed by shlink.
|
||||
|
||||
An example using all env vars could look like this:
|
||||
|
||||
```bash
|
||||
docker run \
|
||||
--name shlink \
|
||||
-p 8080:8080 \
|
||||
-e SHORT_DOMAIN_HOST=doma.in \
|
||||
-e SHORT_DOMAIN_SCHEMA=https \
|
||||
-e DB_DRIVER=mysql \
|
||||
-e DB_NAME=shlink \
|
||||
-e DB_USER=root \
|
||||
-e DB_PASSWORD=123abc \
|
||||
-e DB_HOST=something.rds.amazonaws.com \
|
||||
-e DB_PORT=3306 \
|
||||
-e DISABLE_TRACK_PARAM="no-track" \
|
||||
-e DELETE_SHORT_URL_THRESHOLD=30 \
|
||||
-e VALIDATE_URLS=true \
|
||||
-e "INVALID_SHORT_URL_REDIRECT_TO=https://my-landing-page.com" \
|
||||
-e "REGULAR_404_REDIRECT_TO=https://my-landing-page.com" \
|
||||
-e "BASE_URL_REDIRECT_TO=https://my-landing-page.com" \
|
||||
-e "REDIS_SERVERS=tcp://172.20.0.1:6379,tcp://172.20.0.2:6379" \
|
||||
-e "BASE_PATH=/my-campaign" \
|
||||
-e WEB_WORKER_NUM=64 \
|
||||
-e TASK_WORKER_NUM=32 \
|
||||
-e "VISITS_WEBHOOKS=http://my-api.com/api/v2.3/notify,https://third-party.io/foo" \
|
||||
shlinkio/shlink:stable
|
||||
```
|
||||
|
||||
## Provide config via volumes
|
||||
|
||||
Rather than providing custom configuration via env vars, it is also possible ot provide config files in json format.
|
||||
|
||||
Mounting a volume at `config/params` you will make shlink load all the files on it with the `.config.json` suffix.
|
||||
|
||||
The whole configuration should have this format, but it can be split into multiple files that will be merged:
|
||||
|
||||
```json
|
||||
{
|
||||
"disable_track_param": "my_param",
|
||||
"delete_short_url_threshold": 30,
|
||||
"short_domain_schema": "https",
|
||||
"short_domain_host": "doma.in",
|
||||
"validate_url": true,
|
||||
"invalid_short_url_redirect_to": "https://my-landing-page.com",
|
||||
"regular_404_redirect_to": "https://my-landing-page.com",
|
||||
"base_url_redirect_to": "https://my-landing-page.com",
|
||||
"base_path": "/my-campaign",
|
||||
"web_worker_num": 64,
|
||||
"task_worker_num": 32,
|
||||
"redis_servers": [
|
||||
"tcp://172.20.0.1:6379",
|
||||
"tcp://172.20.0.2:6379"
|
||||
],
|
||||
"visits_webhooks": [
|
||||
"http://my-api.com/api/v2.3/notify",
|
||||
"https://third-party.io/foo"
|
||||
],
|
||||
"db_config": {
|
||||
"driver": "pdo_mysql",
|
||||
"dbname": "shlink",
|
||||
"user": "root",
|
||||
"password": "123abc",
|
||||
"host": "something.rds.amazonaws.com",
|
||||
"port": "3306"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
> This is internally parsed to how shlink expects the config. If you are using a version previous to 1.17.0, this parser is not present and you need to provide a config structure like the one [documented previously](https://github.com/shlinkio/shlink-docker-image/tree/v1.16.3#provide-config-via-volumes).
|
||||
|
||||
Once created just run shlink with the volume:
|
||||
|
||||
```bash
|
||||
docker run --name shlink -p 8080:8080 -v ${PWD}/my/config/dir:/etc/shlink/config/params shlinkio/shlink:stable
|
||||
```
|
||||
|
||||
## Multi instance considerations
|
||||
|
||||
These are some considerations to take into account when running multiple instances of shlink.
|
||||
|
||||
* Some operations performed by Shlink should never be run more than once at the same time (like creating the database for the first time, or downloading the GeoLite2 database). For this reason, Shlink uses a locking system.
|
||||
|
||||
However, these locks are locally scoped to each Shlink instance by default.
|
||||
|
||||
You can (and should) make the locks to be shared by all Shlink instances by using a redis server/cluster. Just define the `REDIS_SERVERS` env var with the list of servers.
|
||||
|
||||
## Versions
|
||||
|
||||
Versioning on this docker image works as follows:
|
||||
|
||||
* `X.X.X`: when providing a specific version number, the image version will match the shlink version it contains. For example, installing `shlinkio/shlink:1.15.0`, you will get an image containing shlink v1.15.0.
|
||||
* `stable`: always holds the latest stable tag. For example, if latest shlink version is 2.0.0, installing `shlinkio/shlink:stable`, you will get an image containing shlink v2.0.0
|
||||
* `latest`: always holds the latest contents in master, and it's considered unstable and not suitable for production.
|
||||
|
||||
> **Important**: The docker image was introduced with shlink v1.15.0, so there are no official images previous to that versions.
|
||||
3
docker/config/php.ini
Normal file
3
docker/config/php.ini
Normal file
@@ -0,0 +1,3 @@
|
||||
log_errors_max_len=0
|
||||
zend.assertions=1
|
||||
assert.exception=1
|
||||
138
docker/config/shlink_in_docker.local.php
Normal file
138
docker/config/shlink_in_docker.local.php
Normal file
@@ -0,0 +1,138 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Shlinkio\Shlink;
|
||||
|
||||
use Monolog\Handler\StreamHandler;
|
||||
use Monolog\Logger;
|
||||
|
||||
use function explode;
|
||||
use function Functional\contains;
|
||||
use function Shlinkio\Shlink\Common\env;
|
||||
|
||||
$helper = new class {
|
||||
private const DB_DRIVERS_MAP = [
|
||||
'mysql' => 'pdo_mysql',
|
||||
'maria' => 'pdo_mysql',
|
||||
'postgres' => 'pdo_pgsql',
|
||||
];
|
||||
private const DB_PORTS_MAP = [
|
||||
'mysql' => '3306',
|
||||
'maria' => '3306',
|
||||
'postgres' => '5432',
|
||||
];
|
||||
|
||||
public function getDbConfig(): array
|
||||
{
|
||||
$driver = env('DB_DRIVER');
|
||||
if ($driver === null || $driver === 'sqlite') {
|
||||
return [
|
||||
'driver' => 'pdo_sqlite',
|
||||
'path' => 'data/database.sqlite',
|
||||
];
|
||||
}
|
||||
|
||||
$driverOptions = ! contains(['maria', 'mysql'], $driver) ? [] : [
|
||||
// 1002 -> PDO::MYSQL_ATTR_INIT_COMMAND
|
||||
1002 => 'SET NAMES utf8',
|
||||
];
|
||||
return [
|
||||
'driver' => self::DB_DRIVERS_MAP[$driver],
|
||||
'dbname' => env('DB_NAME', 'shlink'),
|
||||
'user' => env('DB_USER'),
|
||||
'password' => env('DB_PASSWORD'),
|
||||
'host' => env('DB_HOST'),
|
||||
'port' => env('DB_PORT', self::DB_PORTS_MAP[$driver]),
|
||||
'driverOptions' => $driverOptions,
|
||||
];
|
||||
}
|
||||
|
||||
public function getNotFoundRedirectsConfig(): array
|
||||
{
|
||||
return [
|
||||
'invalid_short_url' => env('INVALID_SHORT_URL_REDIRECT_TO'),
|
||||
'regular_404' => env('REGULAR_404_REDIRECT_TO'),
|
||||
'base_url' => env('BASE_URL_REDIRECT_TO'),
|
||||
];
|
||||
}
|
||||
|
||||
public function getVisitsWebhooks(): array
|
||||
{
|
||||
$webhooks = env('VISITS_WEBHOOKS');
|
||||
return $webhooks === null ? [] : explode(',', $webhooks);
|
||||
}
|
||||
|
||||
public function getRedisConfig(): ?array
|
||||
{
|
||||
$redisServers = env('REDIS_SERVERS');
|
||||
return $redisServers === null ? null : ['servers' => $redisServers];
|
||||
}
|
||||
};
|
||||
|
||||
return [
|
||||
|
||||
'config_cache_enabled' => false,
|
||||
|
||||
'app_options' => [
|
||||
'disable_track_param' => env('DISABLE_TRACK_PARAM'),
|
||||
],
|
||||
|
||||
'delete_short_urls' => [
|
||||
'check_visits_threshold' => true,
|
||||
'visits_threshold' => (int) env('DELETE_SHORT_URL_THRESHOLD', 15),
|
||||
],
|
||||
|
||||
'entity_manager' => [
|
||||
'connection' => $helper->getDbConfig(),
|
||||
],
|
||||
|
||||
'url_shortener' => [
|
||||
'domain' => [
|
||||
'schema' => env('SHORT_DOMAIN_SCHEMA', 'http'),
|
||||
'hostname' => env('SHORT_DOMAIN_HOST', ''),
|
||||
],
|
||||
'validate_url' => (bool) env('VALIDATE_URLS', false),
|
||||
'visits_webhooks' => $helper->getVisitsWebhooks(),
|
||||
],
|
||||
|
||||
'not_found_redirects' => $helper->getNotFoundRedirectsConfig(),
|
||||
|
||||
'logger' => [
|
||||
'Shlink' => [
|
||||
'handlers' => [
|
||||
'shlink_handler' => [
|
||||
'name' => StreamHandler::class,
|
||||
'params' => [
|
||||
'level' => Logger::INFO,
|
||||
'stream' => 'php://stdout',
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
|
||||
'dependencies' => [
|
||||
'aliases' => env('REDIS_SERVERS') === null ? [] : [
|
||||
'lock_store' => 'redis_lock_store',
|
||||
],
|
||||
],
|
||||
|
||||
'cache' => [
|
||||
'redis' => $helper->getRedisConfig(),
|
||||
],
|
||||
|
||||
'router' => [
|
||||
'base_path' => env('BASE_PATH', ''),
|
||||
],
|
||||
|
||||
'mezzio-swoole' => [
|
||||
'swoole-http-server' => [
|
||||
'options' => [
|
||||
'worker_num' => (int) env('WEB_WORKER_NUM', 16),
|
||||
'task_worker_num' => (int) env('TASK_WORKER_NUM', 16),
|
||||
],
|
||||
],
|
||||
],
|
||||
|
||||
];
|
||||
17
docker/docker-entrypoint.sh
Normal file
17
docker/docker-entrypoint.sh
Normal file
@@ -0,0 +1,17 @@
|
||||
#!/usr/bin/env sh
|
||||
set -e
|
||||
|
||||
cd /etc/shlink
|
||||
|
||||
echo "Creating fresh database if needed..."
|
||||
php bin/cli db:create -n -q
|
||||
|
||||
echo "Updating database..."
|
||||
php bin/cli db:migrate -n -q
|
||||
|
||||
echo "Generating proxies..."
|
||||
php vendor/doctrine/orm/bin/doctrine.php orm:generate-proxies -n -q
|
||||
|
||||
# When restarting the container, swoole 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/mezzio/mezzio-swoole/bin/mezzio-swoole start; do sleep 1 ; done
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user