Compare commits
2027 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4eab3b6935 | ||
|
|
ea420936fa | ||
|
|
979f002f0c | ||
|
|
54fe38a92b | ||
|
|
1e2618d3b8 | ||
|
|
6b0fc80f1e | ||
|
|
d6d0539fa7 | ||
|
|
81742cbbaf | ||
|
|
300cd50310 | ||
|
|
d227a5db78 | ||
|
|
4f2dd9d240 | ||
|
|
9f750f7f6b | ||
|
|
e5a1563f39 | ||
|
|
bc3634e181 | ||
|
|
3343fba861 | ||
|
|
eff4712f69 | ||
|
|
2788264265 | ||
|
|
6fe52143a8 | ||
|
|
058b38528f | ||
|
|
d1619b3723 | ||
|
|
0bdf5f206e | ||
|
|
34aca8ff3f | ||
|
|
b4db7fdf11 | ||
|
|
d10d7fd96d | ||
|
|
9c0c2fc3f9 | ||
|
|
913b3c5fc1 | ||
|
|
6dd3fd275a | ||
|
|
2b05016586 | ||
|
|
52cc943185 | ||
|
|
edd6f7e807 | ||
|
|
d13e6fb0cc | ||
|
|
2208e9b562 | ||
|
|
dadcdb22f5 | ||
|
|
4246b85b43 | ||
|
|
4d259e50f3 | ||
|
|
e5fff0bc08 | ||
|
|
7a3aab2482 | ||
|
|
455ac9a275 | ||
|
|
b70cebb3f2 | ||
|
|
e3b24eea78 | ||
|
|
a96a48ed19 | ||
|
|
6114763e39 | ||
|
|
575f86cc34 | ||
|
|
93cf8b1258 | ||
|
|
76b7523d4a | ||
|
|
c2c1037c01 | ||
|
|
d3feee301e | ||
|
|
531a61a57b | ||
|
|
af252846f7 | ||
|
|
a94fa74a12 | ||
|
|
3e24c3b61b | ||
|
|
581e809fd2 | ||
|
|
e2ceb52c75 | ||
|
|
f975a71230 | ||
|
|
03df998b86 | ||
|
|
b758781407 | ||
|
|
8dec3a5277 | ||
|
|
191d6af4bd | ||
|
|
543b0a5a80 | ||
|
|
b86a644dcd | ||
|
|
e7a3ec3ee2 | ||
|
|
1f23be963a | ||
|
|
726050e777 | ||
|
|
06af621e7c | ||
|
|
30e67151fe | ||
|
|
0ba34cc8eb | ||
|
|
7a5ae2f19e | ||
|
|
de27e453c3 | ||
|
|
b913a14105 | ||
|
|
2bf0bc8334 | ||
|
|
b5529d3a8c | ||
|
|
b5a091861d | ||
|
|
931f980e16 | ||
|
|
5f091eada1 | ||
|
|
bd1a16a8f8 | ||
|
|
6f8669cec7 | ||
|
|
7d52ae3e7d | ||
|
|
3e194246ab | ||
|
|
cce28116a8 | ||
|
|
90125d51f2 | ||
|
|
dc670b8693 | ||
|
|
3f8a33e77c | ||
|
|
3763a1541a | ||
|
|
4bd61d1f80 | ||
|
|
713c6fc7d9 | ||
|
|
b5efcda25e | ||
|
|
12d7e76731 | ||
|
|
249c948d11 | ||
|
|
b8d0b1bd2a | ||
|
|
77b5c93652 | ||
|
|
137be6962c | ||
|
|
357406d2a4 | ||
|
|
4ba98ede77 | ||
|
|
89f250bfb7 | ||
|
|
425c70bfd8 | ||
|
|
74ffd63828 | ||
|
|
f8a2f81ce4 | ||
|
|
e8f05783a9 | ||
|
|
ae0f2282de | ||
|
|
cc0091f753 | ||
|
|
1e84db4a47 | ||
|
|
8f59da1ce7 | ||
|
|
e46751844c | ||
|
|
d131ac5d03 | ||
|
|
878c82e8f0 | ||
|
|
69fed72a81 | ||
|
|
bda6388100 | ||
|
|
1d6a9ec563 | ||
|
|
56517b9840 | ||
|
|
03a0945a29 | ||
|
|
023a0ca824 | ||
|
|
f6a0910c40 | ||
|
|
869708469d | ||
|
|
f51c8e7643 | ||
|
|
256d886930 | ||
|
|
e74a4b5320 | ||
|
|
751ffdab61 | ||
|
|
fec1fc3c76 | ||
|
|
35d27ffb04 | ||
|
|
3ba6b58b9e | ||
|
|
8ce81771b2 | ||
|
|
17c1ff1c4c | ||
|
|
9a2f83b2b5 | ||
|
|
dfd9a3efb8 | ||
|
|
5e630368e6 | ||
|
|
5327160774 | ||
|
|
b092c86658 | ||
|
|
2988b24fac | ||
|
|
b49b0ebf16 | ||
|
|
45f972e351 | ||
|
|
a824349ad2 | ||
|
|
2fd933fc32 | ||
|
|
b48cf19acf | ||
|
|
669a11186b | ||
|
|
355cdd2550 | ||
|
|
34a08e461f | ||
|
|
b747e63d51 | ||
|
|
557cd4f7e2 | ||
|
|
b0189c6457 | ||
|
|
8da630e149 | ||
|
|
d65eafd37f | ||
|
|
4895cbb9dc | ||
|
|
1467c8e416 | ||
|
|
e12cd68010 | ||
|
|
e997d11c2c | ||
|
|
4947e0490a | ||
|
|
c68a7ee22a | ||
|
|
c4d99606cb | ||
|
|
5e3e70454d | ||
|
|
860808a654 | ||
|
|
476c98d4f3 | ||
|
|
35a3224f70 | ||
|
|
0c001a6d81 | ||
|
|
7a9ab1c803 | ||
|
|
eb6ba5f15d | ||
|
|
52cde329d7 | ||
|
|
aae2cd6866 | ||
|
|
becdf04acf | ||
|
|
e3c853361d | ||
|
|
385fc839b5 | ||
|
|
5d72d36c20 | ||
|
|
1fcd2fffd0 | ||
|
|
7cdc84b45d | ||
|
|
3f317d2559 | ||
|
|
788df59ae1 | ||
|
|
d57188addf | ||
|
|
778f7cedbb | ||
|
|
f71218998f | ||
|
|
d1d6a6b373 | ||
|
|
41de28c15f | ||
|
|
c1bbeeb6ac | ||
|
|
9f3d7df5cd | ||
|
|
a7d6637a81 | ||
|
|
14fa2e32ea | ||
|
|
c08f0dad8b | ||
|
|
43853ef14f | ||
|
|
b831c6c3bd | ||
|
|
1f51d60ed2 | ||
|
|
600a3e2045 | ||
|
|
5458657c8b | ||
|
|
603ee9d6bf | ||
|
|
3405f524df | ||
|
|
1fbbef41a7 | ||
|
|
eef04075a5 | ||
|
|
7880bb4abe | ||
|
|
ac0c6aa729 | ||
|
|
691e6c1afb | ||
|
|
09559c78af | ||
|
|
3d6ea5cf7c | ||
|
|
0a7a606541 | ||
|
|
61cc6c8d26 | ||
|
|
5e0db07ef3 | ||
|
|
d188d67c5a | ||
|
|
bd034c11b6 | ||
|
|
0ec867b185 | ||
|
|
c29b077e93 | ||
|
|
d8a42b4c3a | ||
|
|
7879476739 | ||
|
|
fd40e2b7bc | ||
|
|
aefe5e0848 | ||
|
|
fd7bfd845e | ||
|
|
01ca369388 | ||
|
|
15ef29ecea | ||
|
|
b19162ce91 | ||
|
|
7c31b210bd | ||
|
|
a63c214d8d | ||
|
|
c9ada8f41d | ||
|
|
06fac716d1 | ||
|
|
c462bc30e1 | ||
|
|
ad00e54df8 | ||
|
|
ca4543b227 | ||
|
|
ddca307001 | ||
|
|
dd651eb324 | ||
|
|
6ea7347ae1 | ||
|
|
b4810aa4cd | ||
|
|
8a99076a21 | ||
|
|
432bbcb4ac | ||
|
|
0f006a737f | ||
|
|
73d9a535bc | ||
|
|
da5eddaa6a | ||
|
|
d2362d4ddd | ||
|
|
18e41b3064 | ||
|
|
148dff996c | ||
|
|
20d12c4d7c | ||
|
|
f25389a638 | ||
|
|
a10e9646e6 | ||
|
|
8a697bb7d2 | ||
|
|
834eca8461 | ||
|
|
b5347b1801 | ||
|
|
939d9d4463 | ||
|
|
56aee7e96e | ||
|
|
49ab0d425d | ||
|
|
9227b00d1a | ||
|
|
a736bb3b21 | ||
|
|
ecbc0f64d6 | ||
|
|
a522a74199 | ||
|
|
bee4094b84 | ||
|
|
e3e909a023 | ||
|
|
d91d341bbf | ||
|
|
a3d85a7454 | ||
|
|
a5a5b0c3a4 | ||
|
|
50e7204a4f | ||
|
|
0c60b8710e | ||
|
|
bb92930116 | ||
|
|
c1483d4c99 | ||
|
|
aeedd45bb6 | ||
|
|
861cb6efc6 | ||
|
|
a8f4b47803 | ||
|
|
437d769527 | ||
|
|
8dfed49dcf | ||
|
|
794a0a1b14 | ||
|
|
86bd650213 | ||
|
|
c4cb462a5e | ||
|
|
3aecc4fd5b | ||
|
|
7c550af923 | ||
|
|
6588a0eede | ||
|
|
7b219158e1 | ||
|
|
1c7ab0e1ea | ||
|
|
148aa30915 | ||
|
|
781d9a0355 | ||
|
|
6a5c6817d0 | ||
|
|
3d3a292284 | ||
|
|
5bf9547e55 | ||
|
|
45078bb632 | ||
|
|
87060410b5 | ||
|
|
0bc898c999 | ||
|
|
94e188c6c0 | ||
|
|
8a74b3d736 | ||
|
|
897d93b9ab | ||
|
|
f7b0f692d9 | ||
|
|
80c0b84785 | ||
|
|
dc685084d1 | ||
|
|
763c767286 | ||
|
|
01302cf25e | ||
|
|
6dee9260c1 | ||
|
|
8f1847e38b | ||
|
|
176e0b4961 | ||
|
|
de9be5fcf4 | ||
|
|
b46db36a1f | ||
|
|
8dd9287eaf | ||
|
|
ab784515c0 | ||
|
|
337c8c1ee2 | ||
|
|
b46e637a64 | ||
|
|
b2d8cdb43f | ||
|
|
23ae01d34d | ||
|
|
306c496489 | ||
|
|
13e57eb2ae | ||
|
|
c4ecdc9175 | ||
|
|
2e99ee43ce | ||
|
|
4c7c457839 | ||
|
|
9b84df6774 | ||
|
|
9d010f8996 | ||
|
|
21b7ca1a0a | ||
|
|
26578ed55d | ||
|
|
1b6369264b | ||
|
|
7c0033397b | ||
|
|
49a75a08aa | ||
|
|
4dad18e45f | ||
|
|
0016333972 | ||
|
|
b65291bb01 | ||
|
|
dac8a08fd2 | ||
|
|
c18808503c | ||
|
|
06563feffc | ||
|
|
468a529f94 | ||
|
|
0c568a327f | ||
|
|
2e9db77b00 | ||
|
|
c85917e378 | ||
|
|
d2d9810afd | ||
|
|
db13d43f99 | ||
|
|
1acc64c074 | ||
|
|
32c4cd40d3 | ||
|
|
abf02ddbac | ||
|
|
552cfcf805 | ||
|
|
4198ad73de | ||
|
|
b6a49e3b11 | ||
|
|
e716624a3f | ||
|
|
6a8825ecb7 | ||
|
|
e171866226 | ||
|
|
c98616234f | ||
|
|
4fa9f8967b | ||
|
|
5ea42b6c15 | ||
|
|
d0bc6610b1 | ||
|
|
995769de27 | ||
|
|
aeb4f8ed9a | ||
|
|
44e1bef29b | ||
|
|
b472b44c5b | ||
|
|
227b762740 | ||
|
|
c0765eeb92 | ||
|
|
da1b727788 | ||
|
|
2875a857a5 | ||
|
|
81952acb84 | ||
|
|
33fd901552 | ||
|
|
394ae44401 | ||
|
|
5e3aaca8d8 | ||
|
|
1647ee023a | ||
|
|
a95451d12c | ||
|
|
c6c1caa420 | ||
|
|
14b2f3a9f6 | ||
|
|
022fe6af3a | ||
|
|
ed827c279a | ||
|
|
aede99b709 | ||
|
|
281fa9ebdd | ||
|
|
b7c2a423c8 | ||
|
|
a39e841af2 | ||
|
|
00384abe9c | ||
|
|
4b1b107f44 | ||
|
|
4175bd3a5e | ||
|
|
15aeb5c922 | ||
|
|
168f8d85ce | ||
|
|
5fc42d3850 | ||
|
|
64d7d16ea2 | ||
|
|
fa9de43123 | ||
|
|
87618181ab | ||
|
|
03cf9dbe79 | ||
|
|
4afc621d10 | ||
|
|
4f1d7fbd5f | ||
|
|
c5b7116992 | ||
|
|
b08610196c | ||
|
|
3ad070ea18 | ||
|
|
94bc49a0b3 | ||
|
|
9269593c46 | ||
|
|
baa19b5c4c | ||
|
|
4fc48d3259 | ||
|
|
03c0f4649f | ||
|
|
0280fc0f59 | ||
|
|
d0b48c90a6 | ||
|
|
e90ec0b27c | ||
|
|
fc66171a09 | ||
|
|
769f7f45ee | ||
|
|
ca89e8e4f9 | ||
|
|
d7dda228b6 | ||
|
|
28136a656f | ||
|
|
4502ca5543 | ||
|
|
b4decf9124 | ||
|
|
c5d12a49f4 | ||
|
|
dcab31b30c | ||
|
|
c917d643d2 | ||
|
|
8c2ca8656b | ||
|
|
9a22d51996 | ||
|
|
d95352f467 | ||
|
|
8dbfffc383 | ||
|
|
1606ce357e | ||
|
|
096d27f107 | ||
|
|
8cc03a973f | ||
|
|
f4c4f408e2 | ||
|
|
a65c7171c1 | ||
|
|
d14c4fa606 | ||
|
|
7e4c915b0b | ||
|
|
a5a15963b8 | ||
|
|
88515fc571 | ||
|
|
b20e8ae528 | ||
|
|
dcbbe9a9a6 | ||
|
|
61cabaa356 | ||
|
|
30f0df5a10 | ||
|
|
37562c06ec | ||
|
|
478fc2c274 | ||
|
|
5aa113ec16 | ||
|
|
a390e1bdf9 | ||
|
|
44e9a336aa | ||
|
|
8ac347f52d | ||
|
|
a325af3cda | ||
|
|
49ebfdbbaf | ||
|
|
742c7b9dd9 | ||
|
|
558d2a09f7 | ||
|
|
584362b426 | ||
|
|
0bcdcadee4 | ||
|
|
8ae6835a10 | ||
|
|
8addfbdb3d | ||
|
|
db59d55326 | ||
|
|
30ee16a9f6 | ||
|
|
8c981811b3 | ||
|
|
975807c143 | ||
|
|
5aaf9111fd | ||
|
|
a10898f662 | ||
|
|
a853078862 | ||
|
|
b1c2b01998 | ||
|
|
e0f5705c78 | ||
|
|
db1b58e32e | ||
|
|
a777edb881 | ||
|
|
81434a5649 | ||
|
|
62642d891f | ||
|
|
68126aad88 | ||
|
|
7e8d85ca73 | ||
|
|
b2205a6258 | ||
|
|
158702bdfc | ||
|
|
7220bf1ff4 | ||
|
|
03cb2d76d7 | ||
|
|
d6350131e6 | ||
|
|
27db3532d9 | ||
|
|
03f826ffeb | ||
|
|
049a588287 | ||
|
|
3aeafec90f | ||
|
|
32ec9b98d4 | ||
|
|
6646906d58 | ||
|
|
31c3290aec | ||
|
|
31c4192c24 | ||
|
|
d4f8fdbd83 | ||
|
|
877d951a1b | ||
|
|
3a6ee5e3c0 | ||
|
|
ca980b96fa | ||
|
|
a5a0f36166 | ||
|
|
238cefde73 | ||
|
|
b31949b468 | ||
|
|
645abea72a | ||
|
|
7a9209af03 | ||
|
|
c83abc6e9a | ||
|
|
9b891c83fa | ||
|
|
dc8c749212 | ||
|
|
88ad2e7fc2 | ||
|
|
44fb07840e | ||
|
|
e786f9d21f | ||
|
|
9134d07969 | ||
|
|
913264b0db | ||
|
|
2bbc3e6426 | ||
|
|
4c74aec703 | ||
|
|
83ca5b23f5 | ||
|
|
61a679b7cb | ||
|
|
fdbf094893 | ||
|
|
fad0dfbe43 | ||
|
|
7ae8e52da8 | ||
|
|
6a1f25ecf6 | ||
|
|
10ba6cb51b | ||
|
|
5943d2aaa6 | ||
|
|
c434b54969 | ||
|
|
3840d4a1be | ||
|
|
2b77b2411c | ||
|
|
714474d406 | ||
|
|
a1a1bc44ae | ||
|
|
18d9ed7b0d | ||
|
|
9094c1d23f | ||
|
|
dcac312d86 | ||
|
|
2e438f9814 | ||
|
|
ae014e2a14 | ||
|
|
5f5b66a21e | ||
|
|
723e8696af | ||
|
|
32c3c9955c | ||
|
|
847a23ddeb | ||
|
|
08e3e01526 | ||
|
|
82b26add88 | ||
|
|
e3ab34eae1 | ||
|
|
65d9a4125f | ||
|
|
d05344eea7 | ||
|
|
47a2d981c8 | ||
|
|
2229573101 | ||
|
|
a8059c6f1f | ||
|
|
51c1963f4b | ||
|
|
8c10daeddd | ||
|
|
d168e98607 | ||
|
|
a45d990eb7 | ||
|
|
31ae43708a | ||
|
|
cefe327251 | ||
|
|
22280e9d89 | ||
|
|
c73ece41f0 | ||
|
|
7825f7666f | ||
|
|
fc6901ee65 | ||
|
|
1794a07204 | ||
|
|
7215d188a2 | ||
|
|
94890da48f | ||
|
|
078c5c8889 | ||
|
|
23a0cb1245 | ||
|
|
b4015bd8d1 | ||
|
|
3cffaa6e32 | ||
|
|
e68faa63c5 | ||
|
|
e0706a6a89 | ||
|
|
adcb405643 | ||
|
|
63df7db2f5 | ||
|
|
04eb1b1b0e | ||
|
|
40bf57882a | ||
|
|
9a798c20c0 | ||
|
|
45a9783e04 | ||
|
|
ac95ff0166 | ||
|
|
a0df3eeb93 | ||
|
|
e8c5ac8320 | ||
|
|
453d245924 | ||
|
|
9e1a803b8d | ||
|
|
1ba2241203 | ||
|
|
b0aff16779 | ||
|
|
ec58e596e0 | ||
|
|
3ba672bd3f | ||
|
|
aa7b1bbf1b | ||
|
|
7f2483659a | ||
|
|
857cd87607 | ||
|
|
4758fa18e2 | ||
|
|
34ec6fabb3 | ||
|
|
00524c6157 | ||
|
|
e66e2ad858 | ||
|
|
69344a01ac | ||
|
|
a90ef56eb8 | ||
|
|
947c7d0b3f | ||
|
|
8d66edf75b | ||
|
|
d483a04543 | ||
|
|
c6790b1617 | ||
|
|
43649ae188 | ||
|
|
a865824b0c | ||
|
|
f69bb83256 | ||
|
|
7efe9ba3ca | ||
|
|
7a7956e25f | ||
|
|
1d33275eb1 | ||
|
|
d4149e14b5 | ||
|
|
069301492d | ||
|
|
b457996c52 | ||
|
|
f5d4512d5d | ||
|
|
ad5f43c74d | ||
|
|
3510e0fbb5 | ||
|
|
dd62267422 | ||
|
|
ff5670fdbc | ||
|
|
cdb474890b | ||
|
|
5e235ac76f | ||
|
|
115e5eec50 | ||
|
|
ab15812c9f | ||
|
|
864b57760b | ||
|
|
7df60fd821 | ||
|
|
7d6b43537d | ||
|
|
329f68879c | ||
|
|
9ddbbd45fa | ||
|
|
ff86233381 | ||
|
|
37f6e1c51b | ||
|
|
592647eab1 | ||
|
|
9e7cb0e70a | ||
|
|
91f244ef08 | ||
|
|
3ca4faf066 | ||
|
|
3ea4e3a4f1 | ||
|
|
4d2c2d0c57 | ||
|
|
68464ef1eb | ||
|
|
8877c5a4e4 | ||
|
|
1b4989f418 | ||
|
|
f24e363ce3 | ||
|
|
3b402f6ee9 | ||
|
|
370c06a77b | ||
|
|
d760808928 | ||
|
|
c71569f9d9 | ||
|
|
ec378963c0 | ||
|
|
03002ed291 | ||
|
|
77db855785 | ||
|
|
7e23ad3d31 | ||
|
|
e6bee4ff40 | ||
|
|
67c59b2a6f | ||
|
|
818b5f6c97 | ||
|
|
c5d99d22e5 | ||
|
|
0fd0e9b083 | ||
|
|
6838f53733 | ||
|
|
4aa8fc3980 | ||
|
|
24be2a7c3a | ||
|
|
1f979ec2d6 | ||
|
|
5f6d808615 | ||
|
|
c3aecddb51 | ||
|
|
82cfce431d | ||
|
|
bba730582c | ||
|
|
08a6176bcd | ||
|
|
719e345d06 | ||
|
|
0cbdb2fb99 | ||
|
|
f6526de9db | ||
|
|
23de347f4a | ||
|
|
a5225716fe | ||
|
|
5ed8d9ab35 | ||
|
|
054233d427 | ||
|
|
a4bde0562c | ||
|
|
1f4170f98c | ||
|
|
26bd45a9ca | ||
|
|
41e67db8fc | ||
|
|
c215ff486a | ||
|
|
379920445f | ||
|
|
f1bfff8bd9 | ||
|
|
ae15133420 | ||
|
|
544406611c | ||
|
|
be2c90a443 | ||
|
|
122bf47f71 | ||
|
|
0c11d8b9c1 | ||
|
|
d9afee4ffb | ||
|
|
849c23e3aa | ||
|
|
b6c9026ba1 | ||
|
|
4a8854655c | ||
|
|
bb4916f6b5 | ||
|
|
34183ffa4a | ||
|
|
a8258ff2cc | ||
|
|
f9c6e75591 | ||
|
|
66ecddd5aa | ||
|
|
7d5781b16e | ||
|
|
b6ff8bd659 | ||
|
|
edebd158ee | ||
|
|
4b0383f911 | ||
|
|
cbe61578bc | ||
|
|
0605dbdb75 | ||
|
|
81ea262999 | ||
|
|
2cf227210a | ||
|
|
bab2d368f9 | ||
|
|
b1119d1eea | ||
|
|
fb8de00b4b | ||
|
|
e8b522dc76 | ||
|
|
5141f5445c | ||
|
|
2debbc427a | ||
|
|
5edca16c54 | ||
|
|
10495c769c | ||
|
|
2b2e1843f6 | ||
|
|
f900e93cad | ||
|
|
5a30df7dce | ||
|
|
cc3c88f18c | ||
|
|
e18b2a4b8d | ||
|
|
9b4179e29d | ||
|
|
dfc2ca183b | ||
|
|
746003d19b | ||
|
|
fa10aebeab | ||
|
|
7039e11f3f | ||
|
|
bd0dece878 | ||
|
|
950858f217 | ||
|
|
ff70c9924c | ||
|
|
a3f2472cbb | ||
|
|
919c5907ed | ||
|
|
0203ac11e7 | ||
|
|
fc5a67317e | ||
|
|
65fb8c9c1f | ||
|
|
071934b1a0 | ||
|
|
85dcd5d600 | ||
|
|
bc3b548362 | ||
|
|
e3f428287d | ||
|
|
3b58a0c028 | ||
|
|
1517a86c44 | ||
|
|
992cd2fcff | ||
|
|
92251945d6 | ||
|
|
082bb42aad | ||
|
|
b887c60977 | ||
|
|
de0bb31d88 | ||
|
|
dc238e7584 | ||
|
|
68bf055c0f | ||
|
|
83709fc7b9 | ||
|
|
f33b356eef | ||
|
|
69ae237331 | ||
|
|
d4bcf23d93 | ||
|
|
3964f66530 | ||
|
|
f177799f4a | ||
|
|
21dd0c474b | ||
|
|
f8ed6d166d | ||
|
|
c4fabe6057 | ||
|
|
e87835c01e | ||
|
|
64fabfeaea | ||
|
|
cd3f22a7db | ||
|
|
956e021ccb | ||
|
|
5230a28738 | ||
|
|
21ec867367 | ||
|
|
99b34af8df | ||
|
|
c269354423 | ||
|
|
e5384a3c40 | ||
|
|
3592fb532c | ||
|
|
9f2b07bbaa | ||
|
|
f64cca5457 | ||
|
|
a6ec7be0d7 | ||
|
|
f530fb4ceb | ||
|
|
e4fcfdbdcd | ||
|
|
87c0559407 | ||
|
|
281bf1ec87 | ||
|
|
3ee249daa5 | ||
|
|
5b250b5e31 | ||
|
|
fd1fcaa8ad | ||
|
|
b630662db6 | ||
|
|
e29790072c | ||
|
|
2521e94057 | ||
|
|
20fa0c78fe | ||
|
|
daa8513402 | ||
|
|
88626e3d14 | ||
|
|
257c4cc17d | ||
|
|
c9e57e64ef | ||
|
|
cf8383361e | ||
|
|
cc23cba520 | ||
|
|
c19926b58e | ||
|
|
3b1b1421f9 | ||
|
|
dbfa41a5b2 | ||
|
|
66bd5642a0 | ||
|
|
5212272f4e | ||
|
|
8a810555c2 | ||
|
|
340079ceed | ||
|
|
788eebc87d | ||
|
|
9d26fd2974 | ||
|
|
5549aaacbb | ||
|
|
aef74b80c2 | ||
|
|
b2908d2b1f | ||
|
|
fa39302474 | ||
|
|
b568c300fc | ||
|
|
c7c5875239 | ||
|
|
9824f2381d | ||
|
|
b9f59210b0 | ||
|
|
d53a2ae9b1 | ||
|
|
4166831b6a | ||
|
|
d20850e1d7 | ||
|
|
652871a62f | ||
|
|
afb9af473f | ||
|
|
16b70c8ab2 | ||
|
|
04e826cecf | ||
|
|
4fc4e9cece | ||
|
|
3ba0f38f21 | ||
|
|
202a69bdf5 | ||
|
|
d4bc9dd62a | ||
|
|
bad3703ca6 | ||
|
|
859e0a612e | ||
|
|
b5b1f2505a | ||
|
|
f0cffdef92 | ||
|
|
25ce9b4b18 | ||
|
|
1bc7db2b76 | ||
|
|
7529cf3793 | ||
|
|
dc3098849e | ||
|
|
4ab0db0dd9 | ||
|
|
281c148e18 | ||
|
|
e350412f0d | ||
|
|
66b3119b53 | ||
|
|
0354746698 | ||
|
|
9db471b21e | ||
|
|
8d2dff0aec | ||
|
|
89355d13e7 | ||
|
|
679bf4dd3a | ||
|
|
68667c530b | ||
|
|
bbae3c45c4 | ||
|
|
65b5a86a06 | ||
|
|
4be80bac5a | ||
|
|
707fb0673d | ||
|
|
61475df03a | ||
|
|
dd01489e8d | ||
|
|
23974e4224 | ||
|
|
0b86265ef4 | ||
|
|
17b44d5304 | ||
|
|
2c2e6313fa | ||
|
|
08e85388bc | ||
|
|
feac30da71 | ||
|
|
883be681be | ||
|
|
90f189b0e0 | ||
|
|
6443359321 | ||
|
|
4f8012f1cf | ||
|
|
35d3405fca | ||
|
|
f7c2faec14 | ||
|
|
a9e6db509e | ||
|
|
981771598b | ||
|
|
8b5c4f5966 | ||
|
|
8ccbc60ab7 | ||
|
|
f0cc6c9711 | ||
|
|
861644da55 | ||
|
|
df9bb940b8 | ||
|
|
4066cfeec9 | ||
|
|
a5d41e3f00 | ||
|
|
24df9cc742 | ||
|
|
9b81b983fb | ||
|
|
831f192122 | ||
|
|
76e3b760e0 | ||
|
|
f382d3f406 | ||
|
|
535e910b86 | ||
|
|
a6e05af271 | ||
|
|
d064c12d71 | ||
|
|
be9602e2c3 | ||
|
|
4230568ef2 | ||
|
|
16af634bd2 | ||
|
|
e21137a96d | ||
|
|
14447f1d23 | ||
|
|
d4d6b81901 | ||
|
|
02780227fe | ||
|
|
a8f35ae82e | ||
|
|
fbe2c0f297 | ||
|
|
40979533c0 | ||
|
|
1b507fefde | ||
|
|
9607eeef89 | ||
|
|
589ecb7349 | ||
|
|
ebee5ee346 | ||
|
|
726a86a064 | ||
|
|
81a2916b66 | ||
|
|
3546ae81f7 | ||
|
|
d18ebf8911 | ||
|
|
e7d3380223 | ||
|
|
580a625082 | ||
|
|
7e43f67629 | ||
|
|
344e1f37fe | ||
|
|
af30986350 | ||
|
|
ba0605fab4 | ||
|
|
0751549214 | ||
|
|
415de7a29a | ||
|
|
f23592c3c8 | ||
|
|
f495c1af33 | ||
|
|
13b434959e | ||
|
|
4b16d9f566 | ||
|
|
e7138b2dc6 | ||
|
|
e410ca0999 | ||
|
|
5c2e99cba1 | ||
|
|
5a7e8a0079 | ||
|
|
734ff22499 | ||
|
|
b9fb398d3b | ||
|
|
0e0ea3fd26 | ||
|
|
f9c3ac3925 | ||
|
|
b399523124 | ||
|
|
ae7adb04f6 | ||
|
|
19021c39a5 | ||
|
|
4fcead4e12 | ||
|
|
644adf3ab7 | ||
|
|
bfb8cedec9 | ||
|
|
46cba9c880 | ||
|
|
4bee757b19 | ||
|
|
631f0667c7 | ||
|
|
dbf02f3390 | ||
|
|
10ba009f8e | ||
|
|
d6a5c99061 | ||
|
|
6d0ba991c9 | ||
|
|
4103ab298f | ||
|
|
af02b3b03f | ||
|
|
65b9e1e2a8 | ||
|
|
af3f5074d1 | ||
|
|
c007c96d8b | ||
|
|
576eb703d8 | ||
|
|
db47307fcd | ||
|
|
bc2c626505 | ||
|
|
514d469331 | ||
|
|
2d3754e3cc | ||
|
|
726ea4c869 | ||
|
|
fa01543a8c | ||
|
|
3a608748e4 | ||
|
|
578c710d6c | ||
|
|
633f525a3a | ||
|
|
f63fa26316 | ||
|
|
9ee49dca37 | ||
|
|
ed37cc7472 | ||
|
|
fe66215261 | ||
|
|
4a62f97331 | ||
|
|
ee6184e649 | ||
|
|
f56bd1a1e9 | ||
|
|
c005fd8f18 | ||
|
|
66d16b1507 | ||
|
|
c75a3a4073 | ||
|
|
16cd6442e6 | ||
|
|
36e2ba59c9 | ||
|
|
3192aa6981 | ||
|
|
479583281a | ||
|
|
80b4d26f01 | ||
|
|
1e547c2fa3 | ||
|
|
6f098dd198 | ||
|
|
0a037cb040 | ||
|
|
96b0956baf | ||
|
|
487d466b13 | ||
|
|
9a390c7850 | ||
|
|
fda1a00f21 | ||
|
|
f8c62bb9b5 | ||
|
|
c32cbb4b48 | ||
|
|
7dbe85b417 | ||
|
|
6cf64fb008 | ||
|
|
c11cd31cfd | ||
|
|
fe33648f92 | ||
|
|
58558f0437 | ||
|
|
5d4dd8fde0 | ||
|
|
eb897b567b | ||
|
|
836d6ba7c0 | ||
|
|
bc573b8552 | ||
|
|
d261fff273 | ||
|
|
f5c784c153 | ||
|
|
1545cd6c2e | ||
|
|
fbf3669c23 | ||
|
|
0b90a042b1 | ||
|
|
45e49eca55 | ||
|
|
0c7da43cbb | ||
|
|
e47dcb30b5 | ||
|
|
9ac88a7fd7 | ||
|
|
f6baa7169d | ||
|
|
675235f37e | ||
|
|
0dc645b7b7 | ||
|
|
64f5b9adf0 | ||
|
|
e03036ddbe | ||
|
|
d794c2150b | ||
|
|
40db7b5ef4 | ||
|
|
229fb03a9f | ||
|
|
435a993ff0 | ||
|
|
599e8e5256 | ||
|
|
11fb2f9db0 | ||
|
|
c4f0b82091 | ||
|
|
19c00fd13e | ||
|
|
8427fd306e | ||
|
|
83e490ae9c | ||
|
|
fe86a4e3cf | ||
|
|
76bf520026 | ||
|
|
4a3786f3b7 | ||
|
|
11a5e9e724 | ||
|
|
a8c798bf46 | ||
|
|
6acaf260e7 | ||
|
|
3f60a5074a | ||
|
|
f99b6a1d2a | ||
|
|
6f4783b663 | ||
|
|
2d154ecb63 | ||
|
|
285146a87f | ||
|
|
62906b8cf9 | ||
|
|
3b8a2452cb | ||
|
|
da1290ae69 | ||
|
|
b3182c1a5a | ||
|
|
2f262c4f4f | ||
|
|
a47977b2f4 | ||
|
|
5d93fbe942 | ||
|
|
9ae0ede2b5 | ||
|
|
553095c7dc | ||
|
|
e465486486 | ||
|
|
bc66db0caf | ||
|
|
95b8c9f016 | ||
|
|
cbd3d50263 | ||
|
|
c5cf0a3930 | ||
|
|
ebe0ac9ce2 | ||
|
|
d0fe6d840d | ||
|
|
a92b428c3b | ||
|
|
23ea2bfa33 | ||
|
|
f67d5ae16f | ||
|
|
cdc5593617 | ||
|
|
178973dee2 | ||
|
|
e78fbdefdd | ||
|
|
20a0cfb01f | ||
|
|
083b603c2b | ||
|
|
2bcb5b1618 | ||
|
|
e68643108a | ||
|
|
29630d41db | ||
|
|
a7da090cef | ||
|
|
ac419f1b82 | ||
|
|
03b402a88a | ||
|
|
713c13f502 | ||
|
|
92b12e708c | ||
|
|
75f87692a2 | ||
|
|
9854c45fb2 | ||
|
|
8a7a51be2f | ||
|
|
b12bb6cdc5 | ||
|
|
71735e059c | ||
|
|
f8d2084f26 | ||
|
|
3ad8cb21c8 | ||
|
|
b63496a45b | ||
|
|
574e16f9cf | ||
|
|
a7243dfb7e | ||
|
|
f38215bd8e | ||
|
|
8bb039ca1d | ||
|
|
79e685741a | ||
|
|
1a247d57a7 | ||
|
|
8640e85173 | ||
|
|
33db2d26b3 | ||
|
|
849115a461 | ||
|
|
307ab61ca3 | ||
|
|
cac7418ae9 | ||
|
|
d94f3d4d2e | ||
|
|
b6d01f8e60 | ||
|
|
30d32ce793 | ||
|
|
4c93407ea2 | ||
|
|
bfd0fd6f25 | ||
|
|
6ff036ab45 | ||
|
|
e5bf2f9774 | ||
|
|
e6f9c3ad83 | ||
|
|
e3e818323e | ||
|
|
e32a76745c | ||
|
|
969bfd237d | ||
|
|
8b29161786 | ||
|
|
46682d9be5 | ||
|
|
157edc471e | ||
|
|
d9c6659b36 | ||
|
|
5a11926f13 | ||
|
|
3d3a910f8c | ||
|
|
249bcd58ff | ||
|
|
e136eb0744 | ||
|
|
31e1347864 | ||
|
|
75ee597203 | ||
|
|
f492300d3a | ||
|
|
98f933ceb1 | ||
|
|
204f76aaaf | ||
|
|
251c8bdc07 | ||
|
|
b51587be0e | ||
|
|
96207cc49f | ||
|
|
ace7696e46 | ||
|
|
e0d9e29f73 | ||
|
|
a2260014f5 | ||
|
|
85cc8a3c87 | ||
|
|
590b39ce44 | ||
|
|
63dd386604 | ||
|
|
925ba6171b | ||
|
|
271a777d25 | ||
|
|
96aa9a3de8 | ||
|
|
14bf3d9c68 | ||
|
|
1feffb33ab | ||
|
|
4065af8d15 | ||
|
|
50f2bcf1ca | ||
|
|
d55b113e65 | ||
|
|
64cbb47385 | ||
|
|
31a14ed7fc | ||
|
|
a702635294 | ||
|
|
e7fab8c2d6 | ||
|
|
2ad0daaed5 | ||
|
|
fc9532690d | ||
|
|
3228e9fd90 | ||
|
|
8ef301f640 | ||
|
|
240abfaeba | ||
|
|
daf128eac7 | ||
|
|
e9e030d789 | ||
|
|
fcbc4b57c7 | ||
|
|
bbd88f081d | ||
|
|
2d5ec2c5dc | ||
|
|
12b6d76a61 | ||
|
|
6226930bd9 | ||
|
|
4973a143a6 | ||
|
|
2fd6cc15a1 | ||
|
|
7c21a6858f | ||
|
|
1db389bc95 | ||
|
|
cd70bb39f9 | ||
|
|
8cf2406136 | ||
|
|
899223f0f5 | ||
|
|
058557b4e3 | ||
|
|
ce6e4c43cc | ||
|
|
d2ccc1a95b | ||
|
|
2194d04ab1 | ||
|
|
f50d033551 | ||
|
|
598540aaac | ||
|
|
683c049a23 | ||
|
|
8907ea5310 | ||
|
|
ddc466b797 | ||
|
|
0e2c4a54b7 | ||
|
|
3347b7ba25 | ||
|
|
fe24d15882 | ||
|
|
066d5c58fb | ||
|
|
01ff76339e | ||
|
|
0505204ed9 | ||
|
|
22adb0c6fb | ||
|
|
95985a2b3c | ||
|
|
142d8daf78 | ||
|
|
4f435a82e4 | ||
|
|
a3910329ab | ||
|
|
d154820b28 | ||
|
|
adb50a91e7 | ||
|
|
b02ca9b894 | ||
|
|
5a583b3c1a | ||
|
|
f1061a7406 | ||
|
|
086b8c5579 | ||
|
|
945ac6b5a7 | ||
|
|
ac9ddb3575 | ||
|
|
3ac3df1802 | ||
|
|
25da851879 | ||
|
|
8ab2c34d81 | ||
|
|
b42dfd6e39 | ||
|
|
6c248168ad | ||
|
|
8d879a8182 | ||
|
|
8597a6ff54 | ||
|
|
1c54ab8299 | ||
|
|
bade990e6c | ||
|
|
70629cb34f | ||
|
|
db23fcc552 | ||
|
|
faf2afdcf3 | ||
|
|
482b65e130 | ||
|
|
2cdc905d10 | ||
|
|
f218e0f8ae | ||
|
|
44dc61ee91 | ||
|
|
b90df25c68 | ||
|
|
2f3b4104b9 | ||
|
|
56de4cf118 | ||
|
|
42c140068f | ||
|
|
f5a307a30c | ||
|
|
e77161d1fd | ||
|
|
0574633db6 | ||
|
|
c5e245589c | ||
|
|
3061300a54 | ||
|
|
80673e1166 | ||
|
|
39cb236563 | ||
|
|
1ef0527ec3 | ||
|
|
f1b7617f13 | ||
|
|
a0ccbd5795 | ||
|
|
1c10f91a22 | ||
|
|
8e65cb520c | ||
|
|
26ddb263c4 | ||
|
|
839c6470e7 | ||
|
|
49ebcc2f44 | ||
|
|
ec312fa731 | ||
|
|
964967eb3a | ||
|
|
c274b13030 | ||
|
|
439c37298f | ||
|
|
3886adf71b | ||
|
|
192d346832 | ||
|
|
37a7c4a644 | ||
|
|
fe48ade7fc | ||
|
|
16978cb34d | ||
|
|
2b030ba3ff | ||
|
|
55a1fd5b6e | ||
|
|
a4a142a258 | ||
|
|
3b669cd536 | ||
|
|
c3ce742ba9 | ||
|
|
bddeeb03d8 | ||
|
|
264d0b4e0d | ||
|
|
b07786ce95 | ||
|
|
dde6e322f5 | ||
|
|
cfaff118a2 | ||
|
|
89c97bbf0c | ||
|
|
7d165e616d | ||
|
|
a10f0cacdf | ||
|
|
08d06b16bd | ||
|
|
d16bdabc01 | ||
|
|
86a92e33f8 | ||
|
|
d9b51b27c8 | ||
|
|
19ba0b5a9f | ||
|
|
9077d744e9 | ||
|
|
62c29694c8 | ||
|
|
8ac946dcc3 | ||
|
|
31ea6d474c | ||
|
|
a08b3d2f62 | ||
|
|
d68e1b3439 | ||
|
|
18882375c6 | ||
|
|
2d6722826a | ||
|
|
e12914606f | ||
|
|
96c54b0b9c | ||
|
|
788026f2d1 | ||
|
|
8ad1e7e0d9 | ||
|
|
0bc5996ffc | ||
|
|
7ba78fd919 | ||
|
|
8699eaca32 | ||
|
|
98b2db99b3 | ||
|
|
060dcc8500 | ||
|
|
c5e3bc6867 | ||
|
|
17160168a6 | ||
|
|
0744ecb4da | ||
|
|
e2afe97157 | ||
|
|
906902dbcc | ||
|
|
2879eb8f4e | ||
|
|
f3d3f61598 | ||
|
|
0e873c929a | ||
|
|
93883dad9e | ||
|
|
b7a6d76edf | ||
|
|
b40dcfd941 | ||
|
|
e3bb49e5d8 | ||
|
|
ef0f226878 | ||
|
|
0723861b50 | ||
|
|
606d9a0d0d | ||
|
|
d3d858f506 | ||
|
|
2f4c331d8c | ||
|
|
3d046d72da | ||
|
|
a655528825 | ||
|
|
3e940b9c27 | ||
|
|
26d3d49664 | ||
|
|
ce6606ea30 | ||
|
|
58b972d54f | ||
|
|
988ed0378f | ||
|
|
a68036cbf4 | ||
|
|
469db0fd2f | ||
|
|
440683f496 | ||
|
|
ba0943d34b | ||
|
|
5cad7a6347 | ||
|
|
99c5859b1c | ||
|
|
676b0e74a8 | ||
|
|
80c452a434 | ||
|
|
fd93265e5e | ||
|
|
ec9d8b31ce | ||
|
|
d032864610 | ||
|
|
ea126c2391 | ||
|
|
aac5203023 | ||
|
|
2759f28257 | ||
|
|
1e1a134287 | ||
|
|
1f39520c27 | ||
|
|
111a41a783 | ||
|
|
72e7dfcad6 | ||
|
|
1eaf03b712 | ||
|
|
222c77d989 | ||
|
|
c668f4fa2f | ||
|
|
86c8aa7b9b | ||
|
|
e33a2a74bc | ||
|
|
2f858bc73d | ||
|
|
6e90c0bec5 | ||
|
|
26244d5682 | ||
|
|
940cf4ba55 | ||
|
|
b1206fec3f | ||
|
|
72f792230b | ||
|
|
2f615e23ae | ||
|
|
4c9e5605d9 | ||
|
|
7a216a195a | ||
|
|
6d1d3c021a | ||
|
|
2a46f0911a | ||
|
|
58257f7b38 | ||
|
|
79e4444170 | ||
|
|
72d7d643d4 | ||
|
|
d6dec560d0 | ||
|
|
ed83d4e842 | ||
|
|
72941ebfd4 | ||
|
|
ec10a4a77e | ||
|
|
7eae695a78 | ||
|
|
3c4fa33b72 | ||
|
|
52a536a1ee | ||
|
|
a0fc3685fc | ||
|
|
0efd4818df | ||
|
|
5dcc38785d | ||
|
|
4a35938c6a | ||
|
|
37c4b2104f | ||
|
|
313c12113c | ||
|
|
9abb0c985b | ||
|
|
64a40e5f67 | ||
|
|
649abbbc58 | ||
|
|
599a618992 | ||
|
|
7623405c6f | ||
|
|
1d4bb5ba28 | ||
|
|
6fb62bddff | ||
|
|
b39f5ee9e9 | ||
|
|
2bcadc6576 | ||
|
|
ef7e9797f5 | ||
|
|
cd188df212 | ||
|
|
8e670a6e6f | ||
|
|
627a2e4635 | ||
|
|
49261e0ef0 | ||
|
|
fdc3b5a7ef | ||
|
|
050c1ba024 | ||
|
|
1103739aee | ||
|
|
6a91e83436 | ||
|
|
3895de9174 | ||
|
|
d75473a90c | ||
|
|
0a77df9d40 | ||
|
|
cd167f4078 | ||
|
|
abe1d78ee0 | ||
|
|
b71b14a4fd | ||
|
|
be1b474f41 | ||
|
|
7a7884f38d | ||
|
|
6926afbac1 | ||
|
|
046f79270a | ||
|
|
4677c24242 | ||
|
|
65d3a19530 | ||
|
|
a23d441dd3 | ||
|
|
973f3e3c8b | ||
|
|
a11a2c84fe | ||
|
|
fbc47846e3 | ||
|
|
4298893960 | ||
|
|
1ef96662a3 | ||
|
|
b20698ecb2 | ||
|
|
a40b67f65f | ||
|
|
6b32bf8ca9 | ||
|
|
79e9ced183 | ||
|
|
396902cfa1 | ||
|
|
92a660cc0b | ||
|
|
13d44b0bb9 | ||
|
|
cf2081713b | ||
|
|
06ea3362b6 | ||
|
|
30307de37a | ||
|
|
9f7ac09fb0 | ||
|
|
ccfedc20e9 | ||
|
|
1e31444c67 | ||
|
|
30318faf23 | ||
|
|
1e2938bd91 | ||
|
|
f3db353bb4 | ||
|
|
c538795052 | ||
|
|
08a5014b5e | ||
|
|
58b4f0a915 | ||
|
|
e22618b1df | ||
|
|
3a88695122 | ||
|
|
c0f20fde96 | ||
|
|
cab89cc417 | ||
|
|
a1042bc87d | ||
|
|
92cc8470c1 | ||
|
|
b2b18fc00b | ||
|
|
fa97d8a827 | ||
|
|
067b2a96f8 | ||
|
|
886cf7ab2a | ||
|
|
04fce4faa1 | ||
|
|
039bf35c29 | ||
|
|
eee9732d6d | ||
|
|
6823baa791 | ||
|
|
d02a261e1b | ||
|
|
0253e1baf3 | ||
|
|
3a98e21406 | ||
|
|
dfb3e42c89 | ||
|
|
536265696b | ||
|
|
78e2683467 | ||
|
|
3471f48560 | ||
|
|
d7553bbc66 | ||
|
|
6935ea5a7c | ||
|
|
479c00925c | ||
|
|
9268cd2a33 | ||
|
|
af3b088629 | ||
|
|
76816c5f4f | ||
|
|
879b0565fa | ||
|
|
4553feff42 | ||
|
|
b0750b599a | ||
|
|
91d142a5e6 | ||
|
|
0f5941ea67 | ||
|
|
2d29d7f543 | ||
|
|
0d8bbe8d0c | ||
|
|
ec15dfa1ad | ||
|
|
b77be0232b | ||
|
|
4ab9bfab01 | ||
|
|
b2e6e7db61 | ||
|
|
de0b735eab | ||
|
|
e81588616e | ||
|
|
6b73fe336b | ||
|
|
6f078413aa | ||
|
|
089d2e4dd1 | ||
|
|
7d3b0e4db7 | ||
|
|
9758e7ed8c | ||
|
|
30c07c6790 | ||
|
|
f7cddf8fc5 | ||
|
|
0622288990 | ||
|
|
6d910519bc | ||
|
|
8aa149cac4 | ||
|
|
ee62d9a5f0 | ||
|
|
5b15c184eb | ||
|
|
e4aec16ba5 | ||
|
|
3436b52c06 | ||
|
|
94ba244ae1 | ||
|
|
5e2dce8f3f | ||
|
|
ef8778181d | ||
|
|
5c45a7e83d | ||
|
|
c94901ba02 | ||
|
|
62d7f524a8 | ||
|
|
036c2b28d0 | ||
|
|
dfe2cdadbc | ||
|
|
ca6a7ff079 | ||
|
|
66bece60b5 | ||
|
|
221eb7605f | ||
|
|
f420c7f767 | ||
|
|
5bce7b91f1 | ||
|
|
2da0043e0a | ||
|
|
a6134c6b42 | ||
|
|
a2f367357a | ||
|
|
6db60f0aa3 | ||
|
|
74aaed65b6 | ||
|
|
8decceaa38 | ||
|
|
914efc6a64 | ||
|
|
30455864fa | ||
|
|
f16f51a2b3 | ||
|
|
d97db9e17c | ||
|
|
23daa2de72 | ||
|
|
47dd105cd6 | ||
|
|
5a9640bd57 | ||
|
|
c3b6ce34ba | ||
|
|
75fed53ba4 | ||
|
|
89e75653d7 | ||
|
|
9f9f3b6402 | ||
|
|
007d79dd03 | ||
|
|
d9939f5c3a | ||
|
|
93048e3327 | ||
|
|
9f2b0f7c6b | ||
|
|
4d8477a32c | ||
|
|
bdcfcee60e | ||
|
|
c4d7ac272b | ||
|
|
c794ff8b58 | ||
|
|
c48facc863 | ||
|
|
b7d57a53f2 | ||
|
|
99ce8c9f74 | ||
|
|
5ec5396da6 | ||
|
|
c73a592fd1 | ||
|
|
bc11e568b9 | ||
|
|
8d24116859 | ||
|
|
275745fd3a | ||
|
|
0169060de7 | ||
|
|
d49da185d3 | ||
|
|
3a0cea1268 | ||
|
|
768fb1992f | ||
|
|
21525ef945 | ||
|
|
5f6dc186e3 | ||
|
|
3fe48779be | ||
|
|
2ac5236cc7 | ||
|
|
b3122219be | ||
|
|
dddbc232c2 | ||
|
|
4b2a9ea158 | ||
|
|
682de08204 | ||
|
|
d82c0dc75e | ||
|
|
cbbb679dfc | ||
|
|
deca5b31e1 | ||
|
|
6fe5c4dc54 | ||
|
|
49cf81c18b | ||
|
|
37ba90e6bd | ||
|
|
47a6e16ce1 | ||
|
|
f5e92c6897 | ||
|
|
f1014a4810 | ||
|
|
1793424658 | ||
|
|
c94a5b948e | ||
|
|
107cabcd8b | ||
|
|
99bc769894 | ||
|
|
e8f1964941 | ||
|
|
1a491bec1c | ||
|
|
4ba63cdbf8 | ||
|
|
b2c2af3ebb | ||
|
|
9dca19fcb5 | ||
|
|
7c2dab43e1 | ||
|
|
a8c6d9b034 | ||
|
|
be0cb20fa2 | ||
|
|
2ac1025e9f | ||
|
|
27d79b574e | ||
|
|
1b82f42a33 | ||
|
|
f3f9eac67b | ||
|
|
2a86a0e540 | ||
|
|
d14aea708e | ||
|
|
12a05b422d | ||
|
|
a5abe9dbf2 | ||
|
|
07fcb4e016 | ||
|
|
e2cbb2713a | ||
|
|
706e00ace0 | ||
|
|
655fbf94c1 | ||
|
|
afc574aceb | ||
|
|
3da2b56426 | ||
|
|
5f91ad8819 | ||
|
|
131a745514 | ||
|
|
86349f1ad3 | ||
|
|
72e4a7b062 | ||
|
|
0a0165df45 | ||
|
|
992b22fd24 | ||
|
|
6fbe6c673b | ||
|
|
ff22e54b59 | ||
|
|
578365ab68 | ||
|
|
22905a2efc | ||
|
|
26bad75a1a | ||
|
|
04e1950591 | ||
|
|
340f4b8fb5 | ||
|
|
457458a894 | ||
|
|
f6334c3618 | ||
|
|
cf27de965e | ||
|
|
43b2926063 | ||
|
|
1d6464fefb | ||
|
|
927ab76dbd | ||
|
|
34cfe2077b | ||
|
|
4ebe23e89f | ||
|
|
8fa61a6301 | ||
|
|
96c20b36a5 | ||
|
|
a9af5163c1 | ||
|
|
b87b108e53 | ||
|
|
ddaec7c6ac | ||
|
|
4e8e16f16d | ||
|
|
9cefdb7977 | ||
|
|
a6d000714b | ||
|
|
54758272be | ||
|
|
8e9e2c5b61 | ||
|
|
934bf495a0 | ||
|
|
1d8189369c | ||
|
|
25aa9b9bd7 | ||
|
|
a1b879a5b4 | ||
|
|
b70724f7d6 | ||
|
|
a52f96f8e5 | ||
|
|
970c573a12 | ||
|
|
46749044e2 | ||
|
|
16d748800c | ||
|
|
3e698b045a | ||
|
|
999b21577a | ||
|
|
3be5126e2d | ||
|
|
2b14c49c80 | ||
|
|
bace2a10e8 | ||
|
|
006e6b30b7 | ||
|
|
4c5d0321d2 | ||
|
|
fa69c21fa2 | ||
|
|
95439e5602 | ||
|
|
bbd8d8ef4e | ||
|
|
ef269d565c | ||
|
|
8acf6dda6e | ||
|
|
d18219dc14 | ||
|
|
3f1718f4c5 | ||
|
|
825a749b45 | ||
|
|
c2eb09e664 | ||
|
|
adb670dd0c | ||
|
|
5e9ec071dc | ||
|
|
1f41f8da23 | ||
|
|
2a5480da79 | ||
|
|
7add854b40 | ||
|
|
e639cd0bd2 | ||
|
|
3503f1f580 | ||
|
|
853dcbd69a | ||
|
|
c54fff5472 | ||
|
|
699d3d3eaa | ||
|
|
0c91f488f0 | ||
|
|
d3a644877e | ||
|
|
aac2832eb7 | ||
|
|
487c832f5b | ||
|
|
98e2e57bb2 | ||
|
|
c5170df402 | ||
|
|
4be38dfd0c | ||
|
|
597f2b69e9 | ||
|
|
c078a5fb55 | ||
|
|
5db0326350 | ||
|
|
7f6c678eaa | ||
|
|
37ac6cebc1 | ||
|
|
27099aa7fb | ||
|
|
91f4d09608 | ||
|
|
d34b9b1233 | ||
|
|
2badd2b743 | ||
|
|
4517f38680 | ||
|
|
84f9727836 | ||
|
|
85452cde23 | ||
|
|
9b19113262 | ||
|
|
e1bb091363 | ||
|
|
732d664715 | ||
|
|
33498ce903 | ||
|
|
c25b74de84 | ||
|
|
1c39e3402b | ||
|
|
a3bd10bc82 | ||
|
|
d6d237fc52 | ||
|
|
9b7a169110 | ||
|
|
2b17a24206 | ||
|
|
a0d9bd6f09 | ||
|
|
cd33abd92d | ||
|
|
c83563c0ea | ||
|
|
79515ac960 | ||
|
|
8e8a5f3fd6 | ||
|
|
51283cc130 | ||
|
|
4023c077b3 | ||
|
|
f3cf21ba08 | ||
|
|
bec7b59abf | ||
|
|
3e0abe329f | ||
|
|
822fe3db9e | ||
|
|
408ec82a10 | ||
|
|
ced3fa00ef | ||
|
|
595b3c0450 | ||
|
|
1d1c8153e7 | ||
|
|
52f556eb2e | ||
|
|
35fcd20123 | ||
|
|
e518b94fba | ||
|
|
05553ba18a | ||
|
|
4a9e05cf17 | ||
|
|
60fc351344 | ||
|
|
815e06809a | ||
|
|
bfcdf703e8 | ||
|
|
ddb2c1e641 | ||
|
|
e790360de9 | ||
|
|
b00f6fadf8 | ||
|
|
1d6f4bf5db | ||
|
|
80cea91339 | ||
|
|
5942cd6fcf | ||
|
|
2859ba6cd2 | ||
|
|
b9285fd600 | ||
|
|
901df2b90d | ||
|
|
662573d940 | ||
|
|
a8dcd3cac7 | ||
|
|
0ac16a1626 | ||
|
|
5162fa2a13 | ||
|
|
73a3d1d50f | ||
|
|
afc272c4d9 | ||
|
|
f8bcaed3ad | ||
|
|
d1a1b7426e | ||
|
|
99485cc6d8 | ||
|
|
187fee46f4 | ||
|
|
90837546ab | ||
|
|
f4cf4850a3 | ||
|
|
4ef1e491bc | ||
|
|
170f45d46b | ||
|
|
37caa1ad19 | ||
|
|
0312a0911c | ||
|
|
cc88f7678c | ||
|
|
653b470fec | ||
|
|
2603f2f987 | ||
|
|
b106b3cd0a | ||
|
|
b2b6b3af18 | ||
|
|
f911f78c95 | ||
|
|
a7560443f3 | ||
|
|
ab757b2f67 | ||
|
|
261cc68624 | ||
|
|
dc2db3a463 | ||
|
|
ae625e4c8a | ||
|
|
6f5c5b122f | ||
|
|
5d712d7d78 | ||
|
|
1654784471 | ||
|
|
7d83e434e6 | ||
|
|
97a54d44c7 | ||
|
|
6694ca6918 | ||
|
|
8e61e94fba | ||
|
|
69b1c8039e | ||
|
|
5bd89efc09 | ||
|
|
e5185f2099 | ||
|
|
d2ebc880a0 | ||
|
|
165afa436d | ||
|
|
a3f5095dce | ||
|
|
7bda5769fb | ||
|
|
0bf859d485 | ||
|
|
b79dced185 | ||
|
|
e368e618f3 | ||
|
|
bc4c69f207 | ||
|
|
32f29a84f7 | ||
|
|
f0a6420ba9 | ||
|
|
cf91637848 | ||
|
|
9bdf55374c | ||
|
|
d21758c410 | ||
|
|
bc2c945fee | ||
|
|
db2853880d | ||
|
|
cb76c89a08 | ||
|
|
059fa37ca7 | ||
|
|
54cc99448b | ||
|
|
a5dd96805d | ||
|
|
1e155af948 | ||
|
|
dd9ee044eb | ||
|
|
bd8ea17c84 | ||
|
|
0236f5132d | ||
|
|
b8adf5f274 | ||
|
|
c4bce5ec0a | ||
|
|
5bfe7dd128 | ||
|
|
9b3bdebb28 | ||
|
|
7575387236 | ||
|
|
984a99b24e | ||
|
|
a0767417b3 | ||
|
|
790c69ba80 | ||
|
|
f7ba974d97 | ||
|
|
1eab5af5c7 | ||
|
|
3df0bf79f8 | ||
|
|
34aa156d5f | ||
|
|
a88ebc26a9 | ||
|
|
d800062159 | ||
|
|
e5afe4f767 | ||
|
|
ee7a091586 | ||
|
|
7add83f985 | ||
|
|
16bee43f12 | ||
|
|
ba48104c5c | ||
|
|
cc620ddf79 | ||
|
|
6103f6a89b | ||
|
|
4b2c3d2db7 | ||
|
|
dac69daf03 | ||
|
|
3e474a3f2d | ||
|
|
f81999a4fe | ||
|
|
fd80fd65c9 | ||
|
|
ab7c52d049 | ||
|
|
a3cc3d5fc2 | ||
|
|
a6ed0c811d | ||
|
|
8e6b9c5afb | ||
|
|
b9efdd69f1 | ||
|
|
3b96b89492 | ||
|
|
32f7374d92 | ||
|
|
c6eec8b266 | ||
|
|
634ae94542 | ||
|
|
5095a2c59e | ||
|
|
002d2ba8e6 | ||
|
|
d44fe945d8 | ||
|
|
6221f9ed05 | ||
|
|
2e0e24d87b | ||
|
|
a1d869900b | ||
|
|
d90f6c2019 | ||
|
|
ed4c03f154 | ||
|
|
7bfccafca8 | ||
|
|
ae49090bad | ||
|
|
979c16eb9c | ||
|
|
fe85291772 | ||
|
|
893c5ace6f | ||
|
|
89423737e8 | ||
|
|
f9bfb742da | ||
|
|
b7622b2b38 | ||
|
|
8cfb4cf1e1 | ||
|
|
b9e02cf344 | ||
|
|
033df3c3d6 | ||
|
|
692eaf7dc9 | ||
|
|
22b3794154 | ||
|
|
dbb08a6ce0 | ||
|
|
0571a4a88f | ||
|
|
648744f440 | ||
|
|
f8fc1245ca | ||
|
|
5ecc791b38 | ||
|
|
2183b09ffe | ||
|
|
085ab521c3 | ||
|
|
61b274bab9 | ||
|
|
4ca31fc162 | ||
|
|
ae1d39bede | ||
|
|
f803941fe4 | ||
|
|
f93bb88d35 | ||
|
|
ea199dbf8f | ||
|
|
526d7195bc | ||
|
|
cf4143e4e2 | ||
|
|
0f552ae6f4 | ||
|
|
830071278e | ||
|
|
d468fb1efe | ||
|
|
2a268de2cb | ||
|
|
77cbb8ebc4 | ||
|
|
bf84e4a2ed | ||
|
|
a316366ae9 | ||
|
|
50823003b4 | ||
|
|
7c61033bdf | ||
|
|
d588d8d9ef | ||
|
|
cd90d3e581 | ||
|
|
54407af980 | ||
|
|
a31cdcc9f0 | ||
|
|
10d4419387 | ||
|
|
6f67f7bbf0 | ||
|
|
90ef41b419 | ||
|
|
62ab86aefa | ||
|
|
1dd26fb76f | ||
|
|
4a95724425 | ||
|
|
f209fa2d58 | ||
|
|
85e2aab4df | ||
|
|
26c3ea19f4 | ||
|
|
a1e2cd7274 | ||
|
|
6363822ffd | ||
|
|
34f4411aa1 | ||
|
|
b6d08e2203 | ||
|
|
4fa6ae493d | ||
|
|
79645099ba | ||
|
|
18d478e16e | ||
|
|
da97b76563 | ||
|
|
d25dbd5ae6 | ||
|
|
88e8f3363b | ||
|
|
24483ec330 | ||
|
|
15a9fba091 | ||
|
|
73e2485e09 | ||
|
|
0d94879e49 | ||
|
|
6df12ce194 | ||
|
|
c3b60367f3 | ||
|
|
10d3deff37 | ||
|
|
3cb79c167e | ||
|
|
57a17d7e92 | ||
|
|
894934fd08 | ||
|
|
5a8aae3614 | ||
|
|
3dde1a5b05 | ||
|
|
e6c79c19c2 | ||
|
|
d64abeecdc | ||
|
|
da6d45a72c | ||
|
|
497a735d80 | ||
|
|
89f031b338 | ||
|
|
47630dbcd2 | ||
|
|
0d57684565 | ||
|
|
5880767ac3 | ||
|
|
14c4c29af3 | ||
|
|
9f5614446e | ||
|
|
d755e8ffc4 | ||
|
|
a7a968ab6e | ||
|
|
f5757c6081 | ||
|
|
29fa4fa34d | ||
|
|
d2de9fb669 | ||
|
|
e124cd2490 | ||
|
|
e76c9041b5 | ||
|
|
755ae23fdb | ||
|
|
90fde34a45 | ||
|
|
563c60668a | ||
|
|
3a657e1e2f | ||
|
|
4466d733b4 | ||
|
|
dadecdc674 | ||
|
|
a2440d3180 | ||
|
|
56e6a2a16d | ||
|
|
c918eaf903 | ||
|
|
324eda25e0 | ||
|
|
a46116d936 | ||
|
|
8fd419dc72 | ||
|
|
18b27dbd0c | ||
|
|
0c17818a24 | ||
|
|
b1749ee2ef | ||
|
|
27b82c56b1 | ||
|
|
f69bda351d | ||
|
|
4a92d0ff11 | ||
|
|
b37a983bde | ||
|
|
97cf3b26b0 | ||
|
|
c490835f9b | ||
|
|
a3ab2c6e1b | ||
|
|
ce5108937d | ||
|
|
9164db181c | ||
|
|
aa14a17ad6 | ||
|
|
d67b8c0530 | ||
|
|
d41c1a2a52 | ||
|
|
3b938251d9 | ||
|
|
b8aa068876 | ||
|
|
5d288de390 | ||
|
|
af851e708b | ||
|
|
72399e7ccd | ||
|
|
1ffd71e81f | ||
|
|
d627de8e83 | ||
|
|
fc4fdb4fc7 | ||
|
|
126537185b | ||
|
|
24de0773d8 | ||
|
|
cc77af6142 | ||
|
|
c16460af82 | ||
|
|
4c7bed90a3 | ||
|
|
491b2f2c07 | ||
|
|
a038f5e618 | ||
|
|
9ba74328ff | ||
|
|
90a643761a | ||
|
|
6236d36372 | ||
|
|
065c908153 | ||
|
|
a14e612a38 | ||
|
|
0b155b1d20 | ||
|
|
9b9cfd0543 | ||
|
|
3d067371d3 | ||
|
|
381eb5a502 | ||
|
|
4a88f30d13 | ||
|
|
bdf181adec | ||
|
|
f97fce873b | ||
|
|
879017ecca | ||
|
|
83150331e5 | ||
|
|
7249ec4968 | ||
|
|
3ac148f7cd | ||
|
|
a73472f7e5 | ||
|
|
08ca59f990 | ||
|
|
d07f7e757e | ||
|
|
cb13e82b9c | ||
|
|
fd7aa570ed | ||
|
|
c00053f6e1 | ||
|
|
2e0e7f361c | ||
|
|
21101d4da8 | ||
|
|
65f739499f | ||
|
|
91ee4a32cd | ||
|
|
498668929f | ||
|
|
935b12763b | ||
|
|
28b15e4a85 | ||
|
|
6af49a9945 | ||
|
|
c80ad70e3b | ||
|
|
1a20065053 | ||
|
|
edef36bae8 | ||
|
|
3cd25dc2df | ||
|
|
43840d7656 | ||
|
|
a1bdb75036 | ||
|
|
ac0107d450 | ||
|
|
d3d2cf72b9 | ||
|
|
eb9ec4ec31 | ||
|
|
3201830b27 | ||
|
|
58ddec6aff | ||
|
|
59fd58b824 | ||
|
|
efa07f0368 | ||
|
|
1dd6a8e2e4 | ||
|
|
bcd3fa8ce4 | ||
|
|
6ff3cf544b | ||
|
|
ab21f923c6 | ||
|
|
b75fd2e03a | ||
|
|
ec7c7d521f | ||
|
|
f9909713d9 | ||
|
|
59087ced8a | ||
|
|
84435714f5 | ||
|
|
6bd628712e | ||
|
|
997f4a6bdc | ||
|
|
b1fec831c5 | ||
|
|
54fe849efd | ||
|
|
8bf1a9d023 | ||
|
|
07cedd0bdb | ||
|
|
44a93ae556 | ||
|
|
72f790b28c | ||
|
|
a63f7e741a | ||
|
|
58f952df8a | ||
|
|
2acd0ec95d | ||
|
|
105254d053 | ||
|
|
e538f2a3bb | ||
|
|
98ea491469 | ||
|
|
10e50efb33 | ||
|
|
d60023f585 | ||
|
|
c0d5feb433 | ||
|
|
2451167296 | ||
|
|
4a70e4ecd3 | ||
|
|
a90c3da7b6 | ||
|
|
53e15b041d | ||
|
|
7669254a0c | ||
|
|
b450e4093e | ||
|
|
a012d6206f | ||
|
|
30f502a51b | ||
|
|
4defeaf017 | ||
|
|
7f35fb0ada | ||
|
|
cd1a926292 | ||
|
|
e46506b264 | ||
|
|
807c5c3fb4 | ||
|
|
64efb1d43d | ||
|
|
49e1f82b03 | ||
|
|
1bd8636c19 | ||
|
|
cfe84e1275 | ||
|
|
5dda4731a0 | ||
|
|
ce830ea6d3 | ||
|
|
b217b70dfe | ||
|
|
ceee26ad25 | ||
|
|
876018390d | ||
|
|
0366f3544b | ||
|
|
b964ba5317 | ||
|
|
494e36c842 | ||
|
|
9c611a5b13 | ||
|
|
357c478640 | ||
|
|
89f830d9bb | ||
|
|
56150e8707 | ||
|
|
1d60db25bd | ||
|
|
2cac1d9fd2 | ||
|
|
e70724f058 | ||
|
|
27a05e55c9 | ||
|
|
0a0de86ecd | ||
|
|
ec025b7d0f | ||
|
|
744cea1f11 | ||
|
|
073617b6d3 | ||
|
|
8d69945e8e | ||
|
|
3f34a1fb87 | ||
|
|
7bbc7250dd | ||
|
|
63433864d3 | ||
|
|
e53f90fc5c | ||
|
|
33adb08105 | ||
|
|
4a610d182c | ||
|
|
8655d9be87 | ||
|
|
9962ddcd36 | ||
|
|
e117429373 | ||
|
|
dd0f5f961c | ||
|
|
f0b5505770 | ||
|
|
c63b4f9f21 | ||
|
|
6ed19af295 | ||
|
|
46f7a67b14 | ||
|
|
6f2639fd1f | ||
|
|
f1568eb43b | ||
|
|
aefc632ed7 | ||
|
|
bd3555db94 | ||
|
|
ed366fa4cc | ||
|
|
30aeba0af2 | ||
|
|
b4c3bd16b1 | ||
|
|
4b97abaf72 | ||
|
|
e387706a7b | ||
|
|
a7cc8786c3 | ||
|
|
5c0573deb7 | ||
|
|
bc8956db4a | ||
|
|
d44be88b3f | ||
|
|
64ee9a39cc | ||
|
|
4999f982e4 | ||
|
|
f3f0dbac19 | ||
|
|
c2818645c4 | ||
|
|
b0a4aee175 | ||
|
|
67c45f444b | ||
|
|
a8c6e916cf | ||
|
|
f3570d0c9d | ||
|
|
653be14e77 | ||
|
|
71c8b596e7 | ||
|
|
ba45b6a7a6 | ||
|
|
cd311aeaa5 | ||
|
|
4f7a6d5c96 | ||
|
|
a4fb439dd4 | ||
|
|
3870752bba | ||
|
|
7f059c3f3b | ||
|
|
686fe5abbe | ||
|
|
1f47658c3c | ||
|
|
d582a0f9e5 | ||
|
|
b1d51a4103 | ||
|
|
fb0adf74f3 | ||
|
|
0b16300a70 | ||
|
|
43302ef5a8 | ||
|
|
3846ca293c | ||
|
|
00f154ef4e | ||
|
|
4ea826ed2c | ||
|
|
d327142d00 | ||
|
|
37adcb52cf | ||
|
|
fcbb9cda12 | ||
|
|
116c36febc | ||
|
|
43a409fb30 | ||
|
|
63f26d0089 | ||
|
|
218ea09d23 | ||
|
|
a322886710 | ||
|
|
29182ae349 | ||
|
|
bc3bc8dd8a | ||
|
|
e128b847be | ||
|
|
f72251c125 | ||
|
|
9784cbb3ac | ||
|
|
e6f9003fb6 | ||
|
|
e8a5eadd2b | ||
|
|
337bfc47c1 | ||
|
|
677f1da8df | ||
|
|
8918b1ac96 | ||
|
|
0de8eb1568 | ||
|
|
c00aaa9018 | ||
|
|
e837ee5225 | ||
|
|
fc589a0b29 | ||
|
|
b0769d1cf8 | ||
|
|
5befd67270 | ||
|
|
59bdeb67bc | ||
|
|
2d1de2cac8 | ||
|
|
d3f180f270 | ||
|
|
5482766d03 | ||
|
|
dfbe43ef02 | ||
|
|
ed7bb20bbb | ||
|
|
57a2a03469 | ||
|
|
df9c1a88fc | ||
|
|
c8b78d04e2 | ||
|
|
c516969686 | ||
|
|
fd4295ade8 | ||
|
|
39d4f8c73d | ||
|
|
d9b55f1d95 | ||
|
|
66d8a32f49 | ||
|
|
8b091a7b23 | ||
|
|
8edb3dc923 | ||
|
|
a93d1e821d | ||
|
|
9c5cad7571 | ||
|
|
b970b38c29 | ||
|
|
763ef207f1 | ||
|
|
a949ec9e8e | ||
|
|
9518ad9bb4 | ||
|
|
5a0d67e409 | ||
|
|
e6932e7353 | ||
|
|
ddb08f4d2e | ||
|
|
368f7acd2d | ||
|
|
6714f90c37 | ||
|
|
bbaa4d0f05 | ||
|
|
3aa990a1b0 | ||
|
|
464ee11d0d | ||
|
|
f909d38130 | ||
|
|
77e59c886d | ||
|
|
05254326cb | ||
|
|
932dec3bde | ||
|
|
e976a0c716 | ||
|
|
71b68562db | ||
|
|
460d2d23ce | ||
|
|
6aee08b866 | ||
|
|
271b19a4ec | ||
|
|
d5b1dc5bff | ||
|
|
965e69c525 | ||
|
|
bd549c8642 | ||
|
|
66edaed3a0 | ||
|
|
79831322fd | ||
|
|
ed1c5a2197 | ||
|
|
6d32379b67 | ||
|
|
56a62ae505 | ||
|
|
50072f5997 | ||
|
|
e875e05538 | ||
|
|
e2c8551baf | ||
|
|
1b0a2811e0 | ||
|
|
9cac4d23a7 | ||
|
|
b323ddbd33 | ||
|
|
98450ebec3 | ||
|
|
28a5166f56 | ||
|
|
4f128b3fe8 | ||
|
|
fd5060b996 | ||
|
|
a2df486280 | ||
|
|
4e9b19afd1 | ||
|
|
4d78949b8d | ||
|
|
13bafdc924 | ||
|
|
ea95e8e7b5 | ||
|
|
eaa1a2f2ca | ||
|
|
9d6121903e | ||
|
|
2795bf050e | ||
|
|
0e4e430673 |
@@ -1,5 +1,4 @@
|
|||||||
./.github
|
./.github
|
||||||
./.stryker-tmp
|
|
||||||
./build
|
./build
|
||||||
./coverage
|
./coverage
|
||||||
./node_modules
|
./node_modules
|
||||||
|
|||||||
24
.eslintrc
@@ -1,24 +0,0 @@
|
|||||||
{
|
|
||||||
"extends": [
|
|
||||||
"@shlinkio/js-coding-standard"
|
|
||||||
],
|
|
||||||
"plugins": ["jest"],
|
|
||||||
"env": {
|
|
||||||
"jest/globals": true
|
|
||||||
},
|
|
||||||
"parserOptions": {
|
|
||||||
"tsconfigRootDir": ".",
|
|
||||||
"createDefaultProgram": true
|
|
||||||
},
|
|
||||||
"globals": {
|
|
||||||
"process": true,
|
|
||||||
"setImmediate": true
|
|
||||||
},
|
|
||||||
"ignorePatterns": ["src/service*.ts"],
|
|
||||||
"rules": {
|
|
||||||
"complexity": "off",
|
|
||||||
"@typescript-eslint/no-unnecessary-type-assertion": "off",
|
|
||||||
"@typescript-eslint/no-unsafe-return": "off",
|
|
||||||
"@typescript-eslint/no-unsafe-call": "off"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
2
.github/FUNDING.yml
vendored
@@ -1,2 +1,2 @@
|
|||||||
github: ['acelaya']
|
github: ['acelaya']
|
||||||
custom: ['https://acel.me/donate']
|
custom: ['https://slnk.to/donate']
|
||||||
|
|||||||
7
.github/ISSUE_TEMPLATE.md
vendored
@@ -1,7 +0,0 @@
|
|||||||
<!--
|
|
||||||
Before opening an issue, just take into account that this is a completely free of charge and open source project.
|
|
||||||
I'm always happy to help and provide support, but some understanding will be expected.
|
|
||||||
I do this in my own free time, so expect some delays when implementing new features and fixing bugs, and don't take it personal if an issue gets eventually closed.
|
|
||||||
You may also be asked to provide tests or ways to reproduce reported bugs.
|
|
||||||
Try to be polite, and understand it is impossible for an OSS project to cover all use cases.
|
|
||||||
-->
|
|
||||||
36
.github/ISSUE_TEMPLATE/Bug.md
vendored
@@ -1,36 +0,0 @@
|
|||||||
---
|
|
||||||
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 and open source project.
|
|
||||||
I'm always happy to help and provide support, but some understanding will be expected.
|
|
||||||
I do this in my own free time, so expect some delays when implementing new features and fixing bugs, and don't take it personal if an issue gets eventually closed.
|
|
||||||
You may also be asked to provide tests or ways to reproduce reported bugs.
|
|
||||||
Try to be polite, and understand it is impossible for an OSS project to cover all use cases.
|
|
||||||
|
|
||||||
With that said, please fill in the information requested next. More information might be requested once the issue is open.
|
|
||||||
-->
|
|
||||||
|
|
||||||
#### Shlink web client version
|
|
||||||
|
|
||||||
* Version: x.y.z
|
|
||||||
* How do you use shlink-web-client: app.shlink.io|Docker image|self-hosted
|
|
||||||
|
|
||||||
#### 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. -->
|
|
||||||
43
.github/ISSUE_TEMPLATE/Bug.yml
vendored
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
name: 'Bug'
|
||||||
|
description: Something on shlink is broken or not working as documented?
|
||||||
|
labels: ['bug']
|
||||||
|
body:
|
||||||
|
- type: input
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
attributes:
|
||||||
|
label: shlink-web-client version
|
||||||
|
placeholder: x.y.z
|
||||||
|
- type: dropdown
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
attributes:
|
||||||
|
label: How do you use shlink-web-client
|
||||||
|
options:
|
||||||
|
- https://app.shlink.io
|
||||||
|
- Docker image
|
||||||
|
- Self-hosted
|
||||||
|
- Other (explain in summary)
|
||||||
|
- type: textarea
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
attributes:
|
||||||
|
label: Current behavior
|
||||||
|
value: '<!-- How is it actually behaving (and it should not)? -->'
|
||||||
|
- type: textarea
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
attributes:
|
||||||
|
label: Expected behavior
|
||||||
|
value: '<!-- How did you expect it to behave? -->'
|
||||||
|
- type: textarea
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
attributes:
|
||||||
|
label: Minimum steps to reproduce
|
||||||
|
value: |
|
||||||
|
<!--
|
||||||
|
Emphasis in MINIMUM: What is the simplest way to reproduce the bug?
|
||||||
|
Avoid things like "Create a kubernetes cluster", or anything related with cloud providers, as that is rarely the root cause and the bug may be closed as "not reproducible".
|
||||||
|
If you can provide a simple docker compose config, that's even better.
|
||||||
|
-->
|
||||||
19
.github/ISSUE_TEMPLATE/Feature_Request.md
vendored
@@ -1,19 +0,0 @@
|
|||||||
---
|
|
||||||
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 and open source project.
|
|
||||||
I'm always happy to help and provide support, but some understanding will be expected.
|
|
||||||
I do this in my own free time, so expect some delays when implementing new features and fixing bugs, and don't take it personal if an issue gets eventually closed.
|
|
||||||
You may also be asked to provide tests or ways to reproduce reported bugs.
|
|
||||||
Try to be polite, and understand it is impossible for an OSS project to cover all use cases.
|
|
||||||
|
|
||||||
With that said, please fill in the information requested next. More information might be requested once the issue is open.
|
|
||||||
-->
|
|
||||||
|
|
||||||
#### Summary
|
|
||||||
|
|
||||||
<!-- Describe the new feature you would like to request. -->
|
|
||||||
16
.github/ISSUE_TEMPLATE/Feature_Request.yml
vendored
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
name: Feature request
|
||||||
|
description: Do you find shlink-web-client is missing some important feature that would make it more useful?
|
||||||
|
labels: ['feature']
|
||||||
|
body:
|
||||||
|
- type: textarea
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
attributes:
|
||||||
|
label: Summary
|
||||||
|
value: '<!-- Describe the new feature you would like to request. -->'
|
||||||
|
- type: textarea
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
attributes:
|
||||||
|
label: Use case
|
||||||
|
value: '<!-- Explain why do you think this feature would be useful, and what problems would it help to solve. -->'
|
||||||
24
.github/ISSUE_TEMPLATE/Question_Support.md
vendored
@@ -1,24 +0,0 @@
|
|||||||
---
|
|
||||||
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 and open source project.
|
|
||||||
I'm always happy to help and provide support, but some understanding will be expected.
|
|
||||||
I do this in my own free time, so expect some delays when implementing new features and fixing bugs, and don't take it personal if an issue gets eventually closed.
|
|
||||||
You may also be asked to provide tests or ways to reproduce reported bugs.
|
|
||||||
Try to be polite, and understand it is impossible for an OSS project to cover all use cases.
|
|
||||||
|
|
||||||
With that said, please fill in the information requested next. More information might be requested once the issue is open.
|
|
||||||
-->
|
|
||||||
|
|
||||||
#### Shlink web client version
|
|
||||||
|
|
||||||
* Version: x.y.z
|
|
||||||
* How do you use shlink-web-client: app.shlink.io|Docker image|self-hosted
|
|
||||||
|
|
||||||
#### Summary
|
|
||||||
|
|
||||||
<!-- Describe the issue you are facing here. -->
|
|
||||||
5
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
blank_issues_enabled: true
|
||||||
|
contact_links:
|
||||||
|
- name: Question - Support
|
||||||
|
about: Do you need help setting up or using shlink-web-client?
|
||||||
|
url: https://github.com/orgs/shlinkio/discussions/new?category=help-wanted
|
||||||
54
.github/dependabot.yml
vendored
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
version: 2
|
||||||
|
updates:
|
||||||
|
- package-ecosystem: npm
|
||||||
|
directory: '/'
|
||||||
|
schedule:
|
||||||
|
interval: weekly
|
||||||
|
day: saturday
|
||||||
|
time: '09:00'
|
||||||
|
timezone: 'Europe/Madrid'
|
||||||
|
open-pull-requests-limit: 10
|
||||||
|
groups:
|
||||||
|
fontawesome:
|
||||||
|
patterns:
|
||||||
|
- '@fortawesome/*'
|
||||||
|
eslint:
|
||||||
|
patterns:
|
||||||
|
- '@shlinkio/eslint-config-js-coding-standard'
|
||||||
|
- 'typescript-eslint'
|
||||||
|
- '*eslint-plugin*'
|
||||||
|
- 'eslint'
|
||||||
|
shlink:
|
||||||
|
patterns:
|
||||||
|
- '@shlinkio/*'
|
||||||
|
react:
|
||||||
|
patterns:
|
||||||
|
- 'react'
|
||||||
|
- 'react-dom'
|
||||||
|
- '@types/react'
|
||||||
|
- '@types/react-dom'
|
||||||
|
testing:
|
||||||
|
patterns:
|
||||||
|
- '@testing-library/*'
|
||||||
|
vite:
|
||||||
|
patterns:
|
||||||
|
- 'vite'
|
||||||
|
- '@vitejs/*'
|
||||||
|
vitest:
|
||||||
|
patterns:
|
||||||
|
- 'vitest'
|
||||||
|
- '@vitest/*'
|
||||||
|
workbox:
|
||||||
|
patterns:
|
||||||
|
- 'workbox*'
|
||||||
|
tailwindcss:
|
||||||
|
patterns:
|
||||||
|
- 'tailwindcss'
|
||||||
|
- '@tailwindcss/*'
|
||||||
|
- package-ecosystem: docker
|
||||||
|
directory: '/'
|
||||||
|
schedule:
|
||||||
|
interval: weekly
|
||||||
|
day: saturday
|
||||||
|
time: '09:00'
|
||||||
|
timezone: 'Europe/Madrid'
|
||||||
10
.github/workflows/ci-docker-image-build.yml
vendored
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
name: Test docker image build
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
paths:
|
||||||
|
- 'Dockerfile'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build-docker-image:
|
||||||
|
uses: shlinkio/github-actions/.github/workflows/docker-image-build-ci.yml@main
|
||||||
4
.github/workflows/ci.yml
vendored
@@ -11,6 +11,6 @@ jobs:
|
|||||||
ci:
|
ci:
|
||||||
uses: shlinkio/github-actions/.github/workflows/web-app-ci.yml@main
|
uses: shlinkio/github-actions/.github/workflows/web-app-ci.yml@main
|
||||||
with:
|
with:
|
||||||
node-version: 16.13
|
node-version: 22.x
|
||||||
with-mutation-tests: true
|
|
||||||
publish-coverage: true
|
publish-coverage: true
|
||||||
|
install-playwright: true
|
||||||
|
|||||||
13
.github/workflows/deploy-preview.yml
vendored
@@ -5,24 +5,23 @@ on:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
deploy:
|
deploy:
|
||||||
runs-on: ubuntu-20.04
|
runs-on: ubuntu-24.04
|
||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
repository: ${{ github.event.pull_request.head.repo.full_name }}
|
repository: ${{ github.event.pull_request.head.repo.full_name }}
|
||||||
ref: ${{ github.event.pull_request.head.ref }}
|
ref: ${{ github.event.pull_request.head.ref }}
|
||||||
- name: Use node.js
|
- name: Use node.js
|
||||||
uses: actions/setup-node@v1
|
uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: 16.13
|
node-version: 22.10
|
||||||
- name: Build
|
- name: Build
|
||||||
run: |
|
run: |
|
||||||
npm ci && \
|
npm ci && \
|
||||||
node ./scripts/set-homepage.js /shlink-web-client/${GITHUB_HEAD_REF#refs/heads/} && \
|
node ./scripts/set-homepage.cjs /shlink-web-client/${GITHUB_HEAD_REF#refs/heads/} && \
|
||||||
rm src/service-worker.ts && \
|
node --run build
|
||||||
npm run build
|
|
||||||
- name: Deploy preview
|
- name: Deploy preview
|
||||||
uses: shlinkio/deploy-preview-action@v1.0.1
|
uses: shlinkio/deploy-preview-action@v1.0.1
|
||||||
with:
|
with:
|
||||||
|
|||||||
27
.github/workflows/docker-image-build.yml
vendored
@@ -1,28 +1,15 @@
|
|||||||
name: Build docker image
|
name: Build and publish docker image
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches:
|
|
||||||
- develop
|
|
||||||
tags:
|
tags:
|
||||||
- 'v*'
|
- 'v*'
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
runs-on: ubuntu-20.04
|
uses: shlinkio/github-actions/.github/workflows/docker-publish-image.yml@main
|
||||||
steps:
|
secrets: inherit
|
||||||
- name: Checkout code
|
with:
|
||||||
uses: actions/checkout@v2
|
image-name: shlinkio/shlink-web-client
|
||||||
- name: Set up QEMU
|
version-arg-name: VERSION
|
||||||
uses: docker/setup-qemu-action@v1
|
platforms: 'linux/arm64/v8,linux/amd64'
|
||||||
- name: Set up Docker Buildx
|
|
||||||
uses: docker/setup-buildx-action@v1
|
|
||||||
with:
|
|
||||||
version: latest
|
|
||||||
- name: Login to docker hub
|
|
||||||
uses: docker/login-action@v1
|
|
||||||
with:
|
|
||||||
username: ${{ secrets.DOCKER_HUB_USERNAME }}
|
|
||||||
password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}
|
|
||||||
- name: Build the image
|
|
||||||
run: bash ./scripts/docker/build
|
|
||||||
|
|||||||
8
.github/workflows/publish-release.yml
vendored
@@ -7,14 +7,14 @@ on:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
runs-on: ubuntu-20.04
|
runs-on: ubuntu-24.04
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v4
|
||||||
- name: Use node.js
|
- name: Use node.js
|
||||||
uses: actions/setup-node@v1
|
uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: 16.13
|
node-version: 22.10
|
||||||
- name: Generate release assets
|
- name: Generate release assets
|
||||||
run: npm ci && VERSION=${GITHUB_REF#refs/tags/v} npm run build:dist
|
run: npm ci && VERSION=${GITHUB_REF#refs/tags/v} npm run build:dist
|
||||||
- name: Publish release with assets
|
- name: Publish release with assets
|
||||||
|
|||||||
5
.gitignore
vendored
@@ -3,14 +3,11 @@
|
|||||||
|
|
||||||
# testing
|
# testing
|
||||||
/coverage
|
/coverage
|
||||||
/.stryker-tmp
|
|
||||||
/reports
|
/reports
|
||||||
|
|
||||||
# production
|
# production
|
||||||
/build
|
/build
|
||||||
|
/dist
|
||||||
|
|
||||||
npm-debug.log*
|
npm-debug.log*
|
||||||
|
|
||||||
docker-compose.override.yml
|
|
||||||
home
|
|
||||||
public/servers.json*
|
public/servers.json*
|
||||||
|
|||||||
11
.stylelintrc
@@ -1,11 +0,0 @@
|
|||||||
{
|
|
||||||
"extends": [
|
|
||||||
"stylelint-config-adidas",
|
|
||||||
"stylelint-config-adidas-bem",
|
|
||||||
"stylelint-config-recommended-scss"
|
|
||||||
],
|
|
||||||
"syntax": "scss",
|
|
||||||
"plugins": [
|
|
||||||
"stylelint-scss"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
526
CHANGELOG.md
@@ -4,21 +4,15 @@ All notable changes to this project will be documented in this file.
|
|||||||
|
|
||||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org).
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org).
|
||||||
|
|
||||||
## [3.6.0] - 2022-03-17
|
## [4.4.1] - 2025-06-23
|
||||||
### Added
|
### Added
|
||||||
* [#558](https://github.com/shlinkio/shlink-web-client/pull/558) Added dark text for tags where the generated background is too light, improving its legibility.
|
* *Nothing*
|
||||||
* [#570](https://github.com/shlinkio/shlink-web-client/pull/570) Added new section to load non-orphan visits all together when consuming Shlink 3.0.0.
|
|
||||||
* [#556](https://github.com/shlinkio/shlink-web-client/pull/556) Added support to filter short URLs list by "all" tags when consuming Shlink 3.0.0.
|
|
||||||
* [#549](https://github.com/shlinkio/shlink-web-client/pull/549) Allowed to export the list of short URLs as CSV.
|
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
* [#543](https://github.com/shlinkio/shlink-web-client/pull/543) Redesigned settings section.
|
* [shlink-web-component#661](https://github.com/shlinkio/shlink-web-component/issues/661) and [#1571](https://github.com/shlinkio/shlink-web-client/issues/1571) Fully replace bootstrap with tailwind.
|
||||||
* [#567](https://github.com/shlinkio/shlink-web-client/pull/567) Improved Shlink 3.0.0 compatibility by checking the `INVALID_SHORT_URL_DELETION` error code when deleting short URLs.
|
* Add the new light theme brand color.
|
||||||
* [#448](https://github.com/shlinkio/shlink-web-client/pull/448) Updated to bootstrap v5.
|
* Update to `@shlinkio/shlink-frontend-kit` 1.0.0 and `@shlinkio/shlink-web-component` 0.15
|
||||||
* [#524](https://github.com/shlinkio/shlink-web-client/pull/524) Updated to react-router v6.
|
* Replace reactstrap nav bar with `NavBar` component from `@shlinkio/shlink-frontend-kit`
|
||||||
* [#576](https://github.com/shlinkio/shlink-web-client/pull/576) Updated to fontawesome v6.
|
|
||||||
* [#579](https://github.com/shlinkio/shlink-web-client/pull/579) Replaced react-color with react-colorful.
|
|
||||||
* [#564](https://github.com/shlinkio/shlink-web-client/pull/564) Updated most of the dependencies.
|
|
||||||
|
|
||||||
### Deprecated
|
### Deprecated
|
||||||
* *Nothing*
|
* *Nothing*
|
||||||
@@ -27,7 +21,495 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|||||||
* *Nothing*
|
* *Nothing*
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
* [#589](https://github.com/shlinkio/shlink-web-client/pull/589) Fixed alignment of shlink versions footer, by basing the logic on the presence of the sidebar instead of selected server.
|
* *Nothing*
|
||||||
|
|
||||||
|
|
||||||
|
## [4.4.0] - 2025-04-20
|
||||||
|
### Added
|
||||||
|
* [#1510](https://github.com/shlinkio/shlink-web-client/issues/1510) Existing HTTP credentials (cookies, TLS certs, authentication headers) can now be forwarded to the API server if appropriate [CORS headers](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Access-Control-Allow-Credentials) are set
|
||||||
|
* [shlink-web-component#637](https://github.com/shlinkio/shlink-web-component/pull/637) QR codes are now generated client-side, without hitting Shlink.
|
||||||
|
* [shlink-web-component#641](https://github.com/shlinkio/shlink-web-component/issues/641) It is now possible to provide any logo to be used with QR codes.
|
||||||
|
* [shlink-web-component#640](https://github.com/shlinkio/shlink-web-component/issues/640) Allow default QR code settings to be handled via app settings.
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
* Update to `react-router` 7.0
|
||||||
|
* Update to `@shlinkio/shlink-frontend-kit` 0.8.x
|
||||||
|
* Update to `@shlinkio/shlink-web-component` 0.13.x
|
||||||
|
* Update to `@shlinkio/shlink-js-sdk` 2.0.0
|
||||||
|
* Add `eslint-plugin-react-compiler`
|
||||||
|
* Run unit tests in a headless browser using vitest browser mode and playwright.
|
||||||
|
|
||||||
|
### Deprecated
|
||||||
|
* *Nothing*
|
||||||
|
|
||||||
|
### Removed
|
||||||
|
* *Nothing*
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
* *Nothing*
|
||||||
|
|
||||||
|
|
||||||
|
## [4.3.0] - 2024-11-30
|
||||||
|
### Added
|
||||||
|
* [#1360](https://github.com/shlinkio/shlink-web-client/issues/1360) Added ability for server IDs to be generated based on the server name and URL, instead of generating a random UUID.
|
||||||
|
|
||||||
|
This can improve sharing a predefined set of servers cia servers.json, env vars, or simply export and import your servers in some other device, and then be able to share server URLs which continue working.
|
||||||
|
|
||||||
|
All existing servers will keep their generated IDs in existing devices for backwards compatibility, but newly created servers will use the new approach.
|
||||||
|
|
||||||
|
* [shlink-web-component#491](https://github.com/shlinkio/shlink-web-component/issues/491) Add support for colors in QR code configurator.
|
||||||
|
* [shlink-web-component#515](https://github.com/shlinkio/shlink-web-component/issues/515) Add support for geolocation redirect conditions, when using Shlink 4.3 or newer.
|
||||||
|
* [shlink-web-component#514](https://github.com/shlinkio/shlink-web-component/issues/514) Allow filtering short URLs list by domain, when using Shlink 4.3 or newer.
|
||||||
|
* [shlink-web-component#520](https://github.com/shlinkio/shlink-web-component/issues/520) Allow navigating from domains list to short URLs list filtered by one domain, when using Shlink 4.3 or newer.
|
||||||
|
* [shlink-web-component#517](https://github.com/shlinkio/shlink-web-component/issues/517) Update list of known domains when a short URL is created with a new domain.
|
||||||
|
* [shlink-web-component#292](https://github.com/shlinkio/shlink-web-component/issues/292) Add icon in short URLs list indicating if a short URL has redirect rules.
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
* *Nothing*
|
||||||
|
|
||||||
|
### Deprecated
|
||||||
|
* *Nothing*
|
||||||
|
|
||||||
|
### Removed
|
||||||
|
* *Nothing*
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
* [shlink-web-component#504](https://github.com/shlinkio/shlink-web-component/issues/504) Fix fallback interval not causing new visits to be loaded.
|
||||||
|
|
||||||
|
|
||||||
|
## [4.2.2] - 2024-10-19
|
||||||
|
### Added
|
||||||
|
* *Nothing*
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
* Update to `@shlinkio/shlink-frontend-kit` 0.6.0
|
||||||
|
* Update to `@shlinkio/shlink-web-component` 0.10.1
|
||||||
|
|
||||||
|
### Deprecated
|
||||||
|
* *Nothing*
|
||||||
|
|
||||||
|
### Removed
|
||||||
|
* *Nothing*
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
* [shlink-web-component#475](https://github.com/shlinkio/shlink-web-component/issues/475) Fix incorrect amount of dots being displayed in line charts when the difference in days/weeks/months is rounded up.
|
||||||
|
|
||||||
|
|
||||||
|
## [4.2.1] - 2024-10-09
|
||||||
|
### Added
|
||||||
|
* *Nothing*
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
* *Nothing*
|
||||||
|
|
||||||
|
### Deprecated
|
||||||
|
* *Nothing*
|
||||||
|
|
||||||
|
### Removed
|
||||||
|
* *Nothing*
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
* [#1325](https://github.com/shlinkio/shlink-web-client/issues/1325) Get dependency on `uuid` package back, as `crypto.randomUUID()` can only be used in [secure contexts](https://developer.mozilla.org/en-US/docs/Web/Security/Secure_Contexts).
|
||||||
|
* [shlink-web-component#461](https://github.com/shlinkio/shlink-web-component/issues/461) Ensure `shortUrlsList.confirmDeletion` setting is `true` in any case, except when explicitly set to `false`.
|
||||||
|
* [shlink-web-component#237](https://github.com/shlinkio/shlink-web-component/issues/237) Set darker color for previous period in charts, when light theme is enabled.
|
||||||
|
* [shlink-web-component#246](https://github.com/shlinkio/shlink-web-component/issues/246) Fix selected date range not reflected in visits comparison date range selector, when selecting it in the line chart via drag'n'drop.
|
||||||
|
|
||||||
|
|
||||||
|
## [4.2.0] - 2024-10-07
|
||||||
|
### Added
|
||||||
|
* [shlink-web-component#411](https://github.com/shlinkio/shlink-web-component/issues/411) Add support for `ip-address` redirect conditions when Shlink server is >=4.2
|
||||||
|
* [shlink-web-component#196](https://github.com/shlinkio/shlink-web-component/issues/196) Allow active date range to be changed by selecting a range in visits and visits-comparison line charts.
|
||||||
|
* [shlink-web-component#307](https://github.com/shlinkio/shlink-web-component/issues/307) Add new setting to disable short URL deletions confirmation.
|
||||||
|
* [shlink-web-component#435](https://github.com/shlinkio/shlink-web-component/issues/435) Allow toggling between displaying raw user agent and parsed browser/OS in visits table.
|
||||||
|
* [shlink-web-component#197](https://github.com/shlinkio/shlink-web-component/issues/197) Allow line charts to be expanded to the full size of the viewport, both in individual visits views, and when comparing visits.
|
||||||
|
* [shlink-web-component#382](https://github.com/shlinkio/shlink-web-component/issues/382) Initialize QR code modal with all params unset, so that they fall back to the server defaults. Additionally, allow them to be unset if desired.
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
* Use `ShlinkWebSettings` from `@shlinkio/shlink-web-component` to replace local settings UI.
|
||||||
|
* Update to `@shlinkio/eslint-config-js-coding-standard` 3.0, and migrate to ESLint flat config.
|
||||||
|
* Remove dependency on `uuid` package, and use `crypto.randomUUID()` instead.
|
||||||
|
|
||||||
|
### Deprecated
|
||||||
|
* *Nothing*
|
||||||
|
|
||||||
|
### Removed
|
||||||
|
* *Nothing*
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
* *Nothing*
|
||||||
|
|
||||||
|
|
||||||
|
## [4.1.2] - 2024-04-17
|
||||||
|
### Added
|
||||||
|
* *Nothing*
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
* Use new reusable workflow to publish docker image
|
||||||
|
|
||||||
|
### Deprecated
|
||||||
|
* *Nothing*
|
||||||
|
|
||||||
|
### Removed
|
||||||
|
* *Nothing*
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
* [shlink-web-component#244](https://github.com/shlinkio/shlink-web-component/issues/244) Display `visitedUrl` in visits table if the visit object has it, regardless of it being an orphan visit or not.
|
||||||
|
* [shlink-web-component#327](https://github.com/shlinkio/shlink-web-component/issues/327) Ensure orphan visits type is sent to the server, to enable server-side filtering when consumed Shlink supports it.
|
||||||
|
|
||||||
|
|
||||||
|
## [4.1.1] - 2024-04-11
|
||||||
|
### Added
|
||||||
|
* [shlink-web-component#293](https://github.com/shlinkio/shlink-web-component/issues/293) Allow ordering redirect rules via drag'n'drop.
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
* Update JS coding standard
|
||||||
|
* [#1132](https://github.com/shlinkio/shlink-web-client/issues/1132) Add warning message in "validate URLs" setting, indicating it is ignored when consuming Shlink >=4.0.0.
|
||||||
|
|
||||||
|
### Deprecated
|
||||||
|
* *Nothing*
|
||||||
|
|
||||||
|
### Removed
|
||||||
|
* *Nothing*
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
* [shlink-web-component#294](https://github.com/shlinkio/shlink-web-component/issues/294) Make sure "validate URL" control is not displayed in short URL creation/edition, when consuming Shlink >=4.0.0.
|
||||||
|
* [#1130](https://github.com/shlinkio/shlink-web-client/issues/1130) Fix importing servers in Firefox for Android when the CSV file contains spaces.
|
||||||
|
* [#1133](https://github.com/shlinkio/shlink-web-client/issues/1133) Fix Shlink versions alignment in server error pages.
|
||||||
|
|
||||||
|
|
||||||
|
## [4.1.0] - 2024-03-17
|
||||||
|
### Added
|
||||||
|
* [#1079](https://github.com/shlinkio/shlink-web-client/issues/1079) Add support Shlink 4.0.0.
|
||||||
|
* [shlink-web-component#271](https://github.com/shlinkio/shlink-web-component/issues/271) Add support for redirect rules when consuming Shlink 4.0.0.
|
||||||
|
|
||||||
|
Now, if the server supports it, there will be a new item for every short URL dropdown, which will take you to a page where it will be possible to edit that short URL's redirect rules.
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
* [shlink-web-component#249](https://github.com/shlinkio/shlink-web-component/issues/249) Replace `react-datepicker` with native `input[type="date"]` and `input[type="datetime-local"]` elements.
|
||||||
|
* Update dependencies.
|
||||||
|
|
||||||
|
### Deprecated
|
||||||
|
* *Nothing*
|
||||||
|
|
||||||
|
### Removed
|
||||||
|
* [shlink-web-component#276](https://github.com/shlinkio/shlink-web-component/issues/276) Drop support for Shlink older than 3.3.0
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
* [#1084](https://github.com/shlinkio/shlink-web-client/issues/1084) Fix broken server dropdown menu when auto-connect is enabled.
|
||||||
|
|
||||||
|
|
||||||
|
## [4.0.1] - 2024-02-01
|
||||||
|
### Added
|
||||||
|
* *Nothing*
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
* [#821](https://github.com/shlinkio/shlink-web-client/issues/821) Update app gif from README.md
|
||||||
|
|
||||||
|
### Deprecated
|
||||||
|
* *Nothing*
|
||||||
|
|
||||||
|
### Removed
|
||||||
|
* *Nothing*
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
* [#1046](https://github.com/shlinkio/shlink-web-client/issues/1046) Fix running docker image when server env vars are provided.
|
||||||
|
|
||||||
|
|
||||||
|
## [4.0.0] - 2024-01-29
|
||||||
|
### Added
|
||||||
|
* [shlink-web-component #7](https://github.com/shlinkio/shlink-web-component/issues/7) Allow comparing visits for multiple short URLs, tags or domains.
|
||||||
|
|
||||||
|
When in the tags, domains or short URLs tables, you can now pick up to 5 items to compare their visits. Once selected, you are taken to a section displaying a comparative line chart, which supports all regular visits filtering capabilities.
|
||||||
|
|
||||||
|
* [shlink-web-component #9](https://github.com/shlinkio/shlink-web-component/issues/9) Allow comparing visits with the previous period.
|
||||||
|
* [shlink-web-component #12](https://github.com/shlinkio/shlink-web-component/issues/12) and [#13](https://github.com/shlinkio/shlink-web-component/issues/13) Add new "Visits options" section for arbitrary visit stats options. Add section to delete short URL and orphan visits there.
|
||||||
|
|
||||||
|
This section is only visible if short URL visits deletion or orphan visits deletion are supported by connected Shlink server.
|
||||||
|
|
||||||
|
* [shlink-web-component #10](https://github.com/shlinkio/shlink-web-component/issues/10) Improve general accessibility: Add accessibility tests, fix accessibility issues and enable accessibility linting rules.
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
* [#338](https://github.com/shlinkio/shlink-web-client/issues/338) Extract `@shlinkio/shlink-web-component` and `@shlinkio/shlink-frontend-kit` as external libs.
|
||||||
|
* [#978](https://github.com/shlinkio/shlink-web-client/issues/978) Use system preferred theme as default theme.
|
||||||
|
* Use API client from `@shlinkio/shlink-js-sdk` to consume Shlink servers.
|
||||||
|
* [#902](https://github.com/shlinkio/shlink-web-client/pull/902) Docker image is no longer running as root. As a side effect, exposed port is `8080`, not `80` anymore.
|
||||||
|
* [shlink-web-component #117](https://github.com/shlinkio/shlink-web-component/issues/117) Migrate charts from Chart.JS to Recharts.
|
||||||
|
|
||||||
|
### Deprecated
|
||||||
|
* *Nothing*
|
||||||
|
|
||||||
|
### Removed
|
||||||
|
* Drop support for Shlink older than v3.0.0
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
* [#910](https://github.com/shlinkio/shlink-web-client/issues/910) Fix warnings related with missing `act` in tests and refs in `AppUpdateBanner`.
|
||||||
|
|
||||||
|
|
||||||
|
## [3.10.2] - 2023-07-09
|
||||||
|
### Added
|
||||||
|
* *Nothing*
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
* [#781](https://github.com/shlinkio/shlink-web-client/issues/781) Migrate tests from jest to vitest.
|
||||||
|
* [#843](https://github.com/shlinkio/shlink-web-client/issues/843) Build docker image only for new tags, making sure it always includes an actual version number.
|
||||||
|
|
||||||
|
### Deprecated
|
||||||
|
* *Nothing*
|
||||||
|
|
||||||
|
### Removed
|
||||||
|
* *Nothing*
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
* *Nothing*
|
||||||
|
|
||||||
|
|
||||||
|
## [3.10.1] - 2023-04-23
|
||||||
|
### Added
|
||||||
|
* *Nothing*
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
* *Nothing*
|
||||||
|
|
||||||
|
### Deprecated
|
||||||
|
* *Nothing*
|
||||||
|
|
||||||
|
### Removed
|
||||||
|
* *Nothing*
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
* [#826](https://github.com/shlinkio/shlink-web-client/issues/826) Fix generated short URLs CSV so that it can be used to import on Shlink.
|
||||||
|
|
||||||
|
|
||||||
|
## [3.10.0] - 2023-03-19
|
||||||
|
### Added
|
||||||
|
* [#807](https://github.com/shlinkio/shlink-web-client/issues/807) Add support for device-specific long-URLs when creating or editing short URLs.
|
||||||
|
* [#808](https://github.com/shlinkio/shlink-web-client/issues/808) Respect settings on excluding bots in the overview section, for visits cards.
|
||||||
|
* [#809](https://github.com/shlinkio/shlink-web-client/issues/809) Respect settings on excluding bots in the tags list.
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
* [#798](https://github.com/shlinkio/shlink-web-client/issues/798) Remove stryker and mutation testing.
|
||||||
|
* [#800](https://github.com/shlinkio/shlink-web-client/issues/800) Use `/tags/stats` endpoint to load tags stats, when the server supports it.
|
||||||
|
* Update to Vite 4.2
|
||||||
|
* Update to TypeScript 5
|
||||||
|
* Update to coding standard v2.1.0
|
||||||
|
* Decouple tests from RTK internals.
|
||||||
|
|
||||||
|
### Deprecated
|
||||||
|
* *Nothing*
|
||||||
|
|
||||||
|
### Removed
|
||||||
|
* *Nothing*
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
* [#799](https://github.com/shlinkio/shlink-web-client/issues/799) Fix fallback visits not taking into account configuration regarding excluding bots.
|
||||||
|
|
||||||
|
|
||||||
|
## [3.9.1] - 2022-12-31
|
||||||
|
### Added
|
||||||
|
* *Nothing*
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
* *Nothing*
|
||||||
|
|
||||||
|
### Deprecated
|
||||||
|
* *Nothing*
|
||||||
|
|
||||||
|
### Removed
|
||||||
|
* *Nothing*
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
* [#787](https://github.com/shlinkio/shlink-web-client/issues/787) Fixed wrong base path set in vite config when homepage is set as empty string.
|
||||||
|
|
||||||
|
|
||||||
|
## [3.9.0] - 2022-12-31
|
||||||
|
### Added
|
||||||
|
* [#750](https://github.com/shlinkio/shlink-web-client/issues/750) Added new icon indicators telling if a short URL can be normally visited, it received the max amount of visits, is still not enabled, etc.
|
||||||
|
* [#764](https://github.com/shlinkio/shlink-web-client/issues/764) Added support to exclude visits from visits on short URLs list when consuming Shlink 3.4.0.
|
||||||
|
|
||||||
|
This feature also comes with a new setting to disable visits from bots by default, both on short URLs lists and visits sections.
|
||||||
|
|
||||||
|
* [#760](https://github.com/shlinkio/shlink-web-client/issues/760) Added support to exclude short URLs which have reached the maximum amount of visits, or are valid until a date in the past.
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
* [#753](https://github.com/shlinkio/shlink-web-client/issues/753) Migrated from react-scripts/webpack to vite.
|
||||||
|
* [#770](https://github.com/shlinkio/shlink-web-client/issues/770) Updated to latest dependencies.
|
||||||
|
* [#741](https://github.com/shlinkio/shlink-web-client/issues/741) Improved `visitsAsyncThunk`, making it wrap pending/fulfilled/rejected actions, as well as custom ones, in a type-safe way.
|
||||||
|
|
||||||
|
### Deprecated
|
||||||
|
* *Nothing*
|
||||||
|
|
||||||
|
### Removed
|
||||||
|
* [#736](https://github.com/shlinkio/shlink-web-client/issues/736) Removed cards mode in tags. Only table mode is supported now.
|
||||||
|
* [#774](https://github.com/shlinkio/shlink-web-client/issues/774) Dropped support for Shlink older than 2.8.0.
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
* [#715](https://github.com/shlinkio/shlink-web-client/issues/715) Fixed connection still failing on misconfigured servers, after editing their params to set proper values.
|
||||||
|
|
||||||
|
|
||||||
|
## [3.8.2] - 2022-12-17
|
||||||
|
### Added
|
||||||
|
* *Nothing*
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
* *Nothing*
|
||||||
|
|
||||||
|
### Deprecated
|
||||||
|
* *Nothing*
|
||||||
|
|
||||||
|
### Removed
|
||||||
|
* *Nothing*
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
* [#766](https://github.com/shlinkio/shlink-web-client/issues/766) Fixed visits query being lost when switching between sub-sections.
|
||||||
|
* [#765](https://github.com/shlinkio/shlink-web-client/issues/765) Added missing `"Content-Type": "application/json"` to requests with payload, making older Shlink versions fail.
|
||||||
|
|
||||||
|
|
||||||
|
## [3.8.1] - 2022-12-06
|
||||||
|
### Added
|
||||||
|
* *Nothing*
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
* *Nothing*
|
||||||
|
|
||||||
|
### Deprecated
|
||||||
|
* *Nothing*
|
||||||
|
|
||||||
|
### Removed
|
||||||
|
* *Nothing*
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
* [#756](https://github.com/shlinkio/shlink-web-client/issues/756) Fixed all visits interval not working unless switching to a different interval first.
|
||||||
|
* [#757](https://github.com/shlinkio/shlink-web-client/issues/757) Fixed visits fallback interval not working until the visits view has been loaded at least twice.
|
||||||
|
|
||||||
|
|
||||||
|
## [3.8.0] - 2022-12-03
|
||||||
|
### Added
|
||||||
|
* [#708](https://github.com/shlinkio/shlink-web-client/issues/708) Added support for API v3.
|
||||||
|
* [#717](https://github.com/shlinkio/shlink-web-client/issues/717) Allowed to select time in 10 minute intervals when configuring "enabled since" and "enabled until" on short URLs.
|
||||||
|
* [#748](https://github.com/shlinkio/shlink-web-client/issues/748) Improved visits section to add filters to the query string, allowing to navigate to a specific state or bookmarking filters.
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
* [#713](https://github.com/shlinkio/shlink-web-client/issues/713) Updated dependencies.
|
||||||
|
* [#620](https://github.com/shlinkio/shlink-web-client/issues/620) Migrated all reducers to redux toolkit.
|
||||||
|
* [#721](https://github.com/shlinkio/shlink-web-client/issues/721) Migrated from axios to fetch.
|
||||||
|
|
||||||
|
### Deprecated
|
||||||
|
* *Nothing*
|
||||||
|
|
||||||
|
### Removed
|
||||||
|
* *Nothing*
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
* [#590](https://github.com/shlinkio/shlink-web-client/issues/590) Fixed position of the datepicker triangle.
|
||||||
|
* [#729](https://github.com/shlinkio/shlink-web-client/issues/729) Fixed wrong stats displayed in tags after renaming.
|
||||||
|
* [#737](https://github.com/shlinkio/shlink-web-client/issues/737) Fixed incorrect contrast in warning messages when using dark theme.
|
||||||
|
* [#726](https://github.com/shlinkio/shlink-web-client/issues/726) Fixed delete server and delete short URL modals getting removed from the DOM before finishing close transition.
|
||||||
|
* [#749](https://github.com/shlinkio/shlink-web-client/issues/749) Fixed broken short URLs table when some short URL has a too long custom slug.
|
||||||
|
|
||||||
|
|
||||||
|
## [3.7.3] - 2022-09-13
|
||||||
|
### Added
|
||||||
|
* [#703](https://github.com/shlinkio/shlink-web-client/issues/703) Added support to publish docker image in GHCR.
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
* *Nothing*
|
||||||
|
|
||||||
|
### Deprecated
|
||||||
|
* *Nothing*
|
||||||
|
|
||||||
|
### Removed
|
||||||
|
* *Nothing*
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
* [#709](https://github.com/shlinkio/shlink-web-client/issues/709) Fixed visits not being displayed after a large loading has finished.
|
||||||
|
|
||||||
|
|
||||||
|
## [3.7.2] - 2022-08-07
|
||||||
|
### Added
|
||||||
|
* [#671](https://github.com/shlinkio/shlink-web-client/issues/671) Added proper color-scheme in root element based on selected theme.
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
* [#688](https://github.com/shlinkio/shlink-web-client/issues/688) Finalized migration from enzyme to react-testing-library.
|
||||||
|
|
||||||
|
### Deprecated
|
||||||
|
* *Nothing*
|
||||||
|
|
||||||
|
### Removed
|
||||||
|
* *Nothing*
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
* [#695](https://github.com/shlinkio/shlink-web-client/issues/695) Fixed some warnings in tests.
|
||||||
|
* [#693](https://github.com/shlinkio/shlink-web-client/issues/693) Fixed tags, servers and domains search to make it case-insensitive.
|
||||||
|
* [#694](https://github.com/shlinkio/shlink-web-client/issues/694) Fixed editing and loading visits on short URLs with multi-segment slugs.
|
||||||
|
|
||||||
|
|
||||||
|
## [3.7.1] - 2022-05-25
|
||||||
|
### Added
|
||||||
|
* *Nothing*
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
* [#648](https://github.com/shlinkio/shlink-web-client/issues/648) Migrated some scripts to ESM and updated to chalk 5.
|
||||||
|
|
||||||
|
### Deprecated
|
||||||
|
* *Nothing*
|
||||||
|
|
||||||
|
### Removed
|
||||||
|
* *Nothing*
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
* [#653](https://github.com/shlinkio/shlink-web-client/issues/653) Fixed rendering values greater than 1000 in charts, when the browser has certain locales configured.
|
||||||
|
|
||||||
|
|
||||||
|
## [3.7.0] - 2022-05-14
|
||||||
|
### Added
|
||||||
|
* [#622](https://github.com/shlinkio/shlink-web-client/issues/622) Added support to load domain visits when consuming Shlink 3.1.0 or newer.
|
||||||
|
* [#582](https://github.com/shlinkio/shlink-web-client/issues/582) Improved filtering short URLs by tag.
|
||||||
|
|
||||||
|
Now, a new full tags selector component is available, which allows selecting any of the existing tags and also composes a toggle to filter by "any" tag or "all" tags.
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
* [#616](https://github.com/shlinkio/shlink-web-client/issues/616) Updated to React 18.
|
||||||
|
* [#595](https://github.com/shlinkio/shlink-web-client/issues/595) Updated to react-chartjs-2 v4.1.0.
|
||||||
|
* [#594](https://github.com/shlinkio/shlink-web-client/issues/594) Updated to a new coding standard.
|
||||||
|
* [#627](https://github.com/shlinkio/shlink-web-client/issues/627) Updated to Jest 28.
|
||||||
|
* [#603](https://github.com/shlinkio/shlink-web-client/issues/603) Migrated to new and maintained dependencies to parse CSV<->JSON.
|
||||||
|
* [#610](https://github.com/shlinkio/shlink-web-client/issues/610) Migrated to a maintained coding style for CSS.
|
||||||
|
* [#619](https://github.com/shlinkio/shlink-web-client/issues/619) Introduced react testing library, to progressively replace enzyme.
|
||||||
|
|
||||||
|
### Deprecated
|
||||||
|
* *Nothing*
|
||||||
|
|
||||||
|
### Removed
|
||||||
|
* [#623](https://github.com/shlinkio/shlink-web-client/issues/623) Dropped support for Shlink older than 2.6.0.
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
* *Nothing*
|
||||||
|
|
||||||
|
|
||||||
|
## [3.6.0] - 2022-03-17
|
||||||
|
### Added
|
||||||
|
* [#558](https://github.com/shlinkio/shlink-web-client/issues/558) Added dark text for tags where the generated background is too light, improving its legibility.
|
||||||
|
* [#570](https://github.com/shlinkio/shlink-web-client/issues/570) Added new section to load non-orphan visits all together when consuming Shlink 3.0.0.
|
||||||
|
* [#556](https://github.com/shlinkio/shlink-web-client/issues/556) Added support to filter short URLs list by "all" tags when consuming Shlink 3.0.0.
|
||||||
|
* [#549](https://github.com/shlinkio/shlink-web-client/issues/549) Allowed to export the list of short URLs as CSV.
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
* [#543](https://github.com/shlinkio/shlink-web-client/issues/543) Redesigned settings section.
|
||||||
|
* [#567](https://github.com/shlinkio/shlink-web-client/issues/567) Improved Shlink 3.0.0 compatibility by checking the `INVALID_SHORT_URL_DELETION` error code when deleting short URLs.
|
||||||
|
* [#448](https://github.com/shlinkio/shlink-web-client/issues/448) Updated to bootstrap v5.
|
||||||
|
* [#524](https://github.com/shlinkio/shlink-web-client/issues/524) Updated to react-router v6.
|
||||||
|
* [#576](https://github.com/shlinkio/shlink-web-client/issues/576) Updated to fontawesome v6.
|
||||||
|
* [#579](https://github.com/shlinkio/shlink-web-client/issues/579) Replaced react-color with react-colorful.
|
||||||
|
* [#564](https://github.com/shlinkio/shlink-web-client/issues/564) Updated most of the dependencies.
|
||||||
|
|
||||||
|
### Deprecated
|
||||||
|
* *Nothing*
|
||||||
|
|
||||||
|
### Removed
|
||||||
|
* *Nothing*
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
* [#589](https://github.com/shlinkio/shlink-web-client/issues/589) Fixed alignment of shlink versions footer, by basing the logic on the presence of the sidebar instead of selected server.
|
||||||
|
|
||||||
|
|
||||||
## [3.5.1] - 2022-01-08
|
## [3.5.1] - 2022-01-08
|
||||||
@@ -51,27 +533,27 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|||||||
|
|
||||||
## [3.5.0] - 2022-01-01
|
## [3.5.0] - 2022-01-01
|
||||||
### Added
|
### Added
|
||||||
* [#407](https://github.com/shlinkio/shlink-web-client/pull/407) Improved how visits (short URLs, tags and orphan) are loaded, to avoid ending up in a page with "There are no visits matching current filter".
|
* [#407](https://github.com/shlinkio/shlink-web-client/issues/407) Improved how visits (short URLs, tags and orphan) are loaded, to avoid ending up in a page with "There are no visits matching current filter".
|
||||||
|
|
||||||
Now, the app will try to load visits for the configured default interval, and in parallel, it will load the latest visit.
|
Now, the app will try to load visits for the configured default interval, and in parallel, it will load the latest visit.
|
||||||
|
|
||||||
If the resulting list for that interval is empty, it will try to infer the closest interval with visits, based on the latest visit's date, and reload visits for that interval.
|
If the resulting list for that interval is empty, it will try to infer the closest interval with visits, based on the latest visit's date, and reload visits for that interval.
|
||||||
|
|
||||||
* [#547](https://github.com/shlinkio/shlink-web-client/pull/547) Improved domains page, to tell which of the domains are not properly configured.
|
* [#547](https://github.com/shlinkio/shlink-web-client/issues/547) Improved domains page, to tell which of the domains are not properly configured.
|
||||||
|
|
||||||
Now, when this section is loaded, it tries to call the `GET /rest/health` endpoint for each one of the domains, and displays a warning icon on each one that failed.
|
Now, when this section is loaded, it tries to call the `GET /rest/health` endpoint for each one of the domains, and displays a warning icon on each one that failed.
|
||||||
|
|
||||||
The warning includes a link to the documentation, explaining what are the steps to get it fixed.
|
The warning includes a link to the documentation, explaining what are the steps to get it fixed.
|
||||||
|
|
||||||
* [#506](https://github.com/shlinkio/shlink-web-client/pull/506) Improved how servers are handled, displaying a warning when creating or importing servers that already exist.
|
* [#506](https://github.com/shlinkio/shlink-web-client/issues/506) Improved how servers are handled, displaying a warning when creating or importing servers that already exist.
|
||||||
* [#535](https://github.com/shlinkio/shlink-web-client/pull/535) Allowed editing default domain redirects when consuming Shlink 2.10 or newer.
|
* [#535](https://github.com/shlinkio/shlink-web-client/issues/535) Allowed editing default domain redirects when consuming Shlink 2.10 or newer.
|
||||||
* [#531](https://github.com/shlinkio/shlink-web-client/pull/531) Added custom slug field to the basic creation form in the Overview page.
|
* [#531](https://github.com/shlinkio/shlink-web-client/issues/531) Added custom slug field to the basic creation form in the Overview page.
|
||||||
* [#537](https://github.com/shlinkio/shlink-web-client/pull/537) Allowed to customize the ordering for every list in the app that supports it, being currently tags and short URLs.
|
* [#537](https://github.com/shlinkio/shlink-web-client/issues/537) Allowed to customize the ordering for every list in the app that supports it, being currently tags and short URLs.
|
||||||
* [#542](https://github.com/shlinkio/shlink-web-client/pull/542) Added ordering for short URLs to the query, so that it is consistent with the rest of the filtering params.
|
* [#542](https://github.com/shlinkio/shlink-web-client/issues/542) Added ordering for short URLs to the query, so that it is consistent with the rest of the filtering params.
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
* [#534](https://github.com/shlinkio/shlink-web-client/pull/534) Updated axios.
|
* [#534](https://github.com/shlinkio/shlink-web-client/issues/534) Updated axios.
|
||||||
* [#538](https://github.com/shlinkio/shlink-web-client/pull/538) Switched to the `<field>-<dir>` notation in `orderBy` param for short URLs list, in preparation for Shlink v3.0.0
|
* [#538](https://github.com/shlinkio/shlink-web-client/issues/538) Switched to the `<field>-<dir>` notation in `orderBy` param for short URLs list, in preparation for Shlink v3.0.0
|
||||||
|
|
||||||
### Deprecated
|
### Deprecated
|
||||||
* *Nothing*
|
* *Nothing*
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ You will also see how to ensure the code fulfills the expected code checks, and
|
|||||||
|
|
||||||
## System dependencies
|
## System dependencies
|
||||||
|
|
||||||
The project can be run inside a docker container through provided docker-compose configuration.
|
The project can be run inside a docker container through provided `docker compose` configuration.
|
||||||
|
|
||||||
Because of this, the only actual dependencies are [docker](https://docs.docker.com/get-docker/) and [docker-compose](https://docs.docker.com/compose/install/).
|
Because of this, the only actual dependencies are [docker](https://docs.docker.com/get-docker/) and [docker-compose](https://docs.docker.com/compose/install/).
|
||||||
|
|
||||||
@@ -14,16 +14,13 @@ Because of this, the only actual dependencies are [docker](https://docs.docker.c
|
|||||||
|
|
||||||
The first thing you need to do is fork the repository, and clone it in your local machine.
|
The first thing you need to do is fork the repository, and clone it in your local machine.
|
||||||
|
|
||||||
Then you will have to follow these steps:
|
Then simply run `docker compose up` and you will have the project exposed in port `3000` (http://localhost:3000).
|
||||||
|
|
||||||
* Copy the file `docker-compose.override.yml.dist` by also removing the `dist` extension.
|
> The first time the container is created, the project dependencies will be installed and the container may take a bit longer to start.
|
||||||
* Start-up the project by running `docker-compose up`.
|
|
||||||
|
|
||||||
Once this is finished, you will have the project exposed in port `3000` (http://localhost:3000).
|
|
||||||
|
|
||||||
## Project structure
|
## Project structure
|
||||||
|
|
||||||
This project is a [react](https://reactjs.org/) & [redux](https://redux.js.org/) application, built with [typescript](https://www.typescriptlang.org/), which is distributed as a 100% client-side progressive web application.
|
This project is a [react](https://react.dev/) & [redux](https://redux.js.org/) application, built with [typescript](https://www.typescriptlang.org/), which is distributed as a 100% client-side progressive web application.
|
||||||
|
|
||||||
This is the basic project structure:
|
This is the basic project structure:
|
||||||
|
|
||||||
@@ -39,7 +36,7 @@ shlink-web-client
|
|||||||
```
|
```
|
||||||
|
|
||||||
* `config`: It contains some configuration scripts, used during testing, linting and building of the project.
|
* `config`: It contains some configuration scripts, used during testing, linting and building of the project.
|
||||||
* `public`: Will act as the application document root once built, and contains some static assets (favicons, images, etc).
|
* `public`: Will act as the application document root once built, and contains some static assets (favicons, images, etc.).
|
||||||
* `scripts`: It has some of the CLI scripts used to run tests or building.
|
* `scripts`: It has some of the CLI scripts used to run tests or building.
|
||||||
* `src`: Contains the main source code of the application, including both web components, SASS stylesheets and files with logic.
|
* `src`: Contains the main source code of the application, including both web components, SASS stylesheets and files with logic.
|
||||||
* `test`: Contains the project tests.
|
* `test`: Contains the project tests.
|
||||||
@@ -48,20 +45,16 @@ shlink-web-client
|
|||||||
|
|
||||||
> Note: The `indocker` shell script is a helper used to run commands inside the docker container.
|
> Note: The `indocker` shell script is a helper used to run commands inside the docker container.
|
||||||
|
|
||||||
* `./indocker npm run lint`: Checks coding styles are fulfilled, both in JS/TS files as well as in stylesheets.
|
* `./indocker node --run lint`: Checks coding styles are fulfilled in JS/TS files.
|
||||||
* `./indocker npm run lint:js`: Checks coding styles are fulfilled in JS/TS files.
|
* `./indocker node --run lint:fix`: Fixes coding styles in JS/TS files.
|
||||||
* `./indocker npm run lint:css`: Checks coding styles are fulfilled in stylesheets.
|
* `./indocker node --run test`: Runs unit tests with Jest.
|
||||||
* `./indocker npm run lint:js:fix`: Fixes coding styles in JS/TS files.
|
|
||||||
* `./indocker npm run lint:css:fix`: Fixes coding styles in stylesheets.
|
|
||||||
* `./indocker npm run test`: Runs unit tests with Jest.
|
|
||||||
* `./indocker npm run mutate`: Runs mutation tests with StrykerJS (this command can be very slow).
|
|
||||||
|
|
||||||
## Building the project
|
## Building the project
|
||||||
|
|
||||||
The source code in this project cannot be run directly in a web browser, you need to build it first.
|
The source code in this project cannot be run directly in a web browser, you need to build it first.
|
||||||
|
|
||||||
* `./indocker npm run build`: Builds the project using a combination of `webpack`, `babel` and `tsc`, generating the final static files. The content is placed in the `build` folder, which is automatically created if it does not exist.
|
* `./indocker node --run run build`: Builds the project for production using [vite](https://vite.dev/), generating the final static files. The content is placed in the `build` folder, which is automatically created if it does not exist.
|
||||||
* `./indocker npm run serve:build`: Serves the static files inside the `build` folder in port 5000 (http://localhost:5000). Useful to test the content built with previous command.
|
* `./indocker node --run run preview`: Serves the static files inside the `build` folder in a random port. Useful to test the content built with previous command.
|
||||||
|
|
||||||
## Pull request process
|
## Pull request process
|
||||||
|
|
||||||
|
|||||||
18
Dockerfile
@@ -1,12 +1,22 @@
|
|||||||
FROM node:16.13-alpine as node
|
FROM node:24.2-alpine AS node
|
||||||
COPY . /shlink-web-client
|
COPY . /shlink-web-client
|
||||||
ARG VERSION="latest"
|
ARG VERSION="latest"
|
||||||
ENV VERSION ${VERSION}
|
ENV VERSION=${VERSION}
|
||||||
RUN cd /shlink-web-client && npm ci && npm run build
|
RUN cd /shlink-web-client && npm ci && node --run build
|
||||||
|
|
||||||
FROM nginx:1.21-alpine
|
FROM nginxinc/nginx-unprivileged:1.27-alpine
|
||||||
|
ARG UID=101
|
||||||
LABEL maintainer="Alejandro Celaya <alejandro@alejandrocelaya.com>"
|
LABEL maintainer="Alejandro Celaya <alejandro@alejandrocelaya.com>"
|
||||||
|
|
||||||
|
USER root
|
||||||
RUN rm -r /usr/share/nginx/html && rm /etc/nginx/conf.d/default.conf
|
RUN rm -r /usr/share/nginx/html && rm /etc/nginx/conf.d/default.conf
|
||||||
COPY config/docker/nginx.conf /etc/nginx/conf.d/default.conf
|
COPY config/docker/nginx.conf /etc/nginx/conf.d/default.conf
|
||||||
COPY scripts/docker/servers_from_env.sh /docker-entrypoint.d/30-shlink-servers-json.sh
|
COPY scripts/docker/servers_from_env.sh /docker-entrypoint.d/30-shlink-servers-json.sh
|
||||||
COPY --from=node /shlink-web-client/build /usr/share/nginx/html
|
COPY --from=node /shlink-web-client/build /usr/share/nginx/html
|
||||||
|
|
||||||
|
# This is required by 30-shlink-servers-json.sh to be writable for UID
|
||||||
|
RUN echo '[]' > /usr/share/nginx/html/servers.json \
|
||||||
|
&& chown $UID:0 /usr/share/nginx/html/servers.json
|
||||||
|
|
||||||
|
# Switch to non-privileged UID as the last step
|
||||||
|
USER $UID
|
||||||
|
|||||||
18
README.md
@@ -1,11 +1,13 @@
|
|||||||
# shlink-web-client
|
# shlink-web-client
|
||||||
|
|
||||||
[](https://github.com/shlinkio/shlink-web-client/actions?query=workflow%3A%22Continuous+integration%22)
|
[](https://github.com/shlinkio/shlink-web-client/actions/workflows/ci.yml?query=workflow%3A%22Continuous+integration%22)
|
||||||
[](https://app.codecov.io/gh/shlinkio/shlink-web-client)
|
[](https://app.codecov.io/gh/shlinkio/shlink-web-client)
|
||||||
[](https://github.com/shlinkio/shlink-web-client/releases/latest)
|
[](https://github.com/shlinkio/shlink-web-client/releases/latest)
|
||||||
[](https://hub.docker.com/r/shlinkio/shlink-web-client/)
|
[](https://hub.docker.com/r/shlinkio/shlink-web-client/)
|
||||||
[](https://github.com/shlinkio/shlink-web-client/blob/main/LICENSE)
|
[](https://github.com/shlinkio/shlink-web-client/blob/main/LICENSE)
|
||||||
[](https://twitter.com/shlinkio)
|
|
||||||
|
[](https://fosstodon.org/@shlinkio)
|
||||||
|
[](https://bsky.app/profile/shlink.io)
|
||||||
[](https://slnk.to/donate)
|
[](https://slnk.to/donate)
|
||||||
|
|
||||||
A ReactJS-based progressive web application for [Shlink](https://shlink.io).
|
A ReactJS-based progressive web application for [Shlink](https://shlink.io).
|
||||||
@@ -28,7 +30,7 @@ The application runs 100% in the browser, so you can safely access any shlink in
|
|||||||
|
|
||||||
If you want to deploy shlink-web-client in a container-based cluster (kubernetes, docker swarm, etc), just pick the [shlinkio/shlink-web-client](https://hub.docker.com/r/shlinkio/shlink-web-client/) image and do it.
|
If you want to deploy shlink-web-client in a container-based cluster (kubernetes, docker swarm, etc), just pick the [shlinkio/shlink-web-client](https://hub.docker.com/r/shlinkio/shlink-web-client/) image and do it.
|
||||||
|
|
||||||
It's a lightweight [nginx:alpine](https://hub.docker.com/r/library/nginx/) image serving the static app on port 80.
|
It's a lightweight [nginx:alpine](https://hub.docker.com/r/library/nginx/) image serving the static app on port 8080.
|
||||||
|
|
||||||
### Self-hosted
|
### Self-hosted
|
||||||
|
|
||||||
@@ -53,7 +55,7 @@ Those servers can be exported and imported in other browsers, but if for some re
|
|||||||
[
|
[
|
||||||
{
|
{
|
||||||
"name": "Main server",
|
"name": "Main server",
|
||||||
"url": "https://doma.in",
|
"url": "https://s.test",
|
||||||
"apiKey": "09c972b7-506b-49f1-a19a-d729e22e599c"
|
"apiKey": "09c972b7-506b-49f1-a19a-d729e22e599c"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -68,11 +70,11 @@ Those servers can be exported and imported in other browsers, but if for some re
|
|||||||
|
|
||||||
If you are using the shlink-web-client docker image, you can mount the `servers.json` file in a volume inside `/usr/share/nginx/html`, which is the app's document root inside the container.
|
If you are using the shlink-web-client docker image, you can mount the `servers.json` file in a volume inside `/usr/share/nginx/html`, which is the app's document root inside the container.
|
||||||
|
|
||||||
docker run --name shlink-web-client -p 8000:80 -v ${PWD}/servers.json:/usr/share/nginx/html/servers.json shlinkio/shlink-web-client
|
docker run --name shlink-web-client -p 8000:8080 -v ${PWD}/servers.json:/usr/share/nginx/html/servers.json shlinkio/shlink-web-client
|
||||||
|
|
||||||
Alternatively, you can mount a `conf.d` directory, which in turn contains the `servers.json` file, in a volume inside `/usr/share/nginx/html`. *(since shlink-web-client 3.2.0)*.
|
Alternatively, you can mount a `conf.d` directory, which in turn contains the `servers.json` file, in a volume inside `/usr/share/nginx/html`. *(since shlink-web-client 3.2.0)*.
|
||||||
|
|
||||||
docker run --name shlink-web-client -p 8000:80 -v ${PWD}/my-config/:/usr/share/nginx/html/conf.d/ shlinkio/shlink-web-client
|
docker run --name shlink-web-client -p 8000:8080 -v ${PWD}/my-config/:/usr/share/nginx/html/conf.d/ shlinkio/shlink-web-client
|
||||||
|
|
||||||
If you want to pre-configure a single server, you can provide its config via env vars. When the container starts up, it will build the `servers.json` file dynamically based on them. *(since shlink-web-client 3.2.0)*.
|
If you want to pre-configure a single server, you can provide its config via env vars. When the container starts up, it will build the `servers.json` file dynamically based on them. *(since shlink-web-client 3.2.0)*.
|
||||||
|
|
||||||
@@ -83,8 +85,8 @@ If you want to pre-configure a single server, you can provide its config via env
|
|||||||
```shell
|
```shell
|
||||||
docker run \
|
docker run \
|
||||||
--name shlink-web-client \
|
--name shlink-web-client \
|
||||||
-p 8000:80 \
|
-p 8000:8080 \
|
||||||
-e SHLINK_SERVER_URL=https://doma.in \
|
-e SHLINK_SERVER_URL=https://s.test \
|
||||||
-e SHLINK_SERVER_API_KEY=6aeb82c6-e275-4538-a747-31f9abfba63c \
|
-e SHLINK_SERVER_API_KEY=6aeb82c6-e275-4538-a747-31f9abfba63c \
|
||||||
shlinkio/shlink-web-client
|
shlinkio/shlink-web-client
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -1,15 +0,0 @@
|
|||||||
module.exports = {
|
|
||||||
presets: [
|
|
||||||
[
|
|
||||||
'react-app',
|
|
||||||
{
|
|
||||||
runtime: 'automatic',
|
|
||||||
typescript: true,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
],
|
|
||||||
plugins: [
|
|
||||||
'@babel/plugin-proposal-optional-chaining',
|
|
||||||
'@babel/plugin-proposal-nullish-coalescing-operator',
|
|
||||||
],
|
|
||||||
};
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
server {
|
server {
|
||||||
listen 80 default_server;
|
listen 8080 default_server;
|
||||||
charset utf-8;
|
charset utf-8;
|
||||||
root /usr/share/nginx/html;
|
root /usr/share/nginx/html;
|
||||||
index index.html;
|
index index.html;
|
||||||
|
|||||||
101
config/env.js
@@ -1,101 +0,0 @@
|
|||||||
/* eslint-disable @typescript-eslint/prefer-nullish-coalescing, @typescript-eslint/promise-function-async, @typescript-eslint/prefer-optional-chain */
|
|
||||||
|
|
||||||
const fs = require('fs');
|
|
||||||
const path = require('path');
|
|
||||||
const paths = require('./paths');
|
|
||||||
|
|
||||||
// Make sure that including paths.js after env.js will read .env variables.
|
|
||||||
delete require.cache[require.resolve('./paths')];
|
|
||||||
|
|
||||||
const { NODE_ENV } = process.env;
|
|
||||||
|
|
||||||
if (!NODE_ENV) {
|
|
||||||
throw new Error(
|
|
||||||
'The NODE_ENV environment variable is required but was not specified.',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// https://github.com/bkeepers/dotenv#what-other-env-files-can-i-use
|
|
||||||
const dotenvFiles = [
|
|
||||||
`${paths.dotenv}.${NODE_ENV}.local`,
|
|
||||||
`${paths.dotenv}.${NODE_ENV}`,
|
|
||||||
|
|
||||||
// Don't include `.env.local` for `test` environment
|
|
||||||
// since normally you expect tests to produce the same
|
|
||||||
// results for everyone
|
|
||||||
NODE_ENV !== 'test' && `${paths.dotenv}.local`,
|
|
||||||
paths.dotenv,
|
|
||||||
].filter(Boolean);
|
|
||||||
|
|
||||||
// Load environment variables from .env* files. Suppress warnings using silent
|
|
||||||
// if this file is missing. dotenv will never modify any environment variables
|
|
||||||
// that have already been set. Variable expansion is supported in .env files.
|
|
||||||
// https://github.com/motdotla/dotenv
|
|
||||||
// https://github.com/motdotla/dotenv-expand
|
|
||||||
dotenvFiles.forEach((dotenvFile) => {
|
|
||||||
if (fs.existsSync(dotenvFile)) {
|
|
||||||
require('dotenv-expand')(
|
|
||||||
require('dotenv').config({
|
|
||||||
path: dotenvFile,
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// We support resolving modules according to `NODE_PATH`.
|
|
||||||
// This lets you use absolute paths in imports inside large monorepos:
|
|
||||||
// https://github.com/facebook/create-react-app/issues/253.
|
|
||||||
// It works similar to `NODE_PATH` in Node itself:
|
|
||||||
// https://nodejs.org/api/modules.html#modules_loading_from_the_global_folders
|
|
||||||
// Note that unlike in Node, only *relative* paths from `NODE_PATH` are honored.
|
|
||||||
// Otherwise, we risk importing Node.js core modules into an app instead of Webpack shims.
|
|
||||||
// https://github.com/facebook/create-react-app/issues/1023#issuecomment-265344421
|
|
||||||
// We also resolve them to make sure all tools using them work consistently.
|
|
||||||
const appDirectory = fs.realpathSync(process.cwd());
|
|
||||||
|
|
||||||
process.env.NODE_PATH = (process.env.NODE_PATH || '')
|
|
||||||
.split(path.delimiter)
|
|
||||||
.filter((folder) => folder && !path.isAbsolute(folder))
|
|
||||||
.map((folder) => path.resolve(appDirectory, folder))
|
|
||||||
.join(path.delimiter);
|
|
||||||
|
|
||||||
// Grab NODE_ENV and REACT_APP_* environment variables and prepare them to be
|
|
||||||
// injected into the application via DefinePlugin in Webpack configuration.
|
|
||||||
const REACT_APP = /^REACT_APP_/i;
|
|
||||||
|
|
||||||
function getClientEnvironment(publicUrl) {
|
|
||||||
const raw = Object.keys(process.env)
|
|
||||||
.filter((key) => REACT_APP.test(key))
|
|
||||||
.reduce(
|
|
||||||
(env, key) => {
|
|
||||||
env[key] = process.env[key];
|
|
||||||
|
|
||||||
return env;
|
|
||||||
},
|
|
||||||
{
|
|
||||||
|
|
||||||
// Useful for determining whether we’re running in production mode.
|
|
||||||
// Most importantly, it switches React into the correct mode.
|
|
||||||
NODE_ENV: process.env.NODE_ENV || 'development',
|
|
||||||
|
|
||||||
// Useful for resolving the correct path to static assets in `public`.
|
|
||||||
// For example, <img src={process.env.PUBLIC_URL + '/img/logo.png'} />.
|
|
||||||
// This should only be used as an escape hatch. Normally you would put
|
|
||||||
// images into the `src` and `import` them in code to get their paths.
|
|
||||||
PUBLIC_URL: publicUrl,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
// Stringify all values so we can feed into Webpack DefinePlugin
|
|
||||||
const stringified = {
|
|
||||||
'process.env': Object.keys(raw).reduce((env, key) => {
|
|
||||||
env[key] = JSON.stringify(raw[key]);
|
|
||||||
|
|
||||||
return env;
|
|
||||||
}, {}),
|
|
||||||
};
|
|
||||||
|
|
||||||
return { raw, stringified };
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = getClientEnvironment;
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
|
|
||||||
const path = require('path');
|
|
||||||
|
|
||||||
// This is a custom Jest transformer turning file imports into filenames.
|
|
||||||
// http://facebook.github.io/jest/docs/en/webpack.html
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
process(src, filename) {
|
|
||||||
const assetFilename = JSON.stringify(path.basename(filename));
|
|
||||||
|
|
||||||
if (filename.match(/\.svg$/)) {
|
|
||||||
return `module.exports = {
|
|
||||||
__esModule: true,
|
|
||||||
default: ${assetFilename},
|
|
||||||
ReactComponent: (props) => ({
|
|
||||||
$$typeof: Symbol.for('react.element'),
|
|
||||||
type: 'svg',
|
|
||||||
ref: null,
|
|
||||||
key: null,
|
|
||||||
props: Object.assign({}, props, {
|
|
||||||
children: ${assetFilename}
|
|
||||||
})
|
|
||||||
}),
|
|
||||||
};`;
|
|
||||||
}
|
|
||||||
|
|
||||||
return `module.exports = ${assetFilename};`;
|
|
||||||
},
|
|
||||||
};
|
|
||||||
@@ -1,89 +0,0 @@
|
|||||||
/* eslint-disable @typescript-eslint/prefer-nullish-coalescing, @typescript-eslint/promise-function-async, @typescript-eslint/prefer-optional-chain */
|
|
||||||
|
|
||||||
const path = require('path');
|
|
||||||
const fs = require('fs');
|
|
||||||
const url = require('url');
|
|
||||||
|
|
||||||
// Make sure any symlinks in the project folder are resolved:
|
|
||||||
// https://github.com/facebook/create-react-app/issues/637
|
|
||||||
const appDirectory = fs.realpathSync(process.cwd());
|
|
||||||
const resolveApp = (relativePath) => path.resolve(appDirectory, relativePath);
|
|
||||||
|
|
||||||
const envPublicUrl = process.env.PUBLIC_URL;
|
|
||||||
|
|
||||||
function ensureSlash(inputPath, needsSlash) {
|
|
||||||
const hasSlash = inputPath.endsWith('/');
|
|
||||||
|
|
||||||
if (hasSlash && !needsSlash) {
|
|
||||||
return inputPath.substr(0, inputPath.length - 1);
|
|
||||||
} else if (!hasSlash && needsSlash) {
|
|
||||||
return `${inputPath}/`;
|
|
||||||
}
|
|
||||||
|
|
||||||
return inputPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
const getPublicUrl = (appPackageJson) =>
|
|
||||||
envPublicUrl || require(appPackageJson).homepage;
|
|
||||||
|
|
||||||
// We use `PUBLIC_URL` environment variable or "homepage" field to infer
|
|
||||||
// "public path" at which the app is served.
|
|
||||||
// Webpack needs to know it to put the right <script> hrefs into HTML even in
|
|
||||||
// single-page apps that may serve index.html for nested URLs like /todos/42.
|
|
||||||
// We can't use a relative path in HTML because we don't want to load something
|
|
||||||
// like /todos/42/static/js/bundle.7289d.js. We have to know the root.
|
|
||||||
function getServedPath(appPackageJson) {
|
|
||||||
const publicUrl = getPublicUrl(appPackageJson);
|
|
||||||
const servedUrl =
|
|
||||||
envPublicUrl || (publicUrl ? url.parse(publicUrl).pathname : '/');
|
|
||||||
|
|
||||||
return ensureSlash(servedUrl, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
const moduleFileExtensions = [
|
|
||||||
'web.mjs',
|
|
||||||
'mjs',
|
|
||||||
'web.js',
|
|
||||||
'js',
|
|
||||||
'web.ts',
|
|
||||||
'ts',
|
|
||||||
'web.tsx',
|
|
||||||
'tsx',
|
|
||||||
'json',
|
|
||||||
'web.jsx',
|
|
||||||
'jsx',
|
|
||||||
];
|
|
||||||
|
|
||||||
// Resolve file paths in the same order as webpack
|
|
||||||
const resolveModule = (resolveFn, filePath) => {
|
|
||||||
const extension = moduleFileExtensions.find((extension) =>
|
|
||||||
fs.existsSync(resolveFn(`${filePath}.${extension}`)));
|
|
||||||
|
|
||||||
if (extension) {
|
|
||||||
return resolveFn(`${filePath}.${extension}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
return resolveFn(`${filePath}.js`);
|
|
||||||
};
|
|
||||||
|
|
||||||
// config after eject: we're in ./config/
|
|
||||||
module.exports = {
|
|
||||||
dotenv: resolveApp('.env'),
|
|
||||||
appPath: resolveApp('.'),
|
|
||||||
appBuild: resolveApp('build'),
|
|
||||||
appPublic: resolveApp('public'),
|
|
||||||
appHtml: resolveApp('public/index.html'),
|
|
||||||
appIndexJs: resolveModule(resolveApp, 'src/index'),
|
|
||||||
appPackageJson: resolveApp('package.json'),
|
|
||||||
appSrc: resolveApp('src'),
|
|
||||||
appTsConfig: resolveApp('tsconfig.json'),
|
|
||||||
yarnLockFile: resolveApp('yarn.lock'),
|
|
||||||
testsSetup: resolveModule(resolveApp, 'src/setupTests'),
|
|
||||||
proxySetup: resolveApp('src/setupProxy.js'),
|
|
||||||
appNodeModules: resolveApp('node_modules'),
|
|
||||||
publicUrl: getPublicUrl(resolveApp('package.json')),
|
|
||||||
servedPath: getServedPath(resolveApp('package.json')),
|
|
||||||
swSrc: resolveModule(resolveApp, 'src/service-worker'),
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports.moduleFileExtensions = moduleFileExtensions;
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
import Enzyme from 'enzyme';
|
|
||||||
import Adapter from '@wojtekmaj/enzyme-adapter-react-17';
|
|
||||||
|
|
||||||
Enzyme.configure({ adapter: new Adapter() });
|
|
||||||
9
config/test/setupTests.ts
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
import '@testing-library/jest-dom/vitest';
|
||||||
|
import { cleanup } from '@testing-library/react';
|
||||||
|
import { afterEach } from 'vitest';
|
||||||
|
|
||||||
|
// Clear all mocks and cleanup DOM after every test
|
||||||
|
afterEach(() => {
|
||||||
|
vi.clearAllMocks();
|
||||||
|
cleanup();
|
||||||
|
});
|
||||||
@@ -1,674 +0,0 @@
|
|||||||
|
|
||||||
const fs = require('fs');
|
|
||||||
const path = require('path');
|
|
||||||
const webpack = require('webpack');
|
|
||||||
const resolve = require('resolve');
|
|
||||||
const PnpWebpackPlugin = require('pnp-webpack-plugin');
|
|
||||||
const HtmlWebpackPlugin = require('html-webpack-plugin');
|
|
||||||
const CaseSensitivePathsPlugin = require('case-sensitive-paths-webpack-plugin');
|
|
||||||
const InlineChunkHtmlPlugin = require('react-dev-utils/InlineChunkHtmlPlugin');
|
|
||||||
const TerserPlugin = require('terser-webpack-plugin');
|
|
||||||
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
|
|
||||||
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
|
|
||||||
const safePostCssParser = require('postcss-safe-parser');
|
|
||||||
const ManifestPlugin = require('webpack-manifest-plugin');
|
|
||||||
const InterpolateHtmlPlugin = require('react-dev-utils/InterpolateHtmlPlugin');
|
|
||||||
const WorkboxWebpackPlugin = require('workbox-webpack-plugin');
|
|
||||||
const WatchMissingNodeModulesPlugin = require('react-dev-utils/WatchMissingNodeModulesPlugin');
|
|
||||||
const ModuleScopePlugin = require('react-dev-utils/ModuleScopePlugin');
|
|
||||||
const getCSSModuleLocalIdent = require('react-dev-utils/getCSSModuleLocalIdent');
|
|
||||||
const ModuleNotFoundPlugin = require('react-dev-utils/ModuleNotFoundPlugin');
|
|
||||||
const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin-alt');
|
|
||||||
const typescriptFormatter = require('react-dev-utils/typescriptFormatter');
|
|
||||||
const getClientEnvironment = require('./env');
|
|
||||||
const paths = require('./paths');
|
|
||||||
|
|
||||||
// Source maps are resource heavy and can cause out of memory issue for large source files.
|
|
||||||
const shouldUseSourceMap = process.env.GENERATE_SOURCEMAP !== 'false';
|
|
||||||
|
|
||||||
// Some apps do not need the benefits of saving a web request, so not inlining the chunk
|
|
||||||
// makes for a smoother build process.
|
|
||||||
const shouldInlineRuntimeChunk = process.env.INLINE_RUNTIME_CHUNK !== 'false';
|
|
||||||
|
|
||||||
// Check if TypeScript is setup
|
|
||||||
const useTypeScript = fs.existsSync(paths.appTsConfig);
|
|
||||||
|
|
||||||
// Get the path to the uncompiled service worker (if it exists).
|
|
||||||
const swSrc = paths.swSrc;
|
|
||||||
|
|
||||||
// style files regexes
|
|
||||||
const cssRegex = /\.css$/;
|
|
||||||
const cssModuleRegex = /\.module\.css$/;
|
|
||||||
const sassRegex = /\.(scss|sass)$/;
|
|
||||||
const sassModuleRegex = /\.module\.(scss|sass)$/;
|
|
||||||
|
|
||||||
// This is the production and development configuration.
|
|
||||||
// It is focused on developer experience, fast rebuilds, and a minimal bundle.
|
|
||||||
/* eslint-disable complexity */
|
|
||||||
module.exports = (webpackEnv) => {
|
|
||||||
const isEnvDevelopment = webpackEnv === 'development';
|
|
||||||
const isEnvProduction = webpackEnv === 'production';
|
|
||||||
|
|
||||||
// Webpack uses `publicPath` to determine where the app is being served from.
|
|
||||||
// It requires a trailing slash, or the file assets will get an incorrect path.
|
|
||||||
// In development, we always serve from the root. This makes config easier.
|
|
||||||
const publicPath = isEnvProduction
|
|
||||||
? paths.servedPath
|
|
||||||
: isEnvDevelopment && '/';
|
|
||||||
|
|
||||||
// Some apps do not use client-side routing with pushState.
|
|
||||||
// For these, "homepage" can be set to "." to enable relative asset paths.
|
|
||||||
const shouldUseRelativeAssetPaths = publicPath === './';
|
|
||||||
|
|
||||||
// `publicUrl` is just like `publicPath`, but we will provide it to our app
|
|
||||||
// as %PUBLIC_URL% in `index.html` and `process.env.PUBLIC_URL` in JavaScript.
|
|
||||||
// Omit trailing slash as %PUBLIC_URL%/xyz looks better than %PUBLIC_URL%xyz.
|
|
||||||
const publicUrl = isEnvProduction
|
|
||||||
? publicPath.slice(0, -1)
|
|
||||||
: isEnvDevelopment && '';
|
|
||||||
|
|
||||||
// Get environment variables to inject into our app.
|
|
||||||
const env = getClientEnvironment(publicUrl);
|
|
||||||
|
|
||||||
// common function to get style loaders
|
|
||||||
const getStyleLoaders = (cssOptions, preProcessor) => {
|
|
||||||
const loaders = [
|
|
||||||
isEnvDevelopment && require.resolve('style-loader'),
|
|
||||||
isEnvProduction && {
|
|
||||||
loader: MiniCssExtractPlugin.loader,
|
|
||||||
options: Object.assign(
|
|
||||||
{},
|
|
||||||
shouldUseRelativeAssetPaths ? { publicPath: '../../' } : undefined,
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
loader: require.resolve('css-loader'),
|
|
||||||
options: cssOptions,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
|
|
||||||
// Options for PostCSS as we reference these options twice
|
|
||||||
// Adds vendor prefixing based on your specified browser support in
|
|
||||||
// package.json
|
|
||||||
loader: require.resolve('postcss-loader'),
|
|
||||||
options: {
|
|
||||||
|
|
||||||
// Necessary for external CSS imports to work
|
|
||||||
// https://github.com/facebook/create-react-app/issues/2677
|
|
||||||
ident: 'postcss',
|
|
||||||
plugins: () => [
|
|
||||||
require('postcss-flexbugs-fixes'),
|
|
||||||
require('postcss-preset-env')({
|
|
||||||
autoprefixer: {
|
|
||||||
flexbox: 'no-2009',
|
|
||||||
},
|
|
||||||
stage: 3,
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
sourceMap: isEnvProduction && shouldUseSourceMap,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
].filter(Boolean);
|
|
||||||
|
|
||||||
if (preProcessor) {
|
|
||||||
loaders.push({
|
|
||||||
loader: require.resolve(preProcessor),
|
|
||||||
options: {
|
|
||||||
sourceMap: isEnvProduction && shouldUseSourceMap,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return loaders;
|
|
||||||
};
|
|
||||||
|
|
||||||
return {
|
|
||||||
mode: isEnvProduction ? 'production' : isEnvDevelopment && 'development',
|
|
||||||
|
|
||||||
// Stop compilation early in production
|
|
||||||
bail: isEnvProduction,
|
|
||||||
devtool: isEnvProduction
|
|
||||||
? shouldUseSourceMap
|
|
||||||
? 'source-map'
|
|
||||||
: false
|
|
||||||
: isEnvDevelopment && 'cheap-module-source-map',
|
|
||||||
|
|
||||||
// These are the "entry points" to our application.
|
|
||||||
// This means they will be the "root" imports that are included in JS bundle.
|
|
||||||
entry: [
|
|
||||||
|
|
||||||
// Include an alternative client for WebpackDevServer. A client's job is to
|
|
||||||
// connect to WebpackDevServer by a socket and get notified about changes.
|
|
||||||
// When you save a file, the client will either apply hot updates (in case
|
|
||||||
// of CSS changes), or refresh the page (in case of JS changes). When you
|
|
||||||
// make a syntax error, this client will display a syntax error overlay.
|
|
||||||
// Note: instead of the default WebpackDevServer client, we use a custom one
|
|
||||||
// to bring better experience for Create React App users. You can replace
|
|
||||||
// the line below with these two lines if you prefer the stock client:
|
|
||||||
// require.resolve('webpack-dev-server/client') + '?/',
|
|
||||||
// require.resolve('webpack/hot/dev-server'),
|
|
||||||
isEnvDevelopment &&
|
|
||||||
require.resolve('react-dev-utils/webpackHotDevClient'),
|
|
||||||
|
|
||||||
// Finally, this is your app's code:
|
|
||||||
paths.appIndexJs,
|
|
||||||
|
|
||||||
// We include the app code last so that if there is a runtime error during
|
|
||||||
// initialization, it doesn't blow up the WebpackDevServer client, and
|
|
||||||
// changing JS code would still trigger a refresh.
|
|
||||||
].filter(Boolean),
|
|
||||||
output: {
|
|
||||||
|
|
||||||
// The build folder.
|
|
||||||
path: isEnvProduction ? paths.appBuild : undefined,
|
|
||||||
|
|
||||||
// Add /* filename */ comments to generated require()s in the output.
|
|
||||||
pathinfo: isEnvDevelopment,
|
|
||||||
|
|
||||||
// There will be one main bundle, and one file per asynchronous chunk.
|
|
||||||
// In development, it does not produce real files.
|
|
||||||
filename: isEnvProduction
|
|
||||||
? 'static/js/[name].[chunkhash:8].js'
|
|
||||||
: isEnvDevelopment && 'static/js/bundle.js',
|
|
||||||
|
|
||||||
// There are also additional JS chunk files if you use code splitting.
|
|
||||||
chunkFilename: isEnvProduction
|
|
||||||
? 'static/js/[name].[chunkhash:8].chunk.js'
|
|
||||||
: isEnvDevelopment && 'static/js/[name].chunk.js',
|
|
||||||
|
|
||||||
// We inferred the "public path" (such as / or /my-project) from homepage.
|
|
||||||
// We use "/" in development.
|
|
||||||
publicPath,
|
|
||||||
|
|
||||||
// Point sourcemap entries to original disk location (format as URL on Windows)
|
|
||||||
devtoolModuleFilenameTemplate: isEnvProduction
|
|
||||||
? (info) =>
|
|
||||||
path
|
|
||||||
.relative(paths.appSrc, info.absoluteResourcePath)
|
|
||||||
.replace(/\\/g, '/')
|
|
||||||
: isEnvDevelopment &&
|
|
||||||
((info) => path.resolve(info.absoluteResourcePath).replace(/\\/g, '/')),
|
|
||||||
},
|
|
||||||
optimization: {
|
|
||||||
minimize: isEnvProduction,
|
|
||||||
minimizer: [
|
|
||||||
|
|
||||||
// This is only used in production mode
|
|
||||||
new TerserPlugin({
|
|
||||||
terserOptions: {
|
|
||||||
parse: {
|
|
||||||
|
|
||||||
// we want terser to parse ecma 8 code. However, we don't want it
|
|
||||||
// to apply any minfication steps that turns valid ecma 5 code
|
|
||||||
// into invalid ecma 5 code. This is why the 'compress' and 'output'
|
|
||||||
// sections only apply transformations that are ecma 5 safe
|
|
||||||
// https://github.com/facebook/create-react-app/pull/4234
|
|
||||||
ecma: 8,
|
|
||||||
},
|
|
||||||
compress: {
|
|
||||||
ecma: 5,
|
|
||||||
warnings: false,
|
|
||||||
|
|
||||||
// Disabled because of an issue with Uglify breaking seemingly valid code:
|
|
||||||
// https://github.com/facebook/create-react-app/issues/2376
|
|
||||||
// Pending further investigation:
|
|
||||||
// https://github.com/mishoo/UglifyJS2/issues/2011
|
|
||||||
comparisons: false,
|
|
||||||
|
|
||||||
// Disabled because of an issue with Terser breaking valid code:
|
|
||||||
// https://github.com/facebook/create-react-app/issues/5250
|
|
||||||
// Pending futher investigation:
|
|
||||||
// https://github.com/terser-js/terser/issues/120
|
|
||||||
inline: 2,
|
|
||||||
},
|
|
||||||
mangle: {
|
|
||||||
safari10: true,
|
|
||||||
},
|
|
||||||
output: {
|
|
||||||
ecma: 5,
|
|
||||||
comments: false,
|
|
||||||
|
|
||||||
// Turned on because emoji and regex is not minified properly using default
|
|
||||||
// https://github.com/facebook/create-react-app/issues/2488
|
|
||||||
ascii_only: true, // eslint-disable-line @typescript-eslint/camelcase
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
// Use multi-process parallel running to improve the build speed
|
|
||||||
// Default number of concurrent runs: os.cpus().length - 1
|
|
||||||
parallel: true,
|
|
||||||
|
|
||||||
// Enable file caching
|
|
||||||
cache: true,
|
|
||||||
sourceMap: shouldUseSourceMap,
|
|
||||||
}),
|
|
||||||
|
|
||||||
// This is only used in production mode
|
|
||||||
new OptimizeCSSAssetsPlugin({
|
|
||||||
cssProcessorOptions: {
|
|
||||||
parser: safePostCssParser,
|
|
||||||
map: shouldUseSourceMap
|
|
||||||
? {
|
|
||||||
|
|
||||||
// `inline: false` forces the sourcemap to be output into a
|
|
||||||
// separate file
|
|
||||||
inline: false,
|
|
||||||
|
|
||||||
// `annotation: true` appends the sourceMappingURL to the end of
|
|
||||||
// the css file, helping the browser find the sourcemap
|
|
||||||
annotation: true,
|
|
||||||
}
|
|
||||||
: false,
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
|
|
||||||
// Automatically split vendor and commons
|
|
||||||
// https://twitter.com/wSokra/status/969633336732905474
|
|
||||||
// https://medium.com/webpack/webpack-4-code-splitting-chunk-graph-and-the-splitchunks-optimization-be739a861366
|
|
||||||
splitChunks: {
|
|
||||||
chunks: 'all',
|
|
||||||
name: false,
|
|
||||||
},
|
|
||||||
|
|
||||||
// Keep the runtime chunk separated to enable long term caching
|
|
||||||
// https://twitter.com/wSokra/status/969679223278505985
|
|
||||||
runtimeChunk: true,
|
|
||||||
},
|
|
||||||
resolve: {
|
|
||||||
|
|
||||||
// This allows you to set a fallback for where Webpack should look for modules.
|
|
||||||
// We placed these paths second because we want `node_modules` to "win"
|
|
||||||
// if there are any conflicts. This matches Node resolution mechanism.
|
|
||||||
// https://github.com/facebook/create-react-app/issues/253
|
|
||||||
modules: [ 'node_modules' ].concat(
|
|
||||||
|
|
||||||
// It is guaranteed to exist because we tweak it in `env.js`
|
|
||||||
process.env.NODE_PATH.split(path.delimiter).filter(Boolean),
|
|
||||||
),
|
|
||||||
|
|
||||||
// These are the reasonable defaults supported by the Node ecosystem.
|
|
||||||
// We also include JSX as a common component filename extension to support
|
|
||||||
// some tools, although we do not recommend using it, see:
|
|
||||||
// https://github.com/facebook/create-react-app/issues/290
|
|
||||||
// `web` extension prefixes have been added for better support
|
|
||||||
// for React Native Web.
|
|
||||||
extensions: paths.moduleFileExtensions
|
|
||||||
.map((ext) => `.${ext}`)
|
|
||||||
.filter((ext) => useTypeScript || !ext.includes('ts')),
|
|
||||||
alias: {
|
|
||||||
|
|
||||||
// Support React Native Web
|
|
||||||
// https://www.smashingmagazine.com/2016/08/a-glimpse-into-the-future-with-react-native-for-web/
|
|
||||||
'react-native': 'react-native-web',
|
|
||||||
},
|
|
||||||
plugins: [
|
|
||||||
|
|
||||||
// Adds support for installing with Plug'n'Play, leading to faster installs and adding
|
|
||||||
// guards against forgotten dependencies and such.
|
|
||||||
PnpWebpackPlugin,
|
|
||||||
|
|
||||||
// Prevents users from importing files from outside of src/ (or node_modules/).
|
|
||||||
// This often causes confusion because we only process files within src/ with babel.
|
|
||||||
// To fix this, we prevent you from importing files out of src/ -- if you'd like to,
|
|
||||||
// please link the files into your node_modules/ and let module-resolution kick in.
|
|
||||||
// Make sure your source files are compiled, as they will not be processed in any way.
|
|
||||||
new ModuleScopePlugin(paths.appSrc, [ paths.appPackageJson ]),
|
|
||||||
],
|
|
||||||
},
|
|
||||||
resolveLoader: {
|
|
||||||
plugins: [
|
|
||||||
|
|
||||||
// Also related to Plug'n'Play, but this time it tells Webpack to load its loaders
|
|
||||||
// from the current package.
|
|
||||||
PnpWebpackPlugin.moduleLoader(module),
|
|
||||||
],
|
|
||||||
},
|
|
||||||
module: {
|
|
||||||
strictExportPresence: true,
|
|
||||||
rules: [
|
|
||||||
|
|
||||||
// Disable require.ensure as it's not a standard language feature.
|
|
||||||
{ parser: { requireEnsure: false } },
|
|
||||||
|
|
||||||
// First, run the linter.
|
|
||||||
// It's important to do this before Babel processes the JS.
|
|
||||||
{
|
|
||||||
test: /\.(js|mjs|jsx)$/,
|
|
||||||
enforce: 'pre',
|
|
||||||
use: [
|
|
||||||
{
|
|
||||||
options: {
|
|
||||||
formatter: require.resolve('react-dev-utils/eslintFormatter'),
|
|
||||||
eslintPath: require.resolve('eslint'),
|
|
||||||
|
|
||||||
},
|
|
||||||
loader: require.resolve('eslint-loader'),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
include: paths.appSrc,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
|
|
||||||
// "oneOf" will traverse all following loaders until one will
|
|
||||||
// match the requirements. When no loader matches it will fall
|
|
||||||
// back to the "file" loader at the end of the loader list.
|
|
||||||
oneOf: [
|
|
||||||
|
|
||||||
// "url" loader works like "file" loader except that it embeds assets
|
|
||||||
// smaller than specified limit in bytes as data URLs to avoid requests.
|
|
||||||
// A missing `test` is equivalent to a match.
|
|
||||||
{
|
|
||||||
test: [ /\.bmp$/, /\.gif$/, /\.jpe?g$/, /\.png$/ ],
|
|
||||||
loader: require.resolve('url-loader'),
|
|
||||||
options: {
|
|
||||||
limit: 10000,
|
|
||||||
name: 'static/media/[name].[hash:8].[ext]',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
// Process application JS with Babel.
|
|
||||||
// The preset includes JSX, Flow, TypeScript, and some ESnext features.
|
|
||||||
{
|
|
||||||
test: /\.(js|mjs|jsx|ts|tsx)$/,
|
|
||||||
include: paths.appSrc,
|
|
||||||
loader: require.resolve('babel-loader'),
|
|
||||||
options: {
|
|
||||||
customize: require.resolve(
|
|
||||||
'babel-preset-react-app/webpack-overrides',
|
|
||||||
),
|
|
||||||
|
|
||||||
plugins: [
|
|
||||||
[
|
|
||||||
require.resolve('babel-plugin-named-asset-import'),
|
|
||||||
{
|
|
||||||
loaderMap: {
|
|
||||||
svg: {
|
|
||||||
ReactComponent:
|
|
||||||
'@svgr/webpack?-prettier,-svgo![path]',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
],
|
|
||||||
|
|
||||||
// This is a feature of `babel-loader` for webpack (not Babel itself).
|
|
||||||
// It enables caching results in ./node_modules/.cache/babel-loader/
|
|
||||||
// directory for faster rebuilds.
|
|
||||||
cacheDirectory: true,
|
|
||||||
cacheCompression: isEnvProduction,
|
|
||||||
compact: isEnvProduction,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
// Process any JS outside of the app with Babel.
|
|
||||||
// Unlike the application JS, we only compile the standard ES features.
|
|
||||||
{
|
|
||||||
test: /\.(js|mjs)$/,
|
|
||||||
exclude: /@babel(?:\/|\\{1,2})runtime/,
|
|
||||||
loader: require.resolve('babel-loader'),
|
|
||||||
options: {
|
|
||||||
babelrc: false,
|
|
||||||
configFile: false,
|
|
||||||
compact: false,
|
|
||||||
presets: [
|
|
||||||
[
|
|
||||||
require.resolve('babel-preset-react-app/dependencies'),
|
|
||||||
{ helpers: true },
|
|
||||||
],
|
|
||||||
],
|
|
||||||
cacheDirectory: true,
|
|
||||||
cacheCompression: isEnvProduction,
|
|
||||||
|
|
||||||
// If an error happens in a package, it's possible to be
|
|
||||||
// because it was compiled. Thus, we don't want the browser
|
|
||||||
// debugger to show the original code. Instead, the code
|
|
||||||
// being evaluated would be much more helpful.
|
|
||||||
sourceMaps: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
// "postcss" loader applies autoprefixer to our CSS.
|
|
||||||
// "css" loader resolves paths in CSS and adds assets as dependencies.
|
|
||||||
// "style" loader turns CSS into JS modules that inject <style> tags.
|
|
||||||
// In production, we use MiniCSSExtractPlugin to extract that CSS
|
|
||||||
// to a file, but in development "style" loader enables hot editing
|
|
||||||
// of CSS.
|
|
||||||
// By default we support CSS Modules with the extension .module.css
|
|
||||||
{
|
|
||||||
test: cssRegex,
|
|
||||||
exclude: cssModuleRegex,
|
|
||||||
use: getStyleLoaders({
|
|
||||||
importLoaders: 1,
|
|
||||||
sourceMap: isEnvProduction && shouldUseSourceMap,
|
|
||||||
}),
|
|
||||||
|
|
||||||
// Don't consider CSS imports dead code even if the
|
|
||||||
// containing package claims to have no side effects.
|
|
||||||
// Remove this when webpack adds a warning or an error for this.
|
|
||||||
// See https://github.com/webpack/webpack/issues/6571
|
|
||||||
sideEffects: true,
|
|
||||||
},
|
|
||||||
|
|
||||||
// Adds support for CSS Modules (https://github.com/css-modules/css-modules)
|
|
||||||
// using the extension .module.css
|
|
||||||
{
|
|
||||||
test: cssModuleRegex,
|
|
||||||
use: getStyleLoaders({
|
|
||||||
importLoaders: 1,
|
|
||||||
sourceMap: isEnvProduction && shouldUseSourceMap,
|
|
||||||
modules: true,
|
|
||||||
getLocalIdent: getCSSModuleLocalIdent,
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
|
|
||||||
// Opt-in support for SASS (using .scss or .sass extensions).
|
|
||||||
// By default we support SASS Modules with the
|
|
||||||
// extensions .module.scss or .module.sass
|
|
||||||
{
|
|
||||||
test: sassRegex,
|
|
||||||
exclude: sassModuleRegex,
|
|
||||||
use: getStyleLoaders(
|
|
||||||
{
|
|
||||||
importLoaders: 2,
|
|
||||||
sourceMap: isEnvProduction && shouldUseSourceMap,
|
|
||||||
},
|
|
||||||
'sass-loader',
|
|
||||||
),
|
|
||||||
|
|
||||||
// Don't consider CSS imports dead code even if the
|
|
||||||
// containing package claims to have no side effects.
|
|
||||||
// Remove this when webpack adds a warning or an error for this.
|
|
||||||
// See https://github.com/webpack/webpack/issues/6571
|
|
||||||
sideEffects: true,
|
|
||||||
},
|
|
||||||
|
|
||||||
// Adds support for CSS Modules, but using SASS
|
|
||||||
// using the extension .module.scss or .module.sass
|
|
||||||
{
|
|
||||||
test: sassModuleRegex,
|
|
||||||
use: getStyleLoaders(
|
|
||||||
{
|
|
||||||
importLoaders: 2,
|
|
||||||
sourceMap: isEnvProduction && shouldUseSourceMap,
|
|
||||||
modules: true,
|
|
||||||
getLocalIdent: getCSSModuleLocalIdent,
|
|
||||||
},
|
|
||||||
'sass-loader',
|
|
||||||
),
|
|
||||||
},
|
|
||||||
|
|
||||||
// "file" loader makes sure those assets get served by WebpackDevServer.
|
|
||||||
// When you `import` an asset, you get its (virtual) filename.
|
|
||||||
// In production, they would get copied to the `build` folder.
|
|
||||||
// This loader doesn't use a "test" so it will catch all modules
|
|
||||||
// that fall through the other loaders.
|
|
||||||
{
|
|
||||||
loader: require.resolve('file-loader'),
|
|
||||||
|
|
||||||
// Exclude `js` files to keep "css" loader working as it injects
|
|
||||||
// its runtime that would otherwise be processed through "file" loader.
|
|
||||||
// Also exclude `html` and `json` extensions so they get processed
|
|
||||||
// by webpacks internal loaders.
|
|
||||||
exclude: [ /\.(js|mjs|jsx|ts|tsx)$/, /\.html$/, /\.json$/ ],
|
|
||||||
options: {
|
|
||||||
name: 'static/media/[name].[hash:8].[ext]',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
// ** STOP ** Are you adding a new loader?
|
|
||||||
// Make sure to add the new loader(s) before the "file" loader.
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
plugins: [
|
|
||||||
|
|
||||||
// Generates an `index.html` file with the <script> injected.
|
|
||||||
new HtmlWebpackPlugin(
|
|
||||||
Object.assign(
|
|
||||||
{},
|
|
||||||
{
|
|
||||||
inject: true,
|
|
||||||
template: paths.appHtml,
|
|
||||||
},
|
|
||||||
isEnvProduction
|
|
||||||
? {
|
|
||||||
minify: {
|
|
||||||
removeComments: true,
|
|
||||||
collapseWhitespace: true,
|
|
||||||
removeRedundantAttributes: true,
|
|
||||||
useShortDoctype: true,
|
|
||||||
removeEmptyAttributes: true,
|
|
||||||
removeStyleLinkTypeAttributes: true,
|
|
||||||
keepClosingSlash: true,
|
|
||||||
minifyJS: true,
|
|
||||||
minifyCSS: true,
|
|
||||||
minifyURLs: true,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
: undefined,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
|
|
||||||
// Inlines the webpack runtime script. This script is too small to warrant
|
|
||||||
// a network request.
|
|
||||||
isEnvProduction &&
|
|
||||||
shouldInlineRuntimeChunk &&
|
|
||||||
new InlineChunkHtmlPlugin(HtmlWebpackPlugin, [ /runtime~.+[.]js/ ]),
|
|
||||||
|
|
||||||
// Makes some environment variables available in index.html.
|
|
||||||
// The public URL is available as %PUBLIC_URL% in index.html, e.g.:
|
|
||||||
// <link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
|
|
||||||
// In production, it will be an empty string unless you specify "homepage"
|
|
||||||
// in `package.json`, in which case it will be the pathname of that URL.
|
|
||||||
// In development, this will be an empty string.
|
|
||||||
new InterpolateHtmlPlugin(HtmlWebpackPlugin, env.raw),
|
|
||||||
|
|
||||||
// This gives some necessary context to module not found errors, such as
|
|
||||||
// the requesting resource.
|
|
||||||
new ModuleNotFoundPlugin(paths.appPath),
|
|
||||||
|
|
||||||
// Makes some environment variables available to the JS code, for example:
|
|
||||||
// if (process.env.NODE_ENV === 'production') { ... }. See `./env.js`.
|
|
||||||
// It is absolutely essential that NODE_ENV is set to production
|
|
||||||
// during a production build.
|
|
||||||
// Otherwise React will be compiled in the very slow development mode.
|
|
||||||
new webpack.DefinePlugin(env.stringified),
|
|
||||||
|
|
||||||
// This is necessary to emit hot updates (currently CSS only):
|
|
||||||
isEnvDevelopment && new webpack.HotModuleReplacementPlugin(),
|
|
||||||
|
|
||||||
// Watcher doesn't work well if you mistype casing in a path so we use
|
|
||||||
// a plugin that prints an error when you attempt to do this.
|
|
||||||
// See https://github.com/facebook/create-react-app/issues/240
|
|
||||||
isEnvDevelopment && new CaseSensitivePathsPlugin(),
|
|
||||||
|
|
||||||
// If you require a missing module and then `npm install` it, you still have
|
|
||||||
// to restart the development server for Webpack to discover it. This plugin
|
|
||||||
// makes the discovery automatic so you don't have to restart.
|
|
||||||
// See https://github.com/facebook/create-react-app/issues/186
|
|
||||||
isEnvDevelopment &&
|
|
||||||
new WatchMissingNodeModulesPlugin(paths.appNodeModules),
|
|
||||||
isEnvProduction &&
|
|
||||||
new MiniCssExtractPlugin({
|
|
||||||
|
|
||||||
// Options similar to the same options in webpackOptions.output
|
|
||||||
// both options are optional
|
|
||||||
filename: 'static/css/[name].[contenthash:8].css',
|
|
||||||
chunkFilename: 'static/css/[name].[contenthash:8].chunk.css',
|
|
||||||
}),
|
|
||||||
|
|
||||||
// Generate a manifest file which contains a mapping of all asset filenames
|
|
||||||
// to their corresponding output file so that tools can pick it up without
|
|
||||||
// having to parse `index.html`.
|
|
||||||
new ManifestPlugin({
|
|
||||||
fileName: 'asset-manifest.json',
|
|
||||||
publicPath,
|
|
||||||
}),
|
|
||||||
|
|
||||||
// Moment.js is an extremely popular library that bundles large locale files
|
|
||||||
// by default due to how Webpack interprets its code. This is a practical
|
|
||||||
// solution that requires the user to opt into importing specific locales.
|
|
||||||
// https://github.com/jmblog/how-to-optimize-momentjs-with-webpack
|
|
||||||
// You can remove this if you don't use Moment.js:
|
|
||||||
new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/),
|
|
||||||
|
|
||||||
// Generate a service worker script that will precache, and keep up to date,
|
|
||||||
// the HTML & assets that are part of the webpack build.
|
|
||||||
isEnvProduction && fs.existsSync(swSrc) && new WorkboxWebpackPlugin.InjectManifest({
|
|
||||||
swSrc,
|
|
||||||
dontCacheBustURLsMatching: /\.[0-9a-f]{8}\./,
|
|
||||||
exclude: [ /\.map$/, /asset-manifest\.json$/, /LICENSE/ ],
|
|
||||||
// Bump up the default maximum size (2mb) that's precached,
|
|
||||||
// to make lazy-loading failure scenarios less likely.
|
|
||||||
// See https://github.com/cra-template/pwa/issues/13#issuecomment-722667270
|
|
||||||
maximumFileSizeToCacheInBytes: 5 * 1024 * 1024,
|
|
||||||
}),
|
|
||||||
|
|
||||||
// TypeScript type checking
|
|
||||||
useTypeScript &&
|
|
||||||
new ForkTsCheckerWebpackPlugin({
|
|
||||||
typescript: resolve.sync('typescript', {
|
|
||||||
basedir: paths.appNodeModules,
|
|
||||||
}),
|
|
||||||
async: false,
|
|
||||||
checkSyntacticErrors: true,
|
|
||||||
tsconfig: paths.appTsConfig,
|
|
||||||
compilerOptions: {
|
|
||||||
module: 'esnext',
|
|
||||||
moduleResolution: 'node',
|
|
||||||
resolveJsonModule: true,
|
|
||||||
isolatedModules: true,
|
|
||||||
noEmit: true,
|
|
||||||
jsx: 'preserve',
|
|
||||||
},
|
|
||||||
reportFiles: [
|
|
||||||
'**',
|
|
||||||
'!**/*.json',
|
|
||||||
'!**/__tests__/**',
|
|
||||||
'!**/?(*.)(spec|test).*',
|
|
||||||
'!**/src/setupProxy.*',
|
|
||||||
'!**/src/setupTests.*',
|
|
||||||
],
|
|
||||||
watch: paths.appSrc,
|
|
||||||
silent: true,
|
|
||||||
formatter: typescriptFormatter,
|
|
||||||
}),
|
|
||||||
].filter(Boolean),
|
|
||||||
|
|
||||||
// Some libraries import Node modules but don't use them in the browser.
|
|
||||||
// Tell Webpack to provide empty mocks for them so importing them works.
|
|
||||||
node: {
|
|
||||||
dgram: 'empty',
|
|
||||||
fs: 'empty',
|
|
||||||
net: 'empty',
|
|
||||||
tls: 'empty',
|
|
||||||
child_process: 'empty', // eslint-disable-line @typescript-eslint/camelcase
|
|
||||||
},
|
|
||||||
|
|
||||||
// Turn off performance processing because we utilize
|
|
||||||
// our own hints via the FileSizeReporter
|
|
||||||
performance: false,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
@@ -1,116 +0,0 @@
|
|||||||
/* eslint-disable @typescript-eslint/prefer-nullish-coalescing */
|
|
||||||
|
|
||||||
const fs = require('fs');
|
|
||||||
const errorOverlayMiddleware = require('react-dev-utils/errorOverlayMiddleware');
|
|
||||||
const evalSourceMapMiddleware = require('react-dev-utils/evalSourceMapMiddleware');
|
|
||||||
const noopServiceWorkerMiddleware = require('react-dev-utils/noopServiceWorkerMiddleware');
|
|
||||||
const ignoredFiles = require('react-dev-utils/ignoredFiles');
|
|
||||||
const paths = require('./paths');
|
|
||||||
|
|
||||||
const protocol = process.env.HTTPS === 'true' ? 'https' : 'http';
|
|
||||||
const host = process.env.HOST || '0.0.0.0';
|
|
||||||
|
|
||||||
module.exports = function(proxy, allowedHost) {
|
|
||||||
return {
|
|
||||||
|
|
||||||
// WebpackDevServer 2.4.3 introduced a security fix that prevents remote
|
|
||||||
// websites from potentially accessing local content through DNS rebinding:
|
|
||||||
// https://github.com/webpack/webpack-dev-server/issues/887
|
|
||||||
// https://medium.com/webpack/webpack-dev-server-middleware-security-issues-1489d950874a
|
|
||||||
// However, it made several existing use cases such as development in cloud
|
|
||||||
// environment or subdomains in development significantly more complicated:
|
|
||||||
// https://github.com/facebook/create-react-app/issues/2271
|
|
||||||
// https://github.com/facebook/create-react-app/issues/2233
|
|
||||||
// While we're investigating better solutions, for now we will take a
|
|
||||||
// compromise. Since our WDS configuration only serves files in the `public`
|
|
||||||
// folder we won't consider accessing them a vulnerability. However, if you
|
|
||||||
// use the `proxy` feature, it gets more dangerous because it can expose
|
|
||||||
// remote code execution vulnerabilities in backends like Django and Rails.
|
|
||||||
// So we will disable the host check normally, but enable it if you have
|
|
||||||
// specified the `proxy` setting. Finally, we let you override it if you
|
|
||||||
// really know what you're doing with a special environment variable.
|
|
||||||
disableHostCheck:
|
|
||||||
!proxy || process.env.DANGEROUSLY_DISABLE_HOST_CHECK === 'true',
|
|
||||||
|
|
||||||
// Enable gzip compression of generated files.
|
|
||||||
compress: true,
|
|
||||||
|
|
||||||
// Silence WebpackDevServer's own logs since they're generally not useful.
|
|
||||||
// It will still show compile warnings and errors with this setting.
|
|
||||||
clientLogLevel: 'none',
|
|
||||||
|
|
||||||
// By default WebpackDevServer serves physical files from current directory
|
|
||||||
// in addition to all the virtual build products that it serves from memory.
|
|
||||||
// This is confusing because those files won’t automatically be available in
|
|
||||||
// production build folder unless we copy them. However, copying the whole
|
|
||||||
// project directory is dangerous because we may expose sensitive files.
|
|
||||||
// Instead, we establish a convention that only files in `public` directory
|
|
||||||
// get served. Our build script will copy `public` into the `build` folder.
|
|
||||||
// In `index.html`, you can get URL of `public` folder with %PUBLIC_URL%:
|
|
||||||
// <link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
|
|
||||||
// In JavaScript code, you can access it with `process.env.PUBLIC_URL`.
|
|
||||||
// Note that we only recommend to use `public` folder as an escape hatch
|
|
||||||
// for files like `favicon.ico`, `manifest.json`, and libraries that are
|
|
||||||
// for some reason broken when imported through Webpack. If you just want to
|
|
||||||
// use an image, put it in `src` and `import` it from JavaScript instead.
|
|
||||||
contentBase: paths.appPublic,
|
|
||||||
|
|
||||||
// By default files from `contentBase` will not trigger a page reload.
|
|
||||||
watchContentBase: true,
|
|
||||||
|
|
||||||
// Enable hot reloading server. It will provide /sockjs-node/ endpoint
|
|
||||||
// for the WebpackDevServer client so it can learn when the files were
|
|
||||||
// updated. The WebpackDevServer client is included as an entry point
|
|
||||||
// in the Webpack development configuration. Note that only changes
|
|
||||||
// to CSS are currently hot reloaded. JS changes will refresh the browser.
|
|
||||||
hot: true,
|
|
||||||
|
|
||||||
// It is important to tell WebpackDevServer to use the same "root" path
|
|
||||||
// as we specified in the config. In development, we always serve from /.
|
|
||||||
publicPath: '/',
|
|
||||||
|
|
||||||
// WebpackDevServer is noisy by default so we emit custom message instead
|
|
||||||
// by listening to the compiler events with `compiler.hooks[...].tap` calls above.
|
|
||||||
quiet: true,
|
|
||||||
|
|
||||||
// Reportedly, this avoids CPU overload on some systems.
|
|
||||||
// https://github.com/facebook/create-react-app/issues/293
|
|
||||||
// src/node_modules is not ignored to support absolute imports
|
|
||||||
// https://github.com/facebook/create-react-app/issues/1065
|
|
||||||
watchOptions: {
|
|
||||||
ignored: ignoredFiles(paths.appSrc),
|
|
||||||
},
|
|
||||||
|
|
||||||
// Enable HTTPS if the HTTPS environment variable is set to 'true'
|
|
||||||
https: protocol === 'https',
|
|
||||||
host,
|
|
||||||
overlay: false,
|
|
||||||
historyApiFallback: {
|
|
||||||
|
|
||||||
// Paths with dots should still use the history fallback.
|
|
||||||
// See https://github.com/facebook/create-react-app/issues/387.
|
|
||||||
disableDotRule: true,
|
|
||||||
},
|
|
||||||
public: allowedHost,
|
|
||||||
proxy,
|
|
||||||
before(app, server) {
|
|
||||||
if (fs.existsSync(paths.proxySetup)) {
|
|
||||||
// This registers user provided middleware for proxy reasons
|
|
||||||
require(paths.proxySetup)(app);
|
|
||||||
}
|
|
||||||
|
|
||||||
// This lets us fetch source contents from webpack for the error overlay
|
|
||||||
app.use(evalSourceMapMiddleware(server));
|
|
||||||
|
|
||||||
// This lets us open files from the runtime error overlay.
|
|
||||||
app.use(errorOverlayMiddleware());
|
|
||||||
|
|
||||||
// This service worker file is effectively a 'no-op' that will reset any
|
|
||||||
// previous service worker registered for the same host:port combination.
|
|
||||||
// We do this in development to avoid hitting the production cache if
|
|
||||||
// it used the same host and port.
|
|
||||||
// https://github.com/facebook/create-react-app/issues/2272#issuecomment-302832432
|
|
||||||
app.use(noopServiceWorkerMiddleware(paths.publicUrl));
|
|
||||||
},
|
|
||||||
};
|
|
||||||
};
|
|
||||||
15
dev.Dockerfile
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
FROM mcr.microsoft.com/playwright:v1.53.1-noble
|
||||||
|
|
||||||
|
ENV NODE_VERSION 22.14
|
||||||
|
ENV TINI_VERSION v0.19.0
|
||||||
|
|
||||||
|
# Install Node.js
|
||||||
|
RUN curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.2/install.sh | bash && \
|
||||||
|
\. "$HOME/.nvm/nvm.sh" && \
|
||||||
|
nvm install ${NODE_VERSION}
|
||||||
|
|
||||||
|
# Install tini
|
||||||
|
ADD https://github.com/krallin/tini/releases/download/${TINI_VERSION}/tini /sbin/tini
|
||||||
|
RUN chmod +x /sbin/tini
|
||||||
|
# Set tini as the entry point, as node does not properly handle signals
|
||||||
|
ENTRYPOINT ["/sbin/tini", "--"]
|
||||||
2
dist/.gitignore
vendored
@@ -1,2 +0,0 @@
|
|||||||
*
|
|
||||||
!.gitignore
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
version: '3'
|
|
||||||
|
|
||||||
services:
|
|
||||||
shlink_web_client_node:
|
|
||||||
user: 1000:1000
|
|
||||||
volumes:
|
|
||||||
- /etc/passwd:/etc/passwd:ro
|
|
||||||
- /etc/group:/etc/group:ro
|
|
||||||
- ./home:/home/alejandro
|
|
||||||
@@ -1,13 +1,15 @@
|
|||||||
version: '3'
|
|
||||||
|
|
||||||
services:
|
services:
|
||||||
shlink_web_client_node:
|
shlink_web_client_node:
|
||||||
container_name: shlink_web_client_node
|
container_name: shlink_web_client_node
|
||||||
image: node:16.13-alpine
|
user: 1000:1000 # With this, files created via `indocker` script will belong to the host user
|
||||||
command: /bin/sh -c "cd /home/shlink/www && npm install && npm run start"
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: ./dev.Dockerfile
|
||||||
|
working_dir: /home/shlink/www
|
||||||
|
command: /bin/sh -c "npm install && npm run start"
|
||||||
volumes:
|
volumes:
|
||||||
- ./:/home/shlink/www
|
- ./:/home/shlink/www
|
||||||
ports:
|
ports:
|
||||||
- "3000:3000"
|
- "3000:3000"
|
||||||
- "56745:56745"
|
- "56745:56745"
|
||||||
- "5000:5000"
|
- "4173:4173"
|
||||||
|
|||||||
4
eslint.config.js
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
import shlink from '@shlinkio/eslint-config-js-coding-standard';
|
||||||
|
|
||||||
|
/* eslint-disable-next-line no-restricted-exports */
|
||||||
|
export default shlink;
|
||||||
90
index.html
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
||||||
|
<meta name="theme-color" content="#4696e5">
|
||||||
|
|
||||||
|
<!--
|
||||||
|
manifest.json provides metadata used when your web app is added to the
|
||||||
|
homescreen on Android. See https://developers.google.com/web/fundamentals/engage-and-retain/web-app-manifest/
|
||||||
|
-->
|
||||||
|
<link rel="manifest" href="/manifest.json" crossorigin="use-credentials">
|
||||||
|
|
||||||
|
<!-- FavIcon itself -->
|
||||||
|
<link rel="icon" type="image/x-icon" href="/favicon.ico">
|
||||||
|
<link rel="icon" type="image/svg+xml" href="/favicon.svg" sizes="any">
|
||||||
|
<link rel="icon" type="image/png" href="/favicon.png">
|
||||||
|
<link rel="icon" type="image/gif" href="/favicon.gif">
|
||||||
|
<!-- Apple Touch -->
|
||||||
|
<link rel="apple-touch-icon" sizes="16x16" href="/icons/icon-16x16.png">
|
||||||
|
<link rel="apple-touch-icon" sizes="24x24" href="/icons/icon-24x24.png">
|
||||||
|
<link rel="apple-touch-icon" sizes="32x32" href="/icons/icon-32x32.png">
|
||||||
|
<link rel="apple-touch-icon" sizes="40x40" href="/icons/icon-40x40.png">
|
||||||
|
<link rel="apple-touch-icon" sizes="48x48" href="/icons/icon-48x48.png">
|
||||||
|
<link rel="apple-touch-icon" sizes="60x60" href="/icons/icon-60x60.png">
|
||||||
|
<link rel="apple-touch-icon" sizes="64x64" href="/icons/icon-64x64.png">
|
||||||
|
<link rel="apple-touch-icon" sizes="72x72" href="/icons/icon-72x72.png">
|
||||||
|
<link rel="apple-touch-icon" sizes="76x76" href="/icons/icon-76x76.png">
|
||||||
|
<link rel="apple-touch-icon" sizes="96x96" href="/icons/icon-96x96.png">
|
||||||
|
<link rel="apple-touch-icon" sizes="114x114" href="/icons/icon-114x114.png">
|
||||||
|
<link rel="apple-touch-icon" sizes="120x120" href="/icons/icon-120x120.png">
|
||||||
|
<link rel="apple-touch-icon" sizes="128x128" href="/icons/icon-128x128.png">
|
||||||
|
<link rel="apple-touch-icon" sizes="144x144" href="/icons/icon-144x144.png">
|
||||||
|
<link rel="apple-touch-icon" sizes="150x150" href="/icons/icon-150x150.png">
|
||||||
|
<link rel="apple-touch-icon" sizes="152x152" href="/icons/icon-152x152.png">
|
||||||
|
<link rel="apple-touch-icon" sizes="160x160" href="/icons/icon-160x160.png">
|
||||||
|
<link rel="apple-touch-icon" sizes="167x167" href="/icons/icon-167x167.png">
|
||||||
|
<link rel="apple-touch-icon" sizes="180x180" href="/icons/icon-180x180.png">
|
||||||
|
<link rel="apple-touch-icon" sizes="192x192" href="/icons/icon-192x192.png">
|
||||||
|
<link rel="apple-touch-icon" sizes="196x196" href="/icons/icon-196x196.png">
|
||||||
|
<link rel="apple-touch-icon" sizes="228x228" href="/icons/icon-228x228.png">
|
||||||
|
<link rel="apple-touch-icon" sizes="256x256" href="/icons/icon-256x256.png">
|
||||||
|
<link rel="apple-touch-icon" sizes="310x310" href="/icons/icon-310x310.png">
|
||||||
|
<link rel="apple-touch-icon" sizes="384x384" href="/icons/icon-384x384.png">
|
||||||
|
<link rel="apple-touch-icon" sizes="512x512" href="/icons/icon-512x512.png">
|
||||||
|
<link rel="apple-touch-icon" sizes="1024x1024" href="/icons/icon-1024x1024.png">
|
||||||
|
<!-- Normal -->
|
||||||
|
<link rel="icon" type="image/png" sizes="1024x1024" href="/icons/icon-1024x1024.png">
|
||||||
|
<link rel="icon" type="image/png" sizes="512x512" href="/icons/icon-512x512.png">
|
||||||
|
<link rel="icon" type="image/png" sizes="384x384" href="/icons/icon-384x384.png">
|
||||||
|
<link rel="icon" type="image/png" sizes="310x310" href="/icons/icon-310x310.png">
|
||||||
|
<link rel="icon" type="image/png" sizes="256x256" href="/icons/icon-256x256.png">
|
||||||
|
<link rel="icon" type="image/png" sizes="228x228" href="/icons/icon-228x228.png">
|
||||||
|
<link rel="icon" type="image/png" sizes="196x196" href="/icons/icon-196x196.png">
|
||||||
|
<link rel="icon" type="image/png" sizes="192x192" href="/icons/icon-192x192.png">
|
||||||
|
<link rel="icon" type="image/png" sizes="180x180" href="/icons/icon-180x180.png">
|
||||||
|
<link rel="icon" type="image/png" sizes="167x167" href="/icons/icon-167x167.png">
|
||||||
|
<link rel="icon" type="image/png" sizes="160x160" href="/icons/icon-160x160.png">
|
||||||
|
<link rel="icon" type="image/png" sizes="152x152" href="/icons/icon-152x152.png">
|
||||||
|
<link rel="icon" type="image/png" sizes="150x150" href="/icons/icon-150x150.png">
|
||||||
|
<link rel="icon" type="image/png" sizes="144x144" href="/icons/icon-144x144.png">
|
||||||
|
<link rel="icon" type="image/png" sizes="128x128" href="/icons/icon-128x128.png">
|
||||||
|
<link rel="icon" type="image/png" sizes="120x120" href="/icons/icon-120x120.png">
|
||||||
|
<link rel="icon" type="image/png" sizes="114x114" href="/icons/icon-114x114.png">
|
||||||
|
<link rel="icon" type="image/png" sizes="96x96" href="/icons/icon-96x96.png">
|
||||||
|
<link rel="icon" type="image/png" sizes="76x76" href="/icons/icon-76x76.png">
|
||||||
|
<link rel="icon" type="image/png" sizes="72x72" href="/icons/icon-72x72.png">
|
||||||
|
<link rel="icon" type="image/png" sizes="64x64" href="/icons/icon-64x64.png">
|
||||||
|
<link rel="icon" type="image/png" sizes="60x60" href="/icons/icon-60x60.png">
|
||||||
|
<link rel="icon" type="image/png" sizes="48x48" href="/icons/icon-48x48.png">
|
||||||
|
<link rel="icon" type="image/png" sizes="40x40" href="/icons/icon-40x40.png">
|
||||||
|
<link rel="icon" type="image/png" sizes="32x32" href="/icons/icon-32x32.png">
|
||||||
|
<link rel="icon" type="image/png" sizes="24x24" href="/icons/icon-24x24.png">
|
||||||
|
<link rel="icon" type="image/png" sizes="16x16" href="/icons/icon-16x16.png">
|
||||||
|
<!-- MS -->
|
||||||
|
<meta name="msapplication-TileImage" content="/icons/icon-144x144.png">
|
||||||
|
<meta name="msapplication-square70x70logo" content="/icons/icon-70x70.png">
|
||||||
|
<meta name="msapplication-square144x144logo" content="/icons/icon-144x144.png">
|
||||||
|
<meta name="msapplication-square150x150logo" content="/icons/icon-150x150.png">
|
||||||
|
<meta name="msapplication-square310x310logo" content="/icons/icon-310x310.png">
|
||||||
|
<title>Shlink — The URL shortener</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<noscript>
|
||||||
|
You need to enable JavaScript to run this app.
|
||||||
|
</noscript>
|
||||||
|
<div id="root" class="h-full"></div>
|
||||||
|
<script type="module" src="/src/index.tsx"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
2
indocker
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
# Run docker container if it's not up yet
|
# Run docker container if it's not up yet
|
||||||
if ! [[ $(docker ps | grep shlink_web_client_node) ]]; then
|
if ! [[ $(docker ps | grep shlink_web_client_node) ]]; then
|
||||||
docker-compose up -d
|
docker compose up -d
|
||||||
fi
|
fi
|
||||||
|
|
||||||
docker exec -it shlink_web_client_node /bin/sh -c "cd /home/shlink/www && $*"
|
docker exec -it shlink_web_client_node /bin/sh -c "cd /home/shlink/www && $*"
|
||||||
|
|||||||
@@ -1,37 +0,0 @@
|
|||||||
module.exports = {
|
|
||||||
coverageDirectory: '<rootDir>/coverage',
|
|
||||||
collectCoverageFrom: [
|
|
||||||
'src/**/*.{ts,tsx}',
|
|
||||||
'!src/*.{ts,tsx}',
|
|
||||||
'!src/reducers/index.ts',
|
|
||||||
'!src/**/provideServices.ts',
|
|
||||||
'!src/container/*.ts',
|
|
||||||
],
|
|
||||||
coverageThreshold: {
|
|
||||||
global: {
|
|
||||||
statements: 85,
|
|
||||||
branches: 80,
|
|
||||||
functions: 80,
|
|
||||||
lines: 85,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
setupFiles: [ '<rootDir>/config/setupEnzyme.js' ],
|
|
||||||
testMatch: [ '<rootDir>/test/**/*.test.{ts,tsx}' ],
|
|
||||||
testEnvironment: 'jsdom',
|
|
||||||
testURL: 'http://localhost',
|
|
||||||
transform: {
|
|
||||||
'^.+\\.(ts|tsx|js)$': '<rootDir>/node_modules/babel-jest',
|
|
||||||
'^(?!.*\\.(ts|tsx|js|json)$)': '<rootDir>/config/jest/fileTransform.js',
|
|
||||||
},
|
|
||||||
transformIgnorePatterns: [
|
|
||||||
'<rootDir>/.stryker-tmp',
|
|
||||||
'[/\\\\]node_modules[/\\\\].+\\.(js|jsx|ts|tsx)$',
|
|
||||||
'^.+\\.module\\.scss$',
|
|
||||||
],
|
|
||||||
moduleNameMapper: {
|
|
||||||
'^.+\\.module\\.scss$': 'identity-obj-proxy',
|
|
||||||
// Reactstrap module resolution does not work in jest for some reason. Manually mapping it solves the problem
|
|
||||||
'reactstrap': '<rootDir>/node_modules/reactstrap/dist/reactstrap.umd.js',
|
|
||||||
},
|
|
||||||
moduleFileExtensions: [ 'js', 'ts', 'tsx', 'json' ],
|
|
||||||
};
|
|
||||||
147
manifest.ts
Normal file
@@ -0,0 +1,147 @@
|
|||||||
|
import type { ManifestOptions } from 'vite-plugin-pwa';
|
||||||
|
|
||||||
|
export const manifest: Partial<ManifestOptions> = {
|
||||||
|
short_name: 'Shlink',
|
||||||
|
name: 'Shlink',
|
||||||
|
start_url: '/',
|
||||||
|
display: 'standalone',
|
||||||
|
theme_color: '#4696e5',
|
||||||
|
background_color: '#4696e5',
|
||||||
|
icons: [
|
||||||
|
{
|
||||||
|
src: './icons/icon-16x16.png',
|
||||||
|
type: 'image/png',
|
||||||
|
sizes: '16x16',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
src: './icons/icon-24x24.png',
|
||||||
|
type: 'image/png',
|
||||||
|
sizes: '24x24',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
src: './icons/icon-32x32.png',
|
||||||
|
type: 'image/png',
|
||||||
|
sizes: '32x32',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
src: './icons/icon-40x40.png',
|
||||||
|
type: 'image/png',
|
||||||
|
sizes: '40x40',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
src: './icons/icon-48x48.png',
|
||||||
|
type: 'image/png',
|
||||||
|
sizes: '48x48',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
src: './icons/icon-60x60.png',
|
||||||
|
type: 'image/png',
|
||||||
|
sizes: '60x60',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
src: './icons/icon-64x64.png',
|
||||||
|
type: 'image/png',
|
||||||
|
sizes: '64x64',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
src: './icons/icon-72x72.png',
|
||||||
|
type: 'image/png',
|
||||||
|
sizes: '72x72',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
src: './icons/icon-76x76.png',
|
||||||
|
type: 'image/png',
|
||||||
|
sizes: '76x76',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
src: './icons/icon-96x96.png',
|
||||||
|
type: 'image/png',
|
||||||
|
sizes: '96x96',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
src: './icons/icon-114x114.png',
|
||||||
|
type: 'image/png',
|
||||||
|
sizes: '114x114',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
src: './icons/icon-120x120.png',
|
||||||
|
type: 'image/png',
|
||||||
|
sizes: '120x120',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
src: './icons/icon-128x128.png',
|
||||||
|
type: 'image/png',
|
||||||
|
sizes: '128x128',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
src: './icons/icon-144x144.png',
|
||||||
|
type: 'image/png',
|
||||||
|
sizes: '144x144',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
src: './icons/icon-150x150.png',
|
||||||
|
type: 'image/png',
|
||||||
|
sizes: '150x150',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
src: './icons/icon-152x152.png',
|
||||||
|
type: 'image/png',
|
||||||
|
sizes: '152x152',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
src: './icons/icon-160x160.png',
|
||||||
|
type: 'image/png',
|
||||||
|
sizes: '160x160',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
src: './icons/icon-167x167.png',
|
||||||
|
type: 'image/png',
|
||||||
|
sizes: '167x167',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
src: './icons/icon-180x180.png',
|
||||||
|
type: 'image/png',
|
||||||
|
sizes: '180x180',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
src: './icons/icon-192x192.png',
|
||||||
|
type: 'image/png',
|
||||||
|
sizes: '192x192',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
src: './icons/icon-196x196.png',
|
||||||
|
type: 'image/png',
|
||||||
|
sizes: '196x196',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
src: './icons/icon-228x228.png',
|
||||||
|
type: 'image/png',
|
||||||
|
sizes: '228x228',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
src: './icons/icon-256x256.png',
|
||||||
|
type: 'image/png',
|
||||||
|
sizes: '256x256',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
src: './icons/icon-310x310.png',
|
||||||
|
type: 'image/png',
|
||||||
|
sizes: '310x310',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
src: './icons/icon-384x384.png',
|
||||||
|
type: 'image/png',
|
||||||
|
sizes: '384x384',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
src: './icons/icon-512x512.png',
|
||||||
|
type: 'image/png',
|
||||||
|
sizes: '512x512',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
src: './icons/icon-1024x1024.png',
|
||||||
|
type: 'image/png',
|
||||||
|
sizes: '1024x1024',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
64302
package-lock.json
generated
203
package.json
@@ -5,144 +5,81 @@
|
|||||||
"homepage": "",
|
"homepage": "",
|
||||||
"repository": "https://github.com/shlinkio/shlink-web-client",
|
"repository": "https://github.com/shlinkio/shlink-web-client",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"lint": "npm run lint:css && npm run lint:js",
|
"lint": "eslint src test config/test",
|
||||||
"lint:css": "stylelint src/*.scss src/**/*.scss",
|
"lint:fix": "node --run lint -- --fix",
|
||||||
"lint:js": "eslint --ext .js,.ts,.tsx src test",
|
"types": "tsc",
|
||||||
"lint:fix": "npm run lint:css:fix && npm run lint:js:fix",
|
"start": "vite serve --host=0.0.0.0",
|
||||||
"lint:css:fix": "npm run lint:css -- --fix",
|
"preview": "vite preview --host=0.0.0.0",
|
||||||
"lint:js:fix": "npm run lint:js -- --fix",
|
"build": "node --run types && vite build && node scripts/replace-version.mjs",
|
||||||
"start": "node scripts/start.js",
|
"build:dist": "node --run build && node scripts/create-dist-file.mjs",
|
||||||
"serve:build": "serve ./build",
|
"test": "vitest run --run",
|
||||||
"build": "node scripts/build.js && node scripts/replace-version.js",
|
"test:watch": "vitest --watch",
|
||||||
"build:dist": "npm run build && node scripts/create-dist-file.js",
|
"test:ci": "node --run test -- --coverage",
|
||||||
"test": "node scripts/test.js --env=jsdom --colors --verbose",
|
"test:verbose": "node --run test -- --verbose"
|
||||||
"test:coverage": "npm run test -- --coverage --coverageReporters=text --coverageReporters=text-summary",
|
|
||||||
"test:ci": "npm run test:coverage -- --coverageReporters=clover",
|
|
||||||
"test:pretty": "npm run test:coverage -- --coverageReporters=html",
|
|
||||||
"mutate": "./node_modules/.bin/stryker run --concurrency 4"
|
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@fortawesome/fontawesome-free": "^6.0.0",
|
"@fortawesome/fontawesome-free": "^6.7.2",
|
||||||
"@fortawesome/fontawesome-svg-core": "^1.3.0",
|
"@fortawesome/fontawesome-svg-core": "^6.7.2",
|
||||||
"@fortawesome/free-regular-svg-icons": "^6.0.0",
|
"@fortawesome/free-brands-svg-icons": "^6.7.2",
|
||||||
"@fortawesome/free-solid-svg-icons": "^6.0.0",
|
"@fortawesome/free-regular-svg-icons": "^6.7.2",
|
||||||
"@fortawesome/react-fontawesome": "^0.1.17",
|
"@fortawesome/free-solid-svg-icons": "^6.7.2",
|
||||||
"axios": "^0.26.0",
|
"@fortawesome/react-fontawesome": "^0.2.2",
|
||||||
"bootstrap": "^5.1.3",
|
"@json2csv/plainjs": "^7.0.6",
|
||||||
"bottlejs": "^2.0.0",
|
"@reduxjs/toolkit": "^2.8.2",
|
||||||
"bowser": "^2.11.0",
|
"@shlinkio/data-manipulation": "^1.0.3",
|
||||||
"chart.js": "^3.7.1",
|
"@shlinkio/shlink-frontend-kit": "^1.0.0",
|
||||||
"classnames": "^2.3.1",
|
"@shlinkio/shlink-js-sdk": "^2.1.0",
|
||||||
"compare-versions": "^4.1.3",
|
"@shlinkio/shlink-web-component": "^0.15.0",
|
||||||
"csvjson": "^5.1.0",
|
"bottlejs": "^2.0.1",
|
||||||
"date-fns": "^2.28.0",
|
"clsx": "^2.1.1",
|
||||||
"event-source-polyfill": "^1.0.25",
|
"compare-versions": "^6.1.1",
|
||||||
"leaflet": "^1.7.1",
|
"csvtojson": "^2.0.10",
|
||||||
"qs": "^6.9.6",
|
"date-fns": "^4.1.0",
|
||||||
"ramda": "^0.27.2",
|
"react": "^19.1.0",
|
||||||
"react": "^17.0.2",
|
"react-dom": "^19.1.0",
|
||||||
"react-chartjs-2": "^3.3.0",
|
"react-external-link": "^2.5.0",
|
||||||
"react-colorful": "^5.5.1",
|
"react-redux": "^9.2.0",
|
||||||
"react-copy-to-clipboard": "^5.0.4",
|
"react-router": "^7.6.2",
|
||||||
"react-datepicker": "^4.7.0",
|
"redux-localstorage-simple": "^2.5.1",
|
||||||
"react-dom": "^17.0.2",
|
"workbox-core": "^7.3.0",
|
||||||
"react-external-link": "^1.2.2",
|
"workbox-expiration": "^7.3.0",
|
||||||
"react-leaflet": "^3.2.5",
|
"workbox-precaching": "^7.3.0",
|
||||||
"react-redux": "^7.2.6",
|
"workbox-routing": "^7.3.0",
|
||||||
"react-router-dom": "^6.2.2",
|
"workbox-strategies": "^7.3.0"
|
||||||
"react-swipeable": "^6.2.0",
|
|
||||||
"react-tag-autocomplete": "^6.3.0",
|
|
||||||
"reactstrap": "^9.0.1",
|
|
||||||
"redux": "^4.1.2",
|
|
||||||
"redux-localstorage-simple": "^2.4.1",
|
|
||||||
"redux-thunk": "^2.4.1",
|
|
||||||
"uuid": "^8.3.2",
|
|
||||||
"workbox-core": "^6.5.1",
|
|
||||||
"workbox-expiration": "^6.5.1",
|
|
||||||
"workbox-precaching": "^6.5.1",
|
|
||||||
"workbox-routing": "^6.5.1",
|
|
||||||
"workbox-strategies": "^6.5.1"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.17.5",
|
"@shlinkio/eslint-config-js-coding-standard": "~3.5.0",
|
||||||
"@babel/plugin-proposal-nullish-coalescing-operator": "^7.16.7",
|
"@stylistic/eslint-plugin": "^4.4.1",
|
||||||
"@babel/plugin-proposal-optional-chaining": "^7.16.7",
|
"@tailwindcss/vite": "^4.1.10",
|
||||||
"@shlinkio/eslint-config-js-coding-standard": "~1.2.2",
|
"@testing-library/jest-dom": "^6.6.3",
|
||||||
"@stryker-mutator/core": "^5.6.1",
|
"@testing-library/react": "^16.3.0",
|
||||||
"@stryker-mutator/jest-runner": "^5.6.1",
|
"@testing-library/user-event": "^14.6.1",
|
||||||
"@stryker-mutator/typescript-checker": "^5.6.1",
|
"@total-typescript/shoehorn": "^0.1.2",
|
||||||
"@svgr/webpack": "^5.5.0",
|
"@types/react": "^19.1.8",
|
||||||
"@types/classnames": "^2.3.1",
|
"@types/react-dom": "^19.1.6",
|
||||||
"@types/enzyme": "^3.10.11",
|
"@vitejs/plugin-react": "^4.5.2",
|
||||||
"@types/jest": "^27.4.1",
|
"@vitest/browser": "^3.2.4",
|
||||||
"@types/leaflet": "^1.7.9",
|
"@vitest/coverage-v8": "^3.2.4",
|
||||||
"@types/qs": "^6.9.7",
|
"adm-zip": "^0.5.16",
|
||||||
"@types/ramda": "0.27.38",
|
"axe-core": "^4.10.3",
|
||||||
"@types/react": "^17.0.39",
|
"chalk": "^5.4.1",
|
||||||
"@types/react-color": "^3.0.6",
|
"eslint": "^9.29.0",
|
||||||
"@types/react-copy-to-clipboard": "^5.0.2",
|
"eslint-plugin-import": "^2.32.0",
|
||||||
"@types/react-datepicker": "^4.3.4",
|
"eslint-plugin-jsx-a11y": "^6.10.2",
|
||||||
"@types/react-dom": "^17.0.13",
|
"eslint-plugin-react": "^7.37.5",
|
||||||
"@types/react-leaflet": "^2.8.2",
|
"eslint-plugin-react-compiler": "^19.0.0-beta-714736e-20250131",
|
||||||
"@types/react-redux": "^7.1.23",
|
"eslint-plugin-react-hooks": "^5.2.0",
|
||||||
"@types/react-tag-autocomplete": "^6.1.1",
|
"eslint-plugin-simple-import-sort": "^12.1.1",
|
||||||
"@types/uuid": "^8.3.4",
|
"history": "^5.3.0",
|
||||||
"@wojtekmaj/enzyme-adapter-react-17": "0.6.5",
|
"playwright": "^1.53.1",
|
||||||
"adm-zip": "^0.5.9",
|
"tailwindcss": "^4.1.3",
|
||||||
"autoprefixer": "^10.4.2",
|
"typescript": "^5.8.3",
|
||||||
"babel-jest": "^27.5.1",
|
"typescript-eslint": "^8.34.1",
|
||||||
"babel-loader": "^8.2.3",
|
"vite": "^6.3.5",
|
||||||
"babel-plugin-named-asset-import": "^0.3.8",
|
"vite-plugin-pwa": "^1.0.0",
|
||||||
"babel-preset-react-app": "10.0.0",
|
"vitest": "^3.0.5"
|
||||||
"babel-runtime": "^6.26.0",
|
|
||||||
"bfj": "^7.0.2",
|
|
||||||
"case-sensitive-paths-webpack-plugin": "^2.4.0",
|
|
||||||
"chalk": "^4.1.2",
|
|
||||||
"css-loader": "^5.0.1",
|
|
||||||
"dart-sass": "^1.25.0",
|
|
||||||
"dotenv": "^8.2.0",
|
|
||||||
"dotenv-expand": "^5.1.0",
|
|
||||||
"enzyme": "^3.11.0",
|
|
||||||
"eslint": "^7.13.0",
|
|
||||||
"eslint-loader": "^4.0.2",
|
|
||||||
"file-loader": "^6.2.0",
|
|
||||||
"fork-ts-checker-webpack-plugin-alt": "^0.4.14",
|
|
||||||
"fs-extra": "^9.0.1",
|
|
||||||
"html-webpack-plugin": "^4.5.0",
|
|
||||||
"identity-obj-proxy": "^3.0.0",
|
|
||||||
"jest": "^27.5.1",
|
|
||||||
"mini-css-extract-plugin": "^1.3.1",
|
|
||||||
"object-assign": "^4.1.1",
|
|
||||||
"optimize-css-assets-webpack-plugin": "^5.0.4",
|
|
||||||
"pnp-webpack-plugin": "^1.7.0",
|
|
||||||
"postcss": "^8.4.8",
|
|
||||||
"postcss-flexbugs-fixes": "^4.2.1",
|
|
||||||
"postcss-loader": "^3.0.0",
|
|
||||||
"postcss-preset-env": "^6.7.0",
|
|
||||||
"postcss-safe-parser": "^5.0.2",
|
|
||||||
"react-dev-utils": "^11.0.0",
|
|
||||||
"resolve": "^1.22.0",
|
|
||||||
"sass": "^1.49.9",
|
|
||||||
"sass-loader": "^10.1.0",
|
|
||||||
"serve": "^12.0.0",
|
|
||||||
"stryker-cli": "^1.0.2",
|
|
||||||
"style-loader": "^2.0.0",
|
|
||||||
"stylelint": "^13.7.2",
|
|
||||||
"stylelint-config-adidas": "^1.3.0",
|
|
||||||
"stylelint-config-adidas-bem": "^1.2.0",
|
|
||||||
"stylelint-config-recommended-scss": "^4.2.0",
|
|
||||||
"stylelint-scss": "^3.18.0",
|
|
||||||
"sw-precache-webpack-plugin": "^1.0.0",
|
|
||||||
"terser-webpack-plugin": "^4.2.3",
|
|
||||||
"ts-mockery": "^1.2.0",
|
|
||||||
"typescript": "^4.6.2",
|
|
||||||
"url-loader": "^4.1.1",
|
|
||||||
"webpack": "^4.46.0",
|
|
||||||
"webpack-dev-server": "^3.11.3",
|
|
||||||
"webpack-manifest-plugin": "^2.2.0",
|
|
||||||
"whatwg-fetch": "^3.6.2",
|
|
||||||
"workbox-webpack-plugin": "^6.5.1"
|
|
||||||
},
|
},
|
||||||
"browserslist": [
|
"browserslist": [
|
||||||
">0.2%",
|
">0.2%",
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 642 B After Width: | Height: | Size: 662 B |
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 4.4 KiB |
@@ -1 +1,8 @@
|
|||||||
<svg width="512pt" height="512pt" viewBox="0 0 512 512" version="1.1" xmlns="http://www.w3.org/2000/svg"><g fill="#4595e3"><path d=" M 23.71 85.08 C 17.22 49.81 49.44 14.86 85.08 18.12 C 118.83 19.21 145.72 53.33 139.45 86.37 C 155.64 102.30 171.32 118.83 187.87 134.36 C 198.32 111.73 208.84 89.12 219.57 66.62 C 226.05 53.84 243.47 48.74 255.73 56.27 C 263.76 62.10 270.34 69.69 277.25 76.75 C 286.28 86.61 285.72 102.89 276.31 112.31 C 223.38 165.37 170.38 218.37 117.35 271.34 C 107.72 280.99 91.01 281.25 81.11 271.86 C 74.39 264.94 66.82 258.69 61.24 250.77 C 53.72 238.52 58.85 221.07 71.64 214.62 C 94.11 203.87 116.72 193.38 139.33 182.91 C 123.81 166.36 107.30 150.68 91.37 134.49 C 60.20 140.28 27.37 116.78 23.71 85.08 Z" /><path d=" M 205.21 201.23 C 225.32 181.36 260.88 181.11 281.14 200.86 C 299.25 218.75 317.37 236.65 335.10 254.93 C 356.73 278.01 352.01 318.70 326.03 336.56 C 320.07 330.47 313.73 324.65 308.12 318.28 C 323.86 309.39 328.76 286.18 316.63 272.39 C 301.73 256.95 286.30 242.03 271.24 226.75 C 264.49 219.65 256.80 212.00 246.37 211.52 C 224.65 208.64 205.52 233.36 214.49 253.58 C 221.09 266.81 234.22 275.12 243.62 286.24 C 240.43 295.96 238.09 306.13 238.29 316.46 C 225.55 304.29 213.16 291.73 200.89 279.09 C 180.97 257.57 183.10 220.45 205.21 201.23 Z" /><path d=" M 273.90 352.07 C 252.28 328.99 256.98 288.31 282.96 270.46 C 288.93 276.54 295.26 282.36 300.88 288.72 C 285.14 297.62 280.23 320.82 292.38 334.61 C 307.27 350.05 322.70 364.96 337.75 380.25 C 344.51 387.35 352.20 395.00 362.64 395.48 C 384.35 398.37 403.49 373.64 394.51 353.42 C 387.92 340.18 374.78 331.88 365.38 320.76 C 368.56 311.04 370.91 300.86 370.71 290.54 C 383.45 302.70 395.84 315.27 408.11 327.91 C 428.03 349.43 425.90 386.55 403.78 405.77 C 383.68 425.64 348.13 425.89 327.86 406.14 C 309.75 388.25 291.60 370.37 273.90 352.07 Z" /><path d=" M 422.11 403.83 C 431.96 394.07 441.60 384.06 451.66 374.51 C 460.90 383.74 471.89 392.70 474.89 406.11 C 480.16 429.97 484.08 454.13 488.76 478.12 C 490.00 483.41 484.47 488.29 479.35 486.63 C 454.66 481.52 429.55 478.12 405.14 471.84 C 393.17 467.97 385.20 457.75 376.55 449.27 C 386.39 439.49 396.13 429.60 406.06 419.91 C 416.37 433.45 435.74 414.00 422.11 403.83 Z" /></g></svg>
|
<svg width="512pt" height="512pt" viewBox="0 0 512 512" version="1.1" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<g fill="#2078CF">
|
||||||
|
<path d=" M 23.71 85.08 C 17.22 49.81 49.44 14.86 85.08 18.12 C 118.83 19.21 145.72 53.33 139.45 86.37 C 155.64 102.30 171.32 118.83 187.87 134.36 C 198.32 111.73 208.84 89.12 219.57 66.62 C 226.05 53.84 243.47 48.74 255.73 56.27 C 263.76 62.10 270.34 69.69 277.25 76.75 C 286.28 86.61 285.72 102.89 276.31 112.31 C 223.38 165.37 170.38 218.37 117.35 271.34 C 107.72 280.99 91.01 281.25 81.11 271.86 C 74.39 264.94 66.82 258.69 61.24 250.77 C 53.72 238.52 58.85 221.07 71.64 214.62 C 94.11 203.87 116.72 193.38 139.33 182.91 C 123.81 166.36 107.30 150.68 91.37 134.49 C 60.20 140.28 27.37 116.78 23.71 85.08 Z"/>
|
||||||
|
<path d=" M 205.21 201.23 C 225.32 181.36 260.88 181.11 281.14 200.86 C 299.25 218.75 317.37 236.65 335.10 254.93 C 356.73 278.01 352.01 318.70 326.03 336.56 C 320.07 330.47 313.73 324.65 308.12 318.28 C 323.86 309.39 328.76 286.18 316.63 272.39 C 301.73 256.95 286.30 242.03 271.24 226.75 C 264.49 219.65 256.80 212.00 246.37 211.52 C 224.65 208.64 205.52 233.36 214.49 253.58 C 221.09 266.81 234.22 275.12 243.62 286.24 C 240.43 295.96 238.09 306.13 238.29 316.46 C 225.55 304.29 213.16 291.73 200.89 279.09 C 180.97 257.57 183.10 220.45 205.21 201.23 Z"/>
|
||||||
|
<path d=" M 273.90 352.07 C 252.28 328.99 256.98 288.31 282.96 270.46 C 288.93 276.54 295.26 282.36 300.88 288.72 C 285.14 297.62 280.23 320.82 292.38 334.61 C 307.27 350.05 322.70 364.96 337.75 380.25 C 344.51 387.35 352.20 395.00 362.64 395.48 C 384.35 398.37 403.49 373.64 394.51 353.42 C 387.92 340.18 374.78 331.88 365.38 320.76 C 368.56 311.04 370.91 300.86 370.71 290.54 C 383.45 302.70 395.84 315.27 408.11 327.91 C 428.03 349.43 425.90 386.55 403.78 405.77 C 383.68 425.64 348.13 425.89 327.86 406.14 C 309.75 388.25 291.60 370.37 273.90 352.07 Z"/>
|
||||||
|
<path d=" M 422.11 403.83 C 431.96 394.07 441.60 384.06 451.66 374.51 C 460.90 383.74 471.89 392.70 474.89 406.11 C 480.16 429.97 484.08 454.13 488.76 478.12 C 490.00 483.41 484.47 488.29 479.35 486.63 C 454.66 481.52 429.55 478.12 405.14 471.84 C 393.17 467.97 385.20 457.75 376.55 449.27 C 386.39 439.49 396.13 429.60 406.06 419.91 C 416.37 433.45 435.74 414.00 422.11 403.83 Z"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2.2 KiB |
|
Before Width: | Height: | Size: 8.5 KiB After Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 287 B After Width: | Height: | Size: 319 B |
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 2.0 KiB |
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 2.2 KiB |
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 2.2 KiB |
|
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 2.6 KiB |
|
Before Width: | Height: | Size: 381 B After Width: | Height: | Size: 420 B |
|
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 3.0 KiB |
|
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 3.7 KiB |
|
Before Width: | Height: | Size: 437 B After Width: | Height: | Size: 509 B |
|
Before Width: | Height: | Size: 3.2 KiB After Width: | Height: | Size: 4.6 KiB |
|
Before Width: | Height: | Size: 466 B After Width: | Height: | Size: 570 B |
|
Before Width: | Height: | Size: 551 B After Width: | Height: | Size: 642 B |
|
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 6.9 KiB |
|
Before Width: | Height: | Size: 638 B After Width: | Height: | Size: 799 B |
|
Before Width: | Height: | Size: 684 B After Width: | Height: | Size: 834 B |
|
Before Width: | Height: | Size: 750 B After Width: | Height: | Size: 908 B |
|
Before Width: | Height: | Size: 783 B After Width: | Height: | Size: 973 B |
|
Before Width: | Height: | Size: 984 B After Width: | Height: | Size: 1.1 KiB |
@@ -1,109 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
|
||||||
<meta name="theme-color" content="#4696e5">
|
|
||||||
|
|
||||||
<!--
|
|
||||||
manifest.json provides metadata used when your web app is added to the
|
|
||||||
homescreen on Android. See https://developers.google.com/web/fundamentals/engage-and-retain/web-app-manifest/
|
|
||||||
-->
|
|
||||||
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" crossorigin="use-credentials">
|
|
||||||
|
|
||||||
<!-- FavIcon itself -->
|
|
||||||
<link rel="icon" type="image/x-icon" href="%PUBLIC_URL%/favicon.ico">
|
|
||||||
<link rel="icon" type="image/svg+xml" href="%PUBLIC_URL%/favicon.svg" sizes="any">
|
|
||||||
<link rel="icon" type="image/png" href="%PUBLIC_URL%/favicon.png">
|
|
||||||
<link rel="icon" type="image/gif" href="%PUBLIC_URL%/favicon.gif">
|
|
||||||
<!-- Apple Touch -->
|
|
||||||
<link rel="apple-touch-icon" sizes="16x16" href="%PUBLIC_URL%/icons/icon-16x16.png">
|
|
||||||
<link rel="apple-touch-icon" sizes="24x24" href="%PUBLIC_URL%/icons/icon-24x24.png">
|
|
||||||
<link rel="apple-touch-icon" sizes="32x32" href="%PUBLIC_URL%/icons/icon-32x32.png">
|
|
||||||
<link rel="apple-touch-icon" sizes="40x40" href="%PUBLIC_URL%/icons/icon-40x40.png">
|
|
||||||
<link rel="apple-touch-icon" sizes="48x48" href="%PUBLIC_URL%/icons/icon-48x48.png">
|
|
||||||
<link rel="apple-touch-icon" sizes="60x60" href="%PUBLIC_URL%/icons/icon-60x60.png">
|
|
||||||
<link rel="apple-touch-icon" sizes="64x64" href="%PUBLIC_URL%/icons/icon-64x64.png">
|
|
||||||
<link rel="apple-touch-icon" sizes="72x72" href="%PUBLIC_URL%/icons/icon-72x72.png">
|
|
||||||
<link rel="apple-touch-icon" sizes="76x76" href="%PUBLIC_URL%/icons/icon-76x76.png">
|
|
||||||
<link rel="apple-touch-icon" sizes="96x96" href="%PUBLIC_URL%/icons/icon-96x96.png">
|
|
||||||
<link rel="apple-touch-icon" sizes="114x114" href="%PUBLIC_URL%/icons/icon-114x114.png">
|
|
||||||
<link rel="apple-touch-icon" sizes="120x120" href="%PUBLIC_URL%/icons/icon-120x120.png">
|
|
||||||
<link rel="apple-touch-icon" sizes="128x128" href="%PUBLIC_URL%/icons/icon-128x128.png">
|
|
||||||
<link rel="apple-touch-icon" sizes="144x144" href="%PUBLIC_URL%/icons/icon-144x144.png">
|
|
||||||
<link rel="apple-touch-icon" sizes="150x150" href="%PUBLIC_URL%/icons/icon-150x150.png">
|
|
||||||
<link rel="apple-touch-icon" sizes="152x152" href="%PUBLIC_URL%/icons/icon-152x152.png">
|
|
||||||
<link rel="apple-touch-icon" sizes="160x160" href="%PUBLIC_URL%/icons/icon-160x160.png">
|
|
||||||
<link rel="apple-touch-icon" sizes="167x167" href="%PUBLIC_URL%/icons/icon-167x167.png">
|
|
||||||
<link rel="apple-touch-icon" sizes="180x180" href="%PUBLIC_URL%/icons/icon-180x180.png">
|
|
||||||
<link rel="apple-touch-icon" sizes="192x192" href="%PUBLIC_URL%/icons/icon-192x192.png">
|
|
||||||
<link rel="apple-touch-icon" sizes="196x196" href="%PUBLIC_URL%/icons/icon-196x196.png">
|
|
||||||
<link rel="apple-touch-icon" sizes="228x228" href="%PUBLIC_URL%/icons/icon-228x228.png">
|
|
||||||
<link rel="apple-touch-icon" sizes="256x256" href="%PUBLIC_URL%/icons/icon-256x256.png">
|
|
||||||
<link rel="apple-touch-icon" sizes="310x310" href="%PUBLIC_URL%/icons/icon-310x310.png">
|
|
||||||
<link rel="apple-touch-icon" sizes="384x384" href="%PUBLIC_URL%/icons/icon-384x384.png">
|
|
||||||
<link rel="apple-touch-icon" sizes="512x512" href="%PUBLIC_URL%/icons/icon-512x512.png">
|
|
||||||
<link rel="apple-touch-icon" sizes="1024x1024" href="%PUBLIC_URL%/icons/icon-1024x1024.png">
|
|
||||||
<!-- Normal -->
|
|
||||||
<link rel="icon" type="image/png" sizes="1024x1024" href="%PUBLIC_URL%/icons/icon-1024x1024.png">
|
|
||||||
<link rel="icon" type="image/png" sizes="512x512" href="%PUBLIC_URL%/icons/icon-512x512.png">
|
|
||||||
<link rel="icon" type="image/png" sizes="384x384" href="%PUBLIC_URL%/icons/icon-384x384.png">
|
|
||||||
<link rel="icon" type="image/png" sizes="310x310" href="%PUBLIC_URL%/icons/icon-310x310.png">
|
|
||||||
<link rel="icon" type="image/png" sizes="256x256" href="%PUBLIC_URL%/icons/icon-256x256.png">
|
|
||||||
<link rel="icon" type="image/png" sizes="228x228" href="%PUBLIC_URL%/icons/icon-228x228.png">
|
|
||||||
<link rel="icon" type="image/png" sizes="196x196" href="%PUBLIC_URL%/icons/icon-196x196.png">
|
|
||||||
<link rel="icon" type="image/png" sizes="192x192" href="%PUBLIC_URL%/icons/icon-192x192.png">
|
|
||||||
<link rel="icon" type="image/png" sizes="180x180" href="%PUBLIC_URL%/icons/icon-180x180.png">
|
|
||||||
<link rel="icon" type="image/png" sizes="167x167" href="%PUBLIC_URL%/icons/icon-167x167.png">
|
|
||||||
<link rel="icon" type="image/png" sizes="160x160" href="%PUBLIC_URL%/icons/icon-160x160.png">
|
|
||||||
<link rel="icon" type="image/png" sizes="152x152" href="%PUBLIC_URL%/icons/icon-152x152.png">
|
|
||||||
<link rel="icon" type="image/png" sizes="150x150" href="%PUBLIC_URL%/icons/icon-150x150.png">
|
|
||||||
<link rel="icon" type="image/png" sizes="144x144" href="%PUBLIC_URL%/icons/icon-144x144.png">
|
|
||||||
<link rel="icon" type="image/png" sizes="128x128" href="%PUBLIC_URL%/icons/icon-128x128.png">
|
|
||||||
<link rel="icon" type="image/png" sizes="120x120" href="%PUBLIC_URL%/icons/icon-120x120.png">
|
|
||||||
<link rel="icon" type="image/png" sizes="114x114" href="%PUBLIC_URL%/icons/icon-114x114.png">
|
|
||||||
<link rel="icon" type="image/png" sizes="96x96" href="%PUBLIC_URL%/icons/icon-96x96.png">
|
|
||||||
<link rel="icon" type="image/png" sizes="76x76" href="%PUBLIC_URL%/icons/icon-76x76.png">
|
|
||||||
<link rel="icon" type="image/png" sizes="72x72" href="%PUBLIC_URL%/icons/icon-72x72.png">
|
|
||||||
<link rel="icon" type="image/png" sizes="64x64" href="%PUBLIC_URL%/icons/icon-64x64.png">
|
|
||||||
<link rel="icon" type="image/png" sizes="60x60" href="%PUBLIC_URL%/icons/icon-60x60.png">
|
|
||||||
<link rel="icon" type="image/png" sizes="48x48" href="%PUBLIC_URL%/icons/icon-48x48.png">
|
|
||||||
<link rel="icon" type="image/png" sizes="40x40" href="%PUBLIC_URL%/icons/icon-40x40.png">
|
|
||||||
<link rel="icon" type="image/png" sizes="32x32" href="%PUBLIC_URL%/icons/icon-32x32.png">
|
|
||||||
<link rel="icon" type="image/png" sizes="24x24" href="%PUBLIC_URL%/icons/icon-24x24.png">
|
|
||||||
<link rel="icon" type="image/png" sizes="16x16" href="%PUBLIC_URL%/icons/icon-16x16.png">
|
|
||||||
<!-- MS -->
|
|
||||||
<meta name="msapplication-TileImage" content="%PUBLIC_URL%/icons/icon-144x144.png">
|
|
||||||
<meta name="msapplication-square70x70logo" content="%PUBLIC_URL%/icons/icon-70x70.png">
|
|
||||||
<meta name="msapplication-square144x144logo" content="%PUBLIC_URL%/icons/icon-144x144.png">
|
|
||||||
<meta name="msapplication-square150x150logo" content="%PUBLIC_URL%/icons/icon-150x150.png">
|
|
||||||
<meta name="msapplication-square310x310logo" content="%PUBLIC_URL%/icons/icon-310x310.png">
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Notice the use of %PUBLIC_URL% in the tags above.
|
|
||||||
It will be replaced with the URL of the `public` folder during the build.
|
|
||||||
Only files inside the `public` folder can be referenced from the HTML.
|
|
||||||
|
|
||||||
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
|
|
||||||
work correctly both with client-side routing and a non-root public URL.
|
|
||||||
Learn how to configure a non-root public URL by running `npm run build`.
|
|
||||||
-->
|
|
||||||
<title>Shlink — The URL shortener</title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<noscript>
|
|
||||||
You need to enable JavaScript to run this app.
|
|
||||||
</noscript>
|
|
||||||
<div id="root"></div>
|
|
||||||
<!--
|
|
||||||
This HTML file is a template.
|
|
||||||
If you open it directly in the browser, you will see an empty page.
|
|
||||||
|
|
||||||
You can add webfonts, meta tags, or analytics to this file.
|
|
||||||
The build step will place the bundled scripts into the <body> tag.
|
|
||||||
|
|
||||||
To begin the development, run `npm start` or `yarn start`.
|
|
||||||
To create a production bundle, use `npm run build` or `yarn build`.
|
|
||||||
-->
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@@ -1,145 +0,0 @@
|
|||||||
{
|
|
||||||
"short_name": "Shlink",
|
|
||||||
"name": "Shlink",
|
|
||||||
"start_url": "/",
|
|
||||||
"display": "standalone",
|
|
||||||
"theme_color": "#4696e5",
|
|
||||||
"background_color": "#4696e5",
|
|
||||||
"icons": [
|
|
||||||
{
|
|
||||||
"src": "./icons/icon-16x16.png",
|
|
||||||
"type": "image/png",
|
|
||||||
"sizes": "16x16"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"src": "./icons/icon-24x24.png",
|
|
||||||
"type": "image/png",
|
|
||||||
"sizes": "24x24"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"src": "./icons/icon-32x32.png",
|
|
||||||
"type": "image/png",
|
|
||||||
"sizes": "32x32"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"src": "./icons/icon-40x40.png",
|
|
||||||
"type": "image/png",
|
|
||||||
"sizes": "40x40"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"src": "./icons/icon-48x48.png",
|
|
||||||
"type": "image/png",
|
|
||||||
"sizes": "48x48"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"src": "./icons/icon-60x60.png",
|
|
||||||
"type": "image/png",
|
|
||||||
"sizes": "60x60"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"src": "./icons/icon-64x64.png",
|
|
||||||
"type": "image/png",
|
|
||||||
"sizes": "64x64"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"src": "./icons/icon-72x72.png",
|
|
||||||
"type": "image/png",
|
|
||||||
"sizes": "72x72"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"src": "./icons/icon-76x76.png",
|
|
||||||
"type": "image/png",
|
|
||||||
"sizes": "76x76"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"src": "./icons/icon-96x96.png",
|
|
||||||
"type": "image/png",
|
|
||||||
"sizes": "96x96"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"src": "./icons/icon-114x114.png",
|
|
||||||
"type": "image/png",
|
|
||||||
"sizes": "114x114"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"src": "./icons/icon-120x120.png",
|
|
||||||
"type": "image/png",
|
|
||||||
"sizes": "120x120"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"src": "./icons/icon-128x128.png",
|
|
||||||
"type": "image/png",
|
|
||||||
"sizes": "128x128"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"src": "./icons/icon-144x144.png",
|
|
||||||
"type": "image/png",
|
|
||||||
"sizes": "144x144"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"src": "./icons/icon-150x150.png",
|
|
||||||
"type": "image/png",
|
|
||||||
"sizes": "150x150"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"src": "./icons/icon-152x152.png",
|
|
||||||
"type": "image/png",
|
|
||||||
"sizes": "152x152"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"src": "./icons/icon-160x160.png",
|
|
||||||
"type": "image/png",
|
|
||||||
"sizes": "160x160"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"src": "./icons/icon-167x167.png",
|
|
||||||
"type": "image/png",
|
|
||||||
"sizes": "167x167"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"src": "./icons/icon-180x180.png",
|
|
||||||
"type": "image/png",
|
|
||||||
"sizes": "180x180"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"src": "./icons/icon-192x192.png",
|
|
||||||
"type": "image/png",
|
|
||||||
"sizes": "192x192"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"src": "./icons/icon-196x196.png",
|
|
||||||
"type": "image/png",
|
|
||||||
"sizes": "196x196"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"src": "./icons/icon-228x228.png",
|
|
||||||
"type": "image/png",
|
|
||||||
"sizes": "228x228"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"src": "./icons/icon-256x256.png",
|
|
||||||
"type": "image/png",
|
|
||||||
"sizes": "256x256"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"src": "./icons/icon-310x310.png",
|
|
||||||
"type": "image/png",
|
|
||||||
"sizes": "310x310"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"src": "./icons/icon-384x384.png",
|
|
||||||
"type": "image/png",
|
|
||||||
"sizes": "384x384"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"src": "./icons/icon-512x512.png",
|
|
||||||
"type": "image/png",
|
|
||||||
"sizes": "512x512"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"src": "./icons/icon-1024x1024.png",
|
|
||||||
"type": "image/png",
|
|
||||||
"sizes": "1024x1024"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
182
scripts/build.js
@@ -1,182 +0,0 @@
|
|||||||
/* eslint-disable no-console, @typescript-eslint/prefer-nullish-coalescing, @typescript-eslint/promise-function-async, @typescript-eslint/prefer-optional-chain */
|
|
||||||
|
|
||||||
// Do this as the first thing so that any code reading it knows the right env.
|
|
||||||
process.env.BABEL_ENV = 'production';
|
|
||||||
process.env.NODE_ENV = 'production';
|
|
||||||
|
|
||||||
// Makes the script crash on unhandled rejections instead of silently
|
|
||||||
// ignoring them. In the future, promise rejections that are not handled will
|
|
||||||
// terminate the Node.js process with a non-zero exit code.
|
|
||||||
process.on('unhandledRejection', (err) => {
|
|
||||||
throw err;
|
|
||||||
});
|
|
||||||
|
|
||||||
// Ensure environment variables are read.
|
|
||||||
require('../config/env');
|
|
||||||
|
|
||||||
const chalk = require('chalk');
|
|
||||||
const fs = require('fs-extra');
|
|
||||||
const webpack = require('webpack');
|
|
||||||
const bfj = require('bfj');
|
|
||||||
const checkRequiredFiles = require('react-dev-utils/checkRequiredFiles');
|
|
||||||
const formatWebpackMessages = require('react-dev-utils/formatWebpackMessages');
|
|
||||||
const FileSizeReporter = require('react-dev-utils/FileSizeReporter');
|
|
||||||
const printBuildError = require('react-dev-utils/printBuildError');
|
|
||||||
const { checkBrowsers } = require('react-dev-utils/browsersHelper');
|
|
||||||
const paths = require('../config/paths');
|
|
||||||
const configFactory = require('../config/webpack.config');
|
|
||||||
|
|
||||||
const { measureFileSizesBeforeBuild, printFileSizesAfterBuild } = FileSizeReporter;
|
|
||||||
|
|
||||||
// These sizes are pretty large. We'll warn for bundles exceeding them.
|
|
||||||
const WARN_AFTER_BUNDLE_GZIP_SIZE = 512 * 1024; // eslint-disable-line
|
|
||||||
const WARN_AFTER_CHUNK_GZIP_SIZE = 1024 * 1024; // eslint-disable-line
|
|
||||||
|
|
||||||
const isInteractive = process.stdout.isTTY;
|
|
||||||
|
|
||||||
// Warn and crash if required files are missing
|
|
||||||
if (!checkRequiredFiles([ paths.appHtml, paths.appIndexJs ])) {
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Process CLI arguments
|
|
||||||
const argvSliceStart = 2;
|
|
||||||
const argv = process.argv.slice(argvSliceStart);
|
|
||||||
const writeStatsJson = argv.includes('--stats');
|
|
||||||
|
|
||||||
// Generate configuration
|
|
||||||
const config = configFactory('production');
|
|
||||||
|
|
||||||
checkBrowsers(paths.appPath, isInteractive)
|
|
||||||
.then(() =>
|
|
||||||
|
|
||||||
// First, read the current file sizes in build directory.
|
|
||||||
// This lets us display how much they changed later.
|
|
||||||
measureFileSizesBeforeBuild(paths.appBuild))
|
|
||||||
.then((previousFileSizes) => {
|
|
||||||
// Remove all content but keep the directory so that
|
|
||||||
// if you're in it, you don't end up in Trash
|
|
||||||
fs.emptyDirSync(paths.appBuild);
|
|
||||||
|
|
||||||
// Merge with the public folder
|
|
||||||
copyPublicFolder();
|
|
||||||
|
|
||||||
// Start the webpack build
|
|
||||||
return build(previousFileSizes);
|
|
||||||
})
|
|
||||||
.then(
|
|
||||||
({ stats, previousFileSizes, warnings }) => {
|
|
||||||
if (warnings.length) {
|
|
||||||
console.log(chalk.yellow('Compiled with warnings.\n'));
|
|
||||||
console.log(warnings.join('\n\n'));
|
|
||||||
console.log(
|
|
||||||
`\nSearch for the ${
|
|
||||||
chalk.underline(chalk.yellow('keywords'))
|
|
||||||
} to learn more about each warning.`,
|
|
||||||
);
|
|
||||||
console.log(
|
|
||||||
`To ignore, add ${
|
|
||||||
chalk.cyan('// eslint-disable-next-line')
|
|
||||||
} to the line before.\n`,
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
console.log(chalk.green('Compiled successfully.\n'));
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log('File sizes after gzip:\n');
|
|
||||||
printFileSizesAfterBuild(
|
|
||||||
stats,
|
|
||||||
previousFileSizes,
|
|
||||||
paths.appBuild,
|
|
||||||
WARN_AFTER_BUNDLE_GZIP_SIZE,
|
|
||||||
WARN_AFTER_CHUNK_GZIP_SIZE,
|
|
||||||
);
|
|
||||||
console.log();
|
|
||||||
},
|
|
||||||
(err) => {
|
|
||||||
console.log(chalk.red('Failed to compile.\n'));
|
|
||||||
printBuildError(err);
|
|
||||||
process.exit(1);
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.catch((err) => {
|
|
||||||
if (err && err.message) {
|
|
||||||
console.log(err.message);
|
|
||||||
}
|
|
||||||
|
|
||||||
process.exit(1);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Create the production build and print the deployment instructions.
|
|
||||||
function build(previousFileSizes) {
|
|
||||||
console.log('Creating an optimized production build...');
|
|
||||||
|
|
||||||
const compiler = webpack(config);
|
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
compiler.run((err, stats) => {
|
|
||||||
let messages;
|
|
||||||
|
|
||||||
if (err) {
|
|
||||||
if (!err.message) {
|
|
||||||
return reject(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
messages = formatWebpackMessages({
|
|
||||||
errors: [ err.message ],
|
|
||||||
warnings: [],
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
messages = formatWebpackMessages(
|
|
||||||
stats.toJson({ all: false, warnings: true, errors: true }),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (messages.errors.length) {
|
|
||||||
// Only keep the first error. Others are often indicative
|
|
||||||
// of the same problem, but confuse the reader with noise.
|
|
||||||
if (messages.errors.length > 1) {
|
|
||||||
messages.errors.length = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return reject(new Error(messages.errors.join('\n\n')));
|
|
||||||
}
|
|
||||||
if (
|
|
||||||
process.env.CI &&
|
|
||||||
(typeof process.env.CI !== 'string' ||
|
|
||||||
process.env.CI.toLowerCase() !== 'false') &&
|
|
||||||
messages.warnings.length
|
|
||||||
) {
|
|
||||||
console.log(
|
|
||||||
chalk.yellow(
|
|
||||||
'\nTreating warnings as errors because process.env.CI = true.\n' +
|
|
||||||
'Most CI servers set it automatically.\n',
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
return reject(new Error(messages.warnings.join('\n\n')));
|
|
||||||
}
|
|
||||||
|
|
||||||
const resolveArgs = {
|
|
||||||
stats,
|
|
||||||
previousFileSizes,
|
|
||||||
warnings: messages.warnings,
|
|
||||||
};
|
|
||||||
|
|
||||||
if (writeStatsJson) {
|
|
||||||
return bfj // eslint-disable-line promise/no-promise-in-callback
|
|
||||||
.write(`${paths.appBuild}/bundle-stats.json`, stats.toJson())
|
|
||||||
.then(() => resolve(resolveArgs))
|
|
||||||
.catch((error) => reject(new Error(error)));
|
|
||||||
}
|
|
||||||
|
|
||||||
return resolve(resolveArgs);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function copyPublicFolder() {
|
|
||||||
fs.copySync(paths.appPublic, paths.appBuild, {
|
|
||||||
dereference: true,
|
|
||||||
filter: (file) => file !== paths.appHtml,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
/* eslint-disable no-console, @typescript-eslint/prefer-nullish-coalescing, @typescript-eslint/promise-function-async, @typescript-eslint/prefer-optional-chain */
|
|
||||||
|
|
||||||
// Do this as the first thing so that any code reading it knows the right env.
|
|
||||||
process.env.BABEL_ENV = 'production';
|
|
||||||
process.env.NODE_ENV = 'production';
|
|
||||||
|
|
||||||
const chalk = require('chalk');
|
|
||||||
const AdmZip = require('adm-zip');
|
|
||||||
const fs = require('fs-extra');
|
|
||||||
|
|
||||||
function zipDist(version) {
|
|
||||||
const versionFileName = `./dist/shlink-web-client_${version}_dist.zip`;
|
|
||||||
|
|
||||||
console.log(chalk.cyan(`Generating dist file for version ${chalk.bold(version)}...`));
|
|
||||||
const zip = new AdmZip();
|
|
||||||
|
|
||||||
try {
|
|
||||||
if (fs.existsSync(versionFileName)) {
|
|
||||||
fs.unlink(versionFileName);
|
|
||||||
}
|
|
||||||
|
|
||||||
zip.addLocalFolder('./build', `shlink-web-client_${version}_dist`);
|
|
||||||
zip.writeZip(versionFileName);
|
|
||||||
console.log(chalk.green('Dist file properly generated'));
|
|
||||||
} catch (e) {
|
|
||||||
console.log(chalk.red('An error occurred while generating dist file'));
|
|
||||||
console.log(e);
|
|
||||||
}
|
|
||||||
console.log();
|
|
||||||
}
|
|
||||||
|
|
||||||
const version = process.env.VERSION;
|
|
||||||
|
|
||||||
if (version) {
|
|
||||||
zipDist(version);
|
|
||||||
}
|
|
||||||
31
scripts/create-dist-file.mjs
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
import chalk from 'chalk';
|
||||||
|
import AdmZip from 'adm-zip';
|
||||||
|
import fs from 'fs';
|
||||||
|
|
||||||
|
function zipDist(version) {
|
||||||
|
const fileBaseName = `shlink-web-client_${version}_dist`;
|
||||||
|
const versionFileName = `./dist/${fileBaseName}.zip`;
|
||||||
|
|
||||||
|
console.log(chalk.cyan(`Generating dist file for version ${chalk.bold(version)}...`));
|
||||||
|
const zip = new AdmZip();
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (fs.existsSync(versionFileName)) {
|
||||||
|
fs.unlinkSync(versionFileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
zip.addLocalFolder('./build', fileBaseName);
|
||||||
|
zip.writeZip(versionFileName);
|
||||||
|
console.log(chalk.green('Dist file properly generated'));
|
||||||
|
} catch (e) {
|
||||||
|
console.log(chalk.red('An error occurred while generating dist file'));
|
||||||
|
console.log(e);
|
||||||
|
}
|
||||||
|
console.log();
|
||||||
|
}
|
||||||
|
|
||||||
|
const version = process.env.VERSION;
|
||||||
|
|
||||||
|
if (version) {
|
||||||
|
zipDist(version);
|
||||||
|
}
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
set -ex
|
|
||||||
|
|
||||||
PLATFORMS="linux/arm/v7,linux/arm64/v8,linux/amd64"
|
|
||||||
DOCKER_IMAGE="shlinkio/shlink-web-client"
|
|
||||||
|
|
||||||
if [[ "$GITHUB_REF" == *"develop"* ]]; then
|
|
||||||
docker buildx build --push \
|
|
||||||
--platform ${PLATFORMS} \
|
|
||||||
-t ${DOCKER_IMAGE}:latest .
|
|
||||||
|
|
||||||
# If ref is not develop, then this is a tag. Build that docker tag and also "stable"
|
|
||||||
else
|
|
||||||
VERSION=${GITHUB_REF#refs/tags/v}
|
|
||||||
TAGS="-t ${DOCKER_IMAGE}:${VERSION}"
|
|
||||||
|
|
||||||
# Push stable tag only if this is not an alpha or beta release
|
|
||||||
[[ $GITHUB_REF != *"alpha"* && $GITHUB_REF != *"beta"* ]] && TAGS="${TAGS} -t ${DOCKER_IMAGE}:stable"
|
|
||||||
|
|
||||||
docker buildx build --push \
|
|
||||||
--build-arg VERSION=${VERSION} \
|
|
||||||
--platform ${PLATFORMS} \
|
|
||||||
${TAGS} .
|
|
||||||
fi
|
|
||||||
@@ -4,11 +4,14 @@ set -e
|
|||||||
|
|
||||||
ME=$(basename $0)
|
ME=$(basename $0)
|
||||||
|
|
||||||
|
# In order to allow people to pre-configure a server in their shlink-web-client instance via env vars, this function
|
||||||
|
# dumps a servers.json file based on the values provided via env vars
|
||||||
setup_single_shlink_server() {
|
setup_single_shlink_server() {
|
||||||
[ -n "$SHLINK_SERVER_URL" ] || return 0
|
[ -n "$SHLINK_SERVER_URL" ] || return 0
|
||||||
[ -n "$SHLINK_SERVER_API_KEY" ] || return 0
|
[ -n "$SHLINK_SERVER_API_KEY" ] || return 0
|
||||||
local name="${SHLINK_SERVER_NAME:-Shlink}"
|
local name="${SHLINK_SERVER_NAME:-Shlink}"
|
||||||
echo "[{\"name\":\"${name}\",\"url\":\"${SHLINK_SERVER_URL}\",\"apiKey\":\"${SHLINK_SERVER_API_KEY}\"}]" > /usr/share/nginx/html/servers.json
|
local forwardCredentials="${SHLINK_SERVER_FORWARD_CREDENTIALS:-false}"
|
||||||
|
echo "[{\"name\":\"${name}\",\"url\":\"${SHLINK_SERVER_URL}\",\"apiKey\":\"${SHLINK_SERVER_API_KEY}\",\"forwardCredentials\":${forwardCredentials}}]" > /usr/share/nginx/html/servers.json
|
||||||
}
|
}
|
||||||
|
|
||||||
setup_single_shlink_server
|
setup_single_shlink_server
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
const fs = require('fs-extra');
|
import fs from 'fs';
|
||||||
|
|
||||||
function replaceVersionPlaceholder(version) {
|
function replaceVersionPlaceholder(version) {
|
||||||
const staticJsFilesPath = './build/static/js';
|
const staticJsFilesPath = './build/assets';
|
||||||
const versionPlaceholder = '%_VERSION_%';
|
const versionPlaceholder = '%_VERSION_%';
|
||||||
|
|
||||||
const isMainFile = (file) => file.startsWith('main.') && file.endsWith('.js');
|
const isMainFile = (file) => file.startsWith('index-') && file.endsWith('.js');
|
||||||
const [ mainJsFile ] = fs.readdirSync(staticJsFilesPath).filter(isMainFile);
|
const [mainJsFile] = fs.readdirSync(staticJsFilesPath).filter(isMainFile);
|
||||||
const filePath = `${staticJsFilesPath}/${mainJsFile}`;
|
const filePath = `${staticJsFilesPath}/${mainJsFile}`;
|
||||||
const fileContent = fs.readFileSync(filePath, 'utf-8');
|
const fileContent = fs.readFileSync(filePath, 'utf-8');
|
||||||
const replaced = fileContent.replace(versionPlaceholder, version);
|
const replaced = fileContent.replace(versionPlaceholder, version);
|
||||||
125
scripts/start.js
@@ -1,125 +0,0 @@
|
|||||||
/* eslint-disable no-console, @typescript-eslint/prefer-nullish-coalescing, @typescript-eslint/promise-function-async, @typescript-eslint/prefer-optional-chain */
|
|
||||||
|
|
||||||
// Do this as the first thing so that any code reading it knows the right env.
|
|
||||||
process.env.BABEL_ENV = 'development';
|
|
||||||
process.env.NODE_ENV = 'development';
|
|
||||||
|
|
||||||
// Makes the script crash on unhandled rejections instead of silently
|
|
||||||
// ignoring them. In the future, promise rejections that are not handled will
|
|
||||||
// terminate the Node.js process with a non-zero exit code.
|
|
||||||
process.on('unhandledRejection', (err) => {
|
|
||||||
throw err;
|
|
||||||
});
|
|
||||||
|
|
||||||
// Ensure environment variables are read.
|
|
||||||
require('../config/env');
|
|
||||||
|
|
||||||
const fs = require('fs');
|
|
||||||
const chalk = require('chalk');
|
|
||||||
const webpack = require('webpack');
|
|
||||||
const WebpackDevServer = require('webpack-dev-server');
|
|
||||||
const clearConsole = require('react-dev-utils/clearConsole');
|
|
||||||
const checkRequiredFiles = require('react-dev-utils/checkRequiredFiles');
|
|
||||||
const {
|
|
||||||
choosePort,
|
|
||||||
createCompiler,
|
|
||||||
prepareProxy,
|
|
||||||
prepareUrls,
|
|
||||||
} = require('react-dev-utils/WebpackDevServerUtils');
|
|
||||||
const openBrowser = require('react-dev-utils/openBrowser');
|
|
||||||
const { checkBrowsers } = require('react-dev-utils/browsersHelper');
|
|
||||||
const paths = require('../config/paths');
|
|
||||||
const configFactory = require('../config/webpack.config');
|
|
||||||
const createDevServerConfig = require('../config/webpackDevServer.config');
|
|
||||||
|
|
||||||
const useYarn = fs.existsSync(paths.yarnLockFile);
|
|
||||||
const isInteractive = process.stdout.isTTY;
|
|
||||||
|
|
||||||
// Warn and crash if required files are missing
|
|
||||||
if (!checkRequiredFiles([ paths.appHtml, paths.appIndexJs ])) {
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Tools like Cloud9 rely on this.
|
|
||||||
const DEFAULT_PORT = 3000;
|
|
||||||
const PORT = parseInt(process.env.PORT) || DEFAULT_PORT;
|
|
||||||
const HOST = process.env.HOST || '0.0.0.0';
|
|
||||||
|
|
||||||
if (process.env.HOST) {
|
|
||||||
console.log(
|
|
||||||
chalk.cyan(
|
|
||||||
`Attempting to bind to HOST environment variable: ${chalk.yellow(
|
|
||||||
chalk.bold(process.env.HOST),
|
|
||||||
)}`,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
console.log(
|
|
||||||
'If this was unintentional, check that you haven\'t mistakenly set it in your shell.',
|
|
||||||
);
|
|
||||||
console.log(
|
|
||||||
`Learn more here: ${chalk.yellow('http://bit.ly/CRA-advanced-config')}`,
|
|
||||||
);
|
|
||||||
console.log();
|
|
||||||
}
|
|
||||||
|
|
||||||
checkBrowsers(paths.appPath, isInteractive)
|
|
||||||
.then(() =>
|
|
||||||
|
|
||||||
// We attempt to use the default port but if it is busy, we offer the user to
|
|
||||||
// run on a different port. `choosePort()` Promise resolves to the next free port.
|
|
||||||
choosePort(HOST, PORT))
|
|
||||||
.then((port) => {
|
|
||||||
if (port === null) {
|
|
||||||
// We have not found a port.
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const config = configFactory('development');
|
|
||||||
const protocol = process.env.HTTPS === 'true' ? 'https' : 'http';
|
|
||||||
const appName = require(paths.appPackageJson).name;
|
|
||||||
|
|
||||||
const urls = prepareUrls(protocol, HOST, port);
|
|
||||||
|
|
||||||
// Create a webpack compiler that is configured with custom messages.
|
|
||||||
const compiler = createCompiler({ webpack, config, appName, urls, useYarn });
|
|
||||||
|
|
||||||
// Load proxy config
|
|
||||||
const proxySetting = require(paths.appPackageJson).proxy;
|
|
||||||
|
|
||||||
const proxyConfig = prepareProxy(proxySetting, paths.appPublic);
|
|
||||||
|
|
||||||
// Serve webpack assets generated by the compiler over a web server.
|
|
||||||
const serverConfig = createDevServerConfig(
|
|
||||||
proxyConfig,
|
|
||||||
urls.lanUrlForConfig,
|
|
||||||
);
|
|
||||||
const devServer = new WebpackDevServer(compiler, serverConfig);
|
|
||||||
|
|
||||||
// Launch WebpackDevServer.
|
|
||||||
devServer.listen(port, HOST, (err) => {
|
|
||||||
if (err) {
|
|
||||||
return console.log(err);
|
|
||||||
}
|
|
||||||
if (isInteractive) {
|
|
||||||
clearConsole();
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log(chalk.cyan('Starting the development server...\n'));
|
|
||||||
|
|
||||||
return openBrowser(urls.localUrlForBrowser);
|
|
||||||
});
|
|
||||||
|
|
||||||
[ 'SIGINT', 'SIGTERM' ].forEach((sig) => {
|
|
||||||
process.on(sig, () => {
|
|
||||||
devServer.close();
|
|
||||||
process.exit();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
if (err && err.message) {
|
|
||||||
console.log(err.message);
|
|
||||||
}
|
|
||||||
|
|
||||||
process.exit(1);
|
|
||||||
});
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
|
|
||||||
// Do this as the first thing so that any code reading it knows the right env.
|
|
||||||
process.env.BABEL_ENV = 'test';
|
|
||||||
process.env.NODE_ENV = 'test';
|
|
||||||
process.env.PUBLIC_URL = '';
|
|
||||||
|
|
||||||
// Makes the script crash on unhandled rejections instead of silently
|
|
||||||
// ignoring them. In the future, promise rejections that are not handled will
|
|
||||||
// terminate the Node.js process with a non-zero exit code.
|
|
||||||
process.on('unhandledRejection', (err) => {
|
|
||||||
throw err;
|
|
||||||
});
|
|
||||||
|
|
||||||
// Ensure environment variables are read.
|
|
||||||
require('../config/env');
|
|
||||||
|
|
||||||
// Make tests to be matched inside tests folder
|
|
||||||
const jest = require('jest');
|
|
||||||
|
|
||||||
const argumentsToRemove = 2;
|
|
||||||
const argv = process.argv.slice(argumentsToRemove);
|
|
||||||
|
|
||||||
jest.run(argv);
|
|
||||||
16
shlink-web-client.d.ts
vendored
@@ -1,16 +1,6 @@
|
|||||||
declare module 'event-source-polyfill' {
|
declare module '@json2csv/plainjs' {
|
||||||
declare class EventSourcePolyfill {
|
export class Parser {
|
||||||
public onmessage?: ({ data }: { data: string }) => void;
|
parse: <T>(data: T[]) => string;
|
||||||
public onerror?: ({ status }: { status: number }) => void;
|
|
||||||
public close: () => void;
|
|
||||||
public constructor(hubUrl: URL, options?: any);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
declare module 'csvjson' {
|
|
||||||
export declare class CsvJson {
|
|
||||||
public toObject<T>(content: string): T[];
|
|
||||||
public toCSV<T>(data: T[], options: { headers: 'full' | 'none' | 'relative' | 'key'; wrap?: true }): string;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 2.3 MiB After Width: | Height: | Size: 2.8 MiB |
@@ -1,16 +0,0 @@
|
|||||||
import { ProblemDetailsError } from './types';
|
|
||||||
import { isInvalidArgumentError } from './utils';
|
|
||||||
|
|
||||||
export interface ShlinkApiErrorProps {
|
|
||||||
errorData?: ProblemDetailsError;
|
|
||||||
fallbackMessage?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const ShlinkApiError = ({ errorData, fallbackMessage }: ShlinkApiErrorProps) => (
|
|
||||||
<>
|
|
||||||
{errorData?.detail ?? fallbackMessage}
|
|
||||||
{isInvalidArgumentError(errorData) &&
|
|
||||||
<p className="mb-0">Invalid elements: [{errorData.invalidElements.join(', ')}]</p>
|
|
||||||
}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
@@ -1,137 +0,0 @@
|
|||||||
import { isEmpty, isNil, reject } from 'ramda';
|
|
||||||
import { AxiosInstance, AxiosResponse, Method } from 'axios';
|
|
||||||
import { ShortUrl, ShortUrlData } from '../../short-urls/data';
|
|
||||||
import { OptionalString } from '../../utils/utils';
|
|
||||||
import {
|
|
||||||
ShlinkHealth,
|
|
||||||
ShlinkMercureInfo,
|
|
||||||
ShlinkShortUrlsResponse,
|
|
||||||
ShlinkTags,
|
|
||||||
ShlinkTagsResponse,
|
|
||||||
ShlinkVisits,
|
|
||||||
ShlinkVisitsParams,
|
|
||||||
ShlinkShortUrlData,
|
|
||||||
ShlinkDomainsResponse,
|
|
||||||
ShlinkVisitsOverview,
|
|
||||||
ShlinkEditDomainRedirects,
|
|
||||||
ShlinkDomainRedirects,
|
|
||||||
ShlinkShortUrlsListParams,
|
|
||||||
ShlinkShortUrlsListNormalizedParams,
|
|
||||||
} from '../types';
|
|
||||||
import { stringifyQuery } from '../../utils/helpers/query';
|
|
||||||
import { orderToString } from '../../utils/helpers/ordering';
|
|
||||||
|
|
||||||
const buildShlinkBaseUrl = (url: string) => url ? `${url}/rest/v2` : '';
|
|
||||||
const rejectNilProps = reject(isNil);
|
|
||||||
const normalizeOrderByInParams = (params: ShlinkShortUrlsListParams): ShlinkShortUrlsListNormalizedParams => {
|
|
||||||
const { orderBy = {}, ...rest } = params;
|
|
||||||
|
|
||||||
return { ...rest, orderBy: orderToString(orderBy) };
|
|
||||||
};
|
|
||||||
|
|
||||||
export default class ShlinkApiClient {
|
|
||||||
public constructor(
|
|
||||||
private readonly axios: AxiosInstance,
|
|
||||||
private readonly baseUrl: string,
|
|
||||||
private readonly apiKey: string,
|
|
||||||
) {
|
|
||||||
}
|
|
||||||
|
|
||||||
public readonly listShortUrls = async (params: ShlinkShortUrlsListParams = {}): Promise<ShlinkShortUrlsResponse> =>
|
|
||||||
this.performRequest<{ shortUrls: ShlinkShortUrlsResponse }>('/short-urls', 'GET', normalizeOrderByInParams(params))
|
|
||||||
.then(({ data }) => data.shortUrls);
|
|
||||||
|
|
||||||
public readonly createShortUrl = async (options: ShortUrlData): Promise<ShortUrl> => {
|
|
||||||
const filteredOptions = reject((value) => isEmpty(value) || isNil(value), options as any);
|
|
||||||
|
|
||||||
return this.performRequest<ShortUrl>('/short-urls', 'POST', {}, filteredOptions)
|
|
||||||
.then((resp) => resp.data);
|
|
||||||
};
|
|
||||||
|
|
||||||
public readonly getShortUrlVisits = async (shortCode: string, query?: ShlinkVisitsParams): Promise<ShlinkVisits> =>
|
|
||||||
this.performRequest<{ visits: ShlinkVisits }>(`/short-urls/${shortCode}/visits`, 'GET', query)
|
|
||||||
.then(({ data }) => data.visits);
|
|
||||||
|
|
||||||
public readonly getTagVisits = async (tag: string, query?: Omit<ShlinkVisitsParams, 'domain'>): Promise<ShlinkVisits> =>
|
|
||||||
this.performRequest<{ visits: ShlinkVisits }>(`/tags/${tag}/visits`, 'GET', query)
|
|
||||||
.then(({ data }) => data.visits);
|
|
||||||
|
|
||||||
public readonly getOrphanVisits = async (query?: Omit<ShlinkVisitsParams, 'domain'>): Promise<ShlinkVisits> =>
|
|
||||||
this.performRequest<{ visits: ShlinkVisits }>('/visits/orphan', 'GET', query)
|
|
||||||
.then(({ data }) => data.visits);
|
|
||||||
|
|
||||||
public readonly getNonOrphanVisits = async (query?: Omit<ShlinkVisitsParams, 'domain'>): Promise<ShlinkVisits> =>
|
|
||||||
this.performRequest<{ visits: ShlinkVisits }>('/visits/non-orphan', 'GET', query)
|
|
||||||
.then(({ data }) => data.visits);
|
|
||||||
|
|
||||||
public readonly getVisitsOverview = async (): Promise<ShlinkVisitsOverview> =>
|
|
||||||
this.performRequest<{ visits: ShlinkVisitsOverview }>('/visits', 'GET')
|
|
||||||
.then(({ data }) => data.visits);
|
|
||||||
|
|
||||||
public readonly getShortUrl = async (shortCode: string, domain?: OptionalString): Promise<ShortUrl> =>
|
|
||||||
this.performRequest<ShortUrl>(`/short-urls/${shortCode}`, 'GET', { domain })
|
|
||||||
.then(({ data }) => data);
|
|
||||||
|
|
||||||
public readonly deleteShortUrl = async (shortCode: string, domain?: OptionalString): Promise<void> =>
|
|
||||||
this.performRequest(`/short-urls/${shortCode}`, 'DELETE', { domain })
|
|
||||||
.then(() => {});
|
|
||||||
|
|
||||||
// eslint-disable-next-line valid-jsdoc
|
|
||||||
/**
|
|
||||||
* @deprecated. If using Shlink 2.6.0 or greater, use updateShortUrl instead
|
|
||||||
*/
|
|
||||||
public readonly updateShortUrlTags = async (
|
|
||||||
shortCode: string,
|
|
||||||
domain: OptionalString,
|
|
||||||
tags: string[],
|
|
||||||
): Promise<string[]> =>
|
|
||||||
this.performRequest<{ tags: string[] }>(`/short-urls/${shortCode}/tags`, 'PUT', { domain }, { tags })
|
|
||||||
.then(({ data }) => data.tags);
|
|
||||||
|
|
||||||
public readonly updateShortUrl = async (
|
|
||||||
shortCode: string,
|
|
||||||
domain: OptionalString,
|
|
||||||
data: ShlinkShortUrlData,
|
|
||||||
): Promise<ShortUrl> =>
|
|
||||||
this.performRequest<ShortUrl>(`/short-urls/${shortCode}`, 'PATCH', { domain }, data)
|
|
||||||
.then(({ data }) => data);
|
|
||||||
|
|
||||||
public readonly listTags = async (): Promise<ShlinkTags> =>
|
|
||||||
this.performRequest<{ tags: ShlinkTagsResponse }>('/tags', 'GET', { withStats: 'true' })
|
|
||||||
.then((resp) => resp.data.tags)
|
|
||||||
.then(({ data, stats }) => ({ tags: data, stats }));
|
|
||||||
|
|
||||||
public readonly deleteTags = async (tags: string[]): Promise<{ tags: string[] }> =>
|
|
||||||
this.performRequest('/tags', 'DELETE', { tags })
|
|
||||||
.then(() => ({ tags }));
|
|
||||||
|
|
||||||
public readonly editTag = async (oldName: string, newName: string): Promise<{ oldName: string; newName: string }> =>
|
|
||||||
this.performRequest('/tags', 'PUT', {}, { oldName, newName })
|
|
||||||
.then(() => ({ oldName, newName }));
|
|
||||||
|
|
||||||
public readonly health = async (): Promise<ShlinkHealth> =>
|
|
||||||
this.performRequest<ShlinkHealth>('/health', 'GET')
|
|
||||||
.then((resp) => resp.data);
|
|
||||||
|
|
||||||
public readonly mercureInfo = async (): Promise<ShlinkMercureInfo> =>
|
|
||||||
this.performRequest<ShlinkMercureInfo>('/mercure-info', 'GET')
|
|
||||||
.then((resp) => resp.data);
|
|
||||||
|
|
||||||
public readonly listDomains = async (): Promise<ShlinkDomainsResponse> =>
|
|
||||||
this.performRequest<{ domains: ShlinkDomainsResponse }>('/domains', 'GET').then(({ data }) => data.domains);
|
|
||||||
|
|
||||||
public readonly editDomainRedirects = async (
|
|
||||||
domainRedirects: ShlinkEditDomainRedirects,
|
|
||||||
): Promise<ShlinkDomainRedirects> =>
|
|
||||||
this.performRequest<ShlinkDomainRedirects>('/domains/redirects', 'PATCH', {}, domainRedirects).then(({ data }) => data);
|
|
||||||
|
|
||||||
private readonly performRequest = async <T>(url: string, method: Method = 'GET', query = {}, body = {}): Promise<AxiosResponse<T>> =>
|
|
||||||
this.axios({
|
|
||||||
method,
|
|
||||||
url: `${buildShlinkBaseUrl(this.baseUrl)}${url}`,
|
|
||||||
headers: { 'X-Api-Key': this.apiKey },
|
|
||||||
params: rejectNilProps(query),
|
|
||||||
data: body,
|
|
||||||
paramsSerializer: stringifyQuery,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
@@ -1,36 +1,40 @@
|
|||||||
import { AxiosInstance } from 'axios';
|
import type { HttpClient } from '@shlinkio/shlink-js-sdk';
|
||||||
import { prop } from 'ramda';
|
import { ShlinkApiClient } from '@shlinkio/shlink-js-sdk';
|
||||||
import { hasServerData, SelectedServer, ServerWithId } from '../../servers/data';
|
import type { GetState } from '../../container/types';
|
||||||
import { GetState } from '../../container/types';
|
import type { ServerWithId } from '../../servers/data';
|
||||||
import ShlinkApiClient from './ShlinkApiClient';
|
import { hasServerData } from '../../servers/data';
|
||||||
|
|
||||||
const apiClients: Record<string, ShlinkApiClient> = {};
|
const apiClients: Map<string, ShlinkApiClient> = new Map();
|
||||||
|
|
||||||
const isGetState = (getStateOrSelectedServer: GetState | ServerWithId): getStateOrSelectedServer is GetState =>
|
const isGetState = (getStateOrSelectedServer: GetState | ServerWithId): getStateOrSelectedServer is GetState =>
|
||||||
typeof getStateOrSelectedServer === 'function';
|
typeof getStateOrSelectedServer === 'function';
|
||||||
const getSelectedServerFromState = (getState: GetState): SelectedServer => prop('selectedServer', getState());
|
const getSelectedServerFromState = (getState: GetState): ServerWithId => {
|
||||||
|
const { selectedServer } = getState();
|
||||||
export type ShlinkApiClientBuilder = (getStateOrSelectedServer: GetState | ServerWithId) => ShlinkApiClient;
|
if (!hasServerData(selectedServer)) {
|
||||||
|
|
||||||
const buildShlinkApiClient = (axios: AxiosInstance): ShlinkApiClientBuilder => (
|
|
||||||
getStateOrSelectedServer: GetState | ServerWithId,
|
|
||||||
) => {
|
|
||||||
const server = isGetState(getStateOrSelectedServer)
|
|
||||||
? getSelectedServerFromState(getStateOrSelectedServer)
|
|
||||||
: getStateOrSelectedServer;
|
|
||||||
|
|
||||||
if (!hasServerData(server)) {
|
|
||||||
throw new Error('There\'s no selected server or it is not found');
|
throw new Error('There\'s no selected server or it is not found');
|
||||||
}
|
}
|
||||||
|
|
||||||
const { url, apiKey } = server;
|
return selectedServer;
|
||||||
const clientKey = `${url}_${apiKey}`;
|
|
||||||
|
|
||||||
if (!apiClients[clientKey]) {
|
|
||||||
apiClients[clientKey] = new ShlinkApiClient(axios, url, apiKey);
|
|
||||||
}
|
|
||||||
|
|
||||||
return apiClients[clientKey];
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default buildShlinkApiClient;
|
export const buildShlinkApiClient = (httpClient: HttpClient) => (getStateOrSelectedServer: GetState | ServerWithId) => {
|
||||||
|
const { url: baseUrl, apiKey, forwardCredentials } = isGetState(getStateOrSelectedServer)
|
||||||
|
? getSelectedServerFromState(getStateOrSelectedServer)
|
||||||
|
: getStateOrSelectedServer;
|
||||||
|
const serverKey = `${apiKey}_${baseUrl}_${forwardCredentials ? 'forward' : 'no-forward'}`;
|
||||||
|
const existingApiClient = apiClients.get(serverKey);
|
||||||
|
|
||||||
|
if (existingApiClient) {
|
||||||
|
return existingApiClient;
|
||||||
|
}
|
||||||
|
|
||||||
|
const apiClient = new ShlinkApiClient(
|
||||||
|
httpClient,
|
||||||
|
{ apiKey, baseUrl },
|
||||||
|
{ requestCredentials: forwardCredentials ? 'include' : undefined },
|
||||||
|
);
|
||||||
|
apiClients.set(serverKey, apiClient);
|
||||||
|
return apiClient;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ShlinkApiClientBuilder = ReturnType<typeof buildShlinkApiClient>;
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
import Bottle from 'bottlejs';
|
import type Bottle from 'bottlejs';
|
||||||
import buildShlinkApiClient from './ShlinkApiClientBuilder';
|
import { buildShlinkApiClient } from './ShlinkApiClientBuilder';
|
||||||
|
|
||||||
const provideServices = (bottle: Bottle) => {
|
export const provideServices = (bottle: Bottle) => {
|
||||||
bottle.serviceFactory('buildShlinkApiClient', buildShlinkApiClient, 'axios');
|
bottle.serviceFactory('buildShlinkApiClient', buildShlinkApiClient, 'HttpClient');
|
||||||
};
|
};
|
||||||
|
|
||||||
export default provideServices;
|
|
||||||
|
|||||||
@@ -1,6 +0,0 @@
|
|||||||
import { Action } from 'redux';
|
|
||||||
import { ProblemDetailsError } from './index';
|
|
||||||
|
|
||||||
export interface ApiErrorAction extends Action<string> {
|
|
||||||
errorData?: ProblemDetailsError;
|
|
||||||
}
|
|
||||||
@@ -1,122 +0,0 @@
|
|||||||
import { Visit } from '../../visits/types';
|
|
||||||
import { OptionalString } from '../../utils/utils';
|
|
||||||
import { ShortUrl, ShortUrlMeta, ShortUrlsOrder } from '../../short-urls/data';
|
|
||||||
|
|
||||||
export interface ShlinkShortUrlsResponse {
|
|
||||||
data: ShortUrl[];
|
|
||||||
pagination: ShlinkPaginator;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ShlinkMercureInfo {
|
|
||||||
token: string;
|
|
||||||
mercureHubUrl: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ShlinkHealth {
|
|
||||||
status: 'pass' | 'fail';
|
|
||||||
version: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ShlinkTagsStats {
|
|
||||||
tag: string;
|
|
||||||
shortUrlsCount: number;
|
|
||||||
visitsCount: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ShlinkTags {
|
|
||||||
tags: string[];
|
|
||||||
stats: ShlinkTagsStats[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ShlinkTagsResponse {
|
|
||||||
data: string[];
|
|
||||||
stats: ShlinkTagsStats[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ShlinkPaginator {
|
|
||||||
currentPage: number;
|
|
||||||
pagesCount: number;
|
|
||||||
totalItems: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ShlinkVisits {
|
|
||||||
data: Visit[];
|
|
||||||
pagination: ShlinkPaginator;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ShlinkVisitsOverview {
|
|
||||||
visitsCount: number;
|
|
||||||
orphanVisitsCount?: number; // Optional only for versions older than 2.6.0
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ShlinkVisitsParams {
|
|
||||||
domain?: OptionalString;
|
|
||||||
page?: number;
|
|
||||||
itemsPerPage?: number;
|
|
||||||
startDate?: string;
|
|
||||||
endDate?: string;
|
|
||||||
excludeBots?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ShlinkShortUrlData extends ShortUrlMeta {
|
|
||||||
longUrl?: string;
|
|
||||||
title?: string;
|
|
||||||
validateUrl?: boolean;
|
|
||||||
tags?: string[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ShlinkDomainRedirects {
|
|
||||||
baseUrlRedirect: string | null;
|
|
||||||
regular404Redirect: string | null;
|
|
||||||
invalidShortUrlRedirect: string | null;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ShlinkEditDomainRedirects extends Partial<ShlinkDomainRedirects> {
|
|
||||||
domain: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ShlinkDomain {
|
|
||||||
domain: string;
|
|
||||||
isDefault: boolean;
|
|
||||||
redirects?: ShlinkDomainRedirects; // Optional only for Shlink older than 2.8
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ShlinkDomainsResponse {
|
|
||||||
data: ShlinkDomain[];
|
|
||||||
defaultRedirects?: ShlinkDomainRedirects; // Optional only for Shlink older than 2.10
|
|
||||||
}
|
|
||||||
|
|
||||||
export type TagsFilteringMode = 'all' | 'any';
|
|
||||||
|
|
||||||
export interface ShlinkShortUrlsListParams {
|
|
||||||
page?: string;
|
|
||||||
itemsPerPage?: number;
|
|
||||||
tags?: string[];
|
|
||||||
searchTerm?: string;
|
|
||||||
startDate?: string;
|
|
||||||
endDate?: string;
|
|
||||||
orderBy?: ShortUrlsOrder;
|
|
||||||
tagsMode?: TagsFilteringMode;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ShlinkShortUrlsListNormalizedParams extends Omit<ShlinkShortUrlsListParams, 'orderBy'> {
|
|
||||||
orderBy?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ProblemDetailsError {
|
|
||||||
type: string;
|
|
||||||
detail: string;
|
|
||||||
title: string;
|
|
||||||
status: number;
|
|
||||||
[extraProps: string]: any;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface InvalidArgumentError extends ProblemDetailsError {
|
|
||||||
type: 'INVALID_ARGUMENT';
|
|
||||||
invalidElements: string[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface InvalidShortUrlDeletion extends ProblemDetailsError {
|
|
||||||
type: 'INVALID_SHORTCODE_DELETION' | 'INVALID_SHORT_URL_DELETION';
|
|
||||||
threshold: number;
|
|
||||||
}
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
import { AxiosError } from 'axios';
|
|
||||||
import { InvalidArgumentError, InvalidShortUrlDeletion, ProblemDetailsError } from '../types';
|
|
||||||
|
|
||||||
export const parseApiError = (e: AxiosError<ProblemDetailsError>) => e.response?.data;
|
|
||||||
|
|
||||||
export const isInvalidArgumentError = (error?: ProblemDetailsError): error is InvalidArgumentError =>
|
|
||||||
error?.type === 'INVALID_ARGUMENT';
|
|
||||||
|
|
||||||
export const isInvalidDeletionError = (error?: ProblemDetailsError): error is InvalidShortUrlDeletion =>
|
|
||||||
error?.type === 'INVALID_SHORTCODE_DELETION' || error?.type === 'INVALID_SHORT_URL_DELETION';
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
@import '../utils/base';
|
|
||||||
|
|
||||||
.app-container {
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.app {
|
|
||||||
padding-top: $headerHeight;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.shlink-wrapper {
|
|
||||||
min-height: 100%;
|
|
||||||
padding-bottom: $footer-height + $footer-margin;
|
|
||||||
margin-bottom: -($footer-height + $footer-margin);
|
|
||||||
}
|
|
||||||
|
|
||||||
.shlink-footer {
|
|
||||||
height: $footer-height;
|
|
||||||
margin-top: $footer-margin;
|
|
||||||
padding: 0;
|
|
||||||
|
|
||||||
@media (min-width: $mdMin) {
|
|
||||||
padding: 0 15px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
104
src/app/App.tsx
@@ -1,69 +1,109 @@
|
|||||||
import { useEffect, FC } from 'react';
|
import { changeThemeInMarkup, getSystemPreferredTheme } from '@shlinkio/shlink-frontend-kit';
|
||||||
import { Route, Routes, useLocation } from 'react-router-dom';
|
import type { Settings } from '@shlinkio/shlink-web-component/settings';
|
||||||
import classNames from 'classnames';
|
import { clsx } from 'clsx';
|
||||||
import NotFound from '../common/NotFound';
|
import type { FC } from 'react';
|
||||||
import { ServersMap } from '../servers/data';
|
import { useEffect, useRef } from 'react';
|
||||||
import { Settings } from '../settings/reducers/settings';
|
import { Route, Routes, useLocation } from 'react-router';
|
||||||
import { changeThemeInMarkup } from '../utils/theme';
|
|
||||||
import { AppUpdateBanner } from '../common/AppUpdateBanner';
|
import { AppUpdateBanner } from '../common/AppUpdateBanner';
|
||||||
|
import { NotFound } from '../common/NotFound';
|
||||||
|
import type { FCWithDeps } from '../container/utils';
|
||||||
|
import { componentFactory, useDependencies } from '../container/utils';
|
||||||
|
import type { ServersMap } from '../servers/data';
|
||||||
import { forceUpdate } from '../utils/helpers/sw';
|
import { forceUpdate } from '../utils/helpers/sw';
|
||||||
import './App.scss';
|
|
||||||
|
|
||||||
interface AppProps {
|
type AppProps = {
|
||||||
fetchServers: () => void;
|
fetchServers: () => void;
|
||||||
servers: ServersMap;
|
servers: ServersMap;
|
||||||
settings: Settings;
|
settings: Settings;
|
||||||
resetAppUpdate: () => void;
|
resetAppUpdate: () => void;
|
||||||
appUpdated: boolean;
|
appUpdated: boolean;
|
||||||
}
|
};
|
||||||
|
|
||||||
|
type AppDeps = {
|
||||||
|
MainHeader: FC;
|
||||||
|
Home: FC;
|
||||||
|
ShlinkWebComponentContainer: FC;
|
||||||
|
CreateServer: FC;
|
||||||
|
EditServer: FC;
|
||||||
|
Settings: FC;
|
||||||
|
ManageServers: FC;
|
||||||
|
ShlinkVersionsContainer: FC;
|
||||||
|
};
|
||||||
|
|
||||||
|
const App: FCWithDeps<AppProps, AppDeps> = (
|
||||||
|
{ fetchServers, servers, settings, appUpdated, resetAppUpdate },
|
||||||
|
) => {
|
||||||
|
const {
|
||||||
|
MainHeader,
|
||||||
|
Home,
|
||||||
|
ShlinkWebComponentContainer,
|
||||||
|
CreateServer,
|
||||||
|
EditServer,
|
||||||
|
Settings,
|
||||||
|
ManageServers,
|
||||||
|
ShlinkVersionsContainer,
|
||||||
|
} = useDependencies(App);
|
||||||
|
|
||||||
const App = (
|
|
||||||
MainHeader: FC,
|
|
||||||
Home: FC,
|
|
||||||
MenuLayout: FC,
|
|
||||||
CreateServer: FC,
|
|
||||||
EditServer: FC,
|
|
||||||
Settings: FC,
|
|
||||||
ManageServers: FC,
|
|
||||||
ShlinkVersionsContainer: FC,
|
|
||||||
) => ({ fetchServers, servers, settings, appUpdated, resetAppUpdate }: AppProps) => {
|
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
|
const initialServers = useRef(servers);
|
||||||
const isHome = location.pathname === '/';
|
const isHome = location.pathname === '/';
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// On first load, try to fetch the remote servers if the list is empty
|
// Try to fetch the remote servers if the list is empty during first render.
|
||||||
if (Object.keys(servers).length === 0) {
|
// We use a ref because we don't care if the servers list becomes empty later.
|
||||||
|
if (Object.keys(initialServers.current).length === 0) {
|
||||||
fetchServers();
|
fetchServers();
|
||||||
}
|
}
|
||||||
|
}, [fetchServers]);
|
||||||
|
|
||||||
changeThemeInMarkup(settings.ui?.theme ?? 'light');
|
useEffect(() => {
|
||||||
}, []);
|
changeThemeInMarkup(settings.ui?.theme ?? getSystemPreferredTheme());
|
||||||
|
}, [settings.ui?.theme]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="container-fluid app-container">
|
<div className="h-full">
|
||||||
<MainHeader />
|
<MainHeader />
|
||||||
|
|
||||||
<div className="app">
|
<div className="h-full pt-(--header-height)">
|
||||||
<div className={classNames('shlink-wrapper', { 'd-flex d-md-block align-items-center': isHome })}>
|
<div
|
||||||
|
data-testid="shlink-wrapper"
|
||||||
|
className={clsx(
|
||||||
|
'min-h-full pb-[calc(var(--footer-height)+var(--footer-margin))] -mb-[calc(var(--footer-height)+var(--footer-margin))]',
|
||||||
|
{ 'flex items-center pt-4': isHome },
|
||||||
|
)}
|
||||||
|
>
|
||||||
<Routes>
|
<Routes>
|
||||||
<Route index element={<Home />} />
|
<Route index element={<Home />} />
|
||||||
<Route path="/settings/*" element={<Settings />} />
|
<Route path="/settings">
|
||||||
|
{['', '*'].map((path) => <Route key={path} path={path} element={<Settings />} />)}
|
||||||
|
</Route>
|
||||||
<Route path="/manage-servers" element={<ManageServers />} />
|
<Route path="/manage-servers" element={<ManageServers />} />
|
||||||
<Route path="/server/create" element={<CreateServer />} />
|
<Route path="/server/create" element={<CreateServer />} />
|
||||||
<Route path="/server/:serverId/edit" element={<EditServer />} />
|
<Route path="/server/:serverId/edit" element={<EditServer />} />
|
||||||
<Route path="/server/:serverId/*" element={<MenuLayout />} />
|
<Route path="/server/:serverId">
|
||||||
|
{['', '*'].map((path) => <Route key={path} path={path} element={<ShlinkWebComponentContainer />} />)}
|
||||||
|
</Route>
|
||||||
<Route path="*" element={<NotFound />} />
|
<Route path="*" element={<NotFound />} />
|
||||||
</Routes>
|
</Routes>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="shlink-footer">
|
<div className="h-(--footer-height) mt-(--footer-margin) md:px-4">
|
||||||
<ShlinkVersionsContainer />
|
<ShlinkVersionsContainer />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<AppUpdateBanner isOpen={appUpdated} toggle={resetAppUpdate} forceUpdate={forceUpdate} />
|
<AppUpdateBanner isOpen={appUpdated} onClose={resetAppUpdate} forceUpdate={forceUpdate} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default App;
|
export const AppFactory = componentFactory(App, [
|
||||||
|
'MainHeader',
|
||||||
|
'Home',
|
||||||
|
'ShlinkWebComponentContainer',
|
||||||
|
'CreateServer',
|
||||||
|
'EditServer',
|
||||||
|
'Settings',
|
||||||
|
'ManageServers',
|
||||||
|
'ShlinkVersionsContainer',
|
||||||
|
]);
|
||||||
|
|||||||
@@ -1,18 +1,14 @@
|
|||||||
import { Action } from 'redux';
|
import { createSlice } from '@reduxjs/toolkit';
|
||||||
import { buildActionCreator, buildReducer } from '../../utils/helpers/redux';
|
|
||||||
|
|
||||||
/* eslint-disable padding-line-between-statements */
|
const { actions, reducer } = createSlice({
|
||||||
export const APP_UPDATE_AVAILABLE = 'shlink/appUpdates/APP_UPDATE_AVAILABLE';
|
name: 'shlink/appUpdates',
|
||||||
export const RESET_APP_UPDATE = 'shlink/appUpdates/RESET_APP_UPDATE';
|
initialState: false,
|
||||||
/* eslint-enable padding-line-between-statements */
|
reducers: {
|
||||||
|
appUpdateAvailable: () => true,
|
||||||
|
resetAppUpdate: () => false,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
const initialState = false;
|
export const { appUpdateAvailable, resetAppUpdate } = actions;
|
||||||
|
|
||||||
export default buildReducer<boolean, Action<string>>({
|
export const appUpdatesReducer = reducer;
|
||||||
[APP_UPDATE_AVAILABLE]: () => true,
|
|
||||||
[RESET_APP_UPDATE]: () => false,
|
|
||||||
}, initialState);
|
|
||||||
|
|
||||||
export const appUpdateAvailable = buildActionCreator(APP_UPDATE_AVAILABLE);
|
|
||||||
|
|
||||||
export const resetAppUpdate = buildActionCreator(RESET_APP_UPDATE);
|
|
||||||
|
|||||||
@@ -1,27 +1,14 @@
|
|||||||
import Bottle from 'bottlejs';
|
import type Bottle from 'bottlejs';
|
||||||
|
import type { ConnectDecorator } from '../../container/types';
|
||||||
|
import { AppFactory } from '../App';
|
||||||
import { appUpdateAvailable, resetAppUpdate } from '../reducers/appUpdates';
|
import { appUpdateAvailable, resetAppUpdate } from '../reducers/appUpdates';
|
||||||
import App from '../App';
|
|
||||||
import { ConnectDecorator } from '../../container/types';
|
|
||||||
|
|
||||||
const provideServices = (bottle: Bottle, connect: ConnectDecorator) => {
|
export const provideServices = (bottle: Bottle, connect: ConnectDecorator) => {
|
||||||
// Components
|
// Components
|
||||||
bottle.serviceFactory(
|
bottle.factory('App', AppFactory);
|
||||||
'App',
|
bottle.decorator('App', connect(['servers', 'settings', 'appUpdated'], ['fetchServers', 'resetAppUpdate']));
|
||||||
App,
|
|
||||||
'MainHeader',
|
|
||||||
'Home',
|
|
||||||
'MenuLayout',
|
|
||||||
'CreateServer',
|
|
||||||
'EditServer',
|
|
||||||
'Settings',
|
|
||||||
'ManageServers',
|
|
||||||
'ShlinkVersionsContainer',
|
|
||||||
);
|
|
||||||
bottle.decorator('App', connect([ 'servers', 'settings', 'appUpdated' ], [ 'fetchServers', 'resetAppUpdate' ]));
|
|
||||||
|
|
||||||
// Actions
|
// Actions
|
||||||
bottle.serviceFactory('appUpdateAvailable', () => appUpdateAvailable);
|
bottle.serviceFactory('appUpdateAvailable', () => appUpdateAvailable);
|
||||||
bottle.serviceFactory('resetAppUpdate', () => resetAppUpdate);
|
bottle.serviceFactory('resetAppUpdate', () => resetAppUpdate);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default provideServices;
|
|
||||||
|
|||||||
@@ -1,17 +0,0 @@
|
|||||||
@import '../utils/base';
|
|
||||||
@import '../utils/mixins/horizontal-align';
|
|
||||||
|
|
||||||
.app-update-banner.app-update-banner {
|
|
||||||
@include horizontal-align();
|
|
||||||
|
|
||||||
position: fixed;
|
|
||||||
top: $headerHeight - 25px;
|
|
||||||
padding: 0 4rem 0 0;
|
|
||||||
z-index: 1040;
|
|
||||||
margin: 0;
|
|
||||||
color: var(--text-color);
|
|
||||||
text-align: center;
|
|
||||||
width: 700px;
|
|
||||||
max-width: calc(100% - 30px);
|
|
||||||
box-shadow: 0 0 1rem var(--brand-color);
|
|
||||||
}
|
|
||||||
@@ -1,34 +1,46 @@
|
|||||||
import { FC, MouseEventHandler } from 'react';
|
|
||||||
import { Alert, Button } from 'reactstrap';
|
|
||||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
|
||||||
import { faSyncAlt as reloadIcon } from '@fortawesome/free-solid-svg-icons';
|
import { faSyncAlt as reloadIcon } from '@fortawesome/free-solid-svg-icons';
|
||||||
import { SimpleCard } from '../utils/SimpleCard';
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||||
import { useToggle } from '../utils/helpers/hooks';
|
import { Button, Card, CloseButton,useToggle } from '@shlinkio/shlink-frontend-kit';
|
||||||
import './AppUpdateBanner.scss';
|
import { clsx } from 'clsx';
|
||||||
|
import type { FC } from 'react';
|
||||||
|
import { useCallback } from 'react';
|
||||||
|
|
||||||
interface AppUpdateBannerProps {
|
interface AppUpdateBannerProps {
|
||||||
isOpen: boolean;
|
isOpen: boolean;
|
||||||
toggle: MouseEventHandler<any>;
|
onClose: () => void;
|
||||||
forceUpdate: Function;
|
forceUpdate: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const AppUpdateBanner: FC<AppUpdateBannerProps> = ({ isOpen, toggle, forceUpdate }) => {
|
export const AppUpdateBanner: FC<AppUpdateBannerProps> = ({ isOpen, onClose, forceUpdate }) => {
|
||||||
const [ isUpdating,, setUpdating ] = useToggle();
|
const { flag: isUpdating, setToTrue: setUpdating } = useToggle();
|
||||||
const update = () => {
|
const update = useCallback(() => {
|
||||||
setUpdating();
|
setUpdating();
|
||||||
forceUpdate();
|
forceUpdate();
|
||||||
};
|
}, [forceUpdate, setUpdating]);
|
||||||
|
|
||||||
|
if (!isOpen) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Alert className="app-update-banner" isOpen={isOpen} toggle={toggle} tag={SimpleCard} color="secondary">
|
<Card
|
||||||
<h4 className="mb-4">This app has just been updated!</h4>
|
role="alert"
|
||||||
<p className="mb-0">
|
className={clsx(
|
||||||
|
'w-[700px] max-w-[calc(100%-30px)]',
|
||||||
|
'fixed top-[35px] left-[50%] translate-x-[-50%] z-[1040]',
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<Card.Header className="flex items-center justify-between">
|
||||||
|
<h5>This app has just been updated!</h5>
|
||||||
|
<CloseButton onClick={onClose} />
|
||||||
|
</Card.Header>
|
||||||
|
<Card.Body className="flex gap-4 items-center justify-between max-md:flex-col">
|
||||||
Restart it to enjoy the new features.
|
Restart it to enjoy the new features.
|
||||||
<Button disabled={isUpdating} className="ms-2" color="secondary" size="sm" onClick={update}>
|
<Button disabled={isUpdating} variant="secondary" solid onClick={update}>
|
||||||
{!isUpdating && <>Restart now <FontAwesomeIcon icon={reloadIcon} className="ms-1" /></>}
|
{!isUpdating && <>Restart now <FontAwesomeIcon icon={reloadIcon} /></>}
|
||||||
{isUpdating && <>Restarting...</>}
|
{isUpdating && <>Restarting...</>}
|
||||||
</Button>
|
</Button>
|
||||||
</p>
|
</Card.Body>
|
||||||
</Alert>
|
</Card>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,81 +0,0 @@
|
|||||||
@import '../utils/base';
|
|
||||||
@import '../utils/mixins/vertical-align';
|
|
||||||
|
|
||||||
.aside-menu {
|
|
||||||
width: $asideMenuWidth;
|
|
||||||
background-color: var(--primary-color);
|
|
||||||
box-shadow: rgba(0, 0, 0, .05) 0 8px 15px;
|
|
||||||
position: fixed !important;
|
|
||||||
padding-top: 13px;
|
|
||||||
padding-bottom: 10px;
|
|
||||||
top: $headerHeight;
|
|
||||||
bottom: 0;
|
|
||||||
left: 0;
|
|
||||||
display: block;
|
|
||||||
z-index: 1010;
|
|
||||||
overflow-x: hidden;
|
|
||||||
overflow-y: auto;
|
|
||||||
|
|
||||||
@media (min-width: $mdMin) {
|
|
||||||
padding: 30px 15px 15px;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: $smMax) {
|
|
||||||
transition: left 300ms;
|
|
||||||
top: $headerHeight - 3px;
|
|
||||||
box-shadow: -10px 0 50px 11px rgba(0, 0, 0, .55);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.aside-menu--hidden {
|
|
||||||
@media (max-width: $smMax) {
|
|
||||||
left: -($asideMenuWidth + 35px);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.aside-menu__nav {
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.aside-menu__item {
|
|
||||||
padding: 10px 20px;
|
|
||||||
margin: 0 -15px;
|
|
||||||
text-decoration: none !important;
|
|
||||||
cursor: pointer;
|
|
||||||
|
|
||||||
@media (max-width: $smMax) {
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.aside-menu__item:hover {
|
|
||||||
background-color: var(--secondary-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
.aside-menu__item--selected,
|
|
||||||
.aside-menu__item--selected:hover {
|
|
||||||
color: #ffffff;
|
|
||||||
background-color: var(--brand-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
.aside-menu__item--divider {
|
|
||||||
border-bottom: 1px solid #eeeeee;
|
|
||||||
margin: 20px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.aside-menu__item--danger {
|
|
||||||
color: $dangerColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
.aside-menu__item--push {
|
|
||||||
margin-top: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.aside-menu__item--danger:hover {
|
|
||||||
color: #ffffff;
|
|
||||||
background-color: $dangerColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
.aside-menu__item-text {
|
|
||||||
margin-left: 8px;
|
|
||||||
}
|
|
||||||