mirror of
https://github.com/unraid/api.git
synced 2026-01-06 08:39:54 -06:00
Compare commits
1079 Commits
snyk-upgra
...
fix/web-in
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e861e44973 | ||
|
|
81c953b68c | ||
|
|
de916d2338 | ||
|
|
f81b59a83a | ||
|
|
83a4f61961 | ||
|
|
99c3cc633b | ||
|
|
44640bae8c | ||
|
|
ebb67210c3 | ||
|
|
5b2cbd670a | ||
|
|
c163998175 | ||
|
|
4fbbbd7f6a | ||
|
|
203c2b88ac | ||
|
|
746d1a8aaa | ||
|
|
c0d3cf5782 | ||
|
|
64d3765a9a | ||
|
|
5dd36d1836 | ||
|
|
4264557789 | ||
|
|
344b023503 | ||
|
|
0331e24a74 | ||
|
|
2b597f9f02 | ||
|
|
bd31e09bcf | ||
|
|
6c73cbf4ad | ||
|
|
4b4aadb5f1 | ||
|
|
5ddecce21c | ||
|
|
6669a963af | ||
|
|
8d386043ae | ||
|
|
16f00a0d8c | ||
|
|
a4e2a77410 | ||
|
|
91a9949a5c | ||
|
|
235746c0ba | ||
|
|
e366cad0a4 | ||
|
|
83344e05c1 | ||
|
|
61ec04cb87 | ||
|
|
a947ff14fa | ||
|
|
5dfd6d5ded | ||
|
|
71e2b70678 | ||
|
|
4daa54cfb5 | ||
|
|
7ef3729769 | ||
|
|
46a368e1b5 | ||
|
|
b53bb3f197 | ||
|
|
1935ba1a7f | ||
|
|
14abc13cc8 | ||
|
|
c3548d5122 | ||
|
|
6c54fa14b1 | ||
|
|
8b93bcea08 | ||
|
|
a6cd74dc5c | ||
|
|
aa1ef1bd4c | ||
|
|
2cdc02f64a | ||
|
|
d0819b8d02 | ||
|
|
74b3e29c74 | ||
|
|
b32f84b105 | ||
|
|
806bd633ac | ||
|
|
e5e1c43bb3 | ||
|
|
3b2d61efc2 | ||
|
|
fe98295496 | ||
|
|
b9947108a4 | ||
|
|
3c27b51ab8 | ||
|
|
2327b00d30 | ||
|
|
bf3b00fbaf | ||
|
|
4f04f93033 | ||
|
|
5dc13755df | ||
|
|
69a34aca14 | ||
|
|
bbc1e02782 | ||
|
|
e77db18870 | ||
|
|
b6805d439e | ||
|
|
f37dda16c2 | ||
|
|
88be62317f | ||
|
|
6b5e012950 | ||
|
|
e05a05926d | ||
|
|
c817cc4b7e | ||
|
|
491b5fe8bc | ||
|
|
8675653e4e | ||
|
|
1f9e282880 | ||
|
|
90cf2c8a16 | ||
|
|
729ed42329 | ||
|
|
309d221542 | ||
|
|
859440386e | ||
|
|
ceac6269b3 | ||
|
|
286ffc54e5 | ||
|
|
84f4f67ce1 | ||
|
|
8827483699 | ||
|
|
f8393eeebe | ||
|
|
5d72b5a970 | ||
|
|
1f5c6424c7 | ||
|
|
39612cd978 | ||
|
|
b1b93e2783 | ||
|
|
eff6c32ccd | ||
|
|
b1ac0f9c83 | ||
|
|
d88b1e9660 | ||
|
|
124fb610b1 | ||
|
|
143515560c | ||
|
|
f0f5a3057a | ||
|
|
4404208deb | ||
|
|
f45719fa6b | ||
|
|
acba0b0365 | ||
|
|
8760a66907 | ||
|
|
5f81c4bd27 | ||
|
|
2310d53684 | ||
|
|
5b3ec8304c | ||
|
|
7a009b6be7 | ||
|
|
be7135efdd | ||
|
|
939383e4ef | ||
|
|
0042f14ab3 | ||
|
|
3e8c101edd | ||
|
|
477c113ce1 | ||
|
|
caf797cf4a | ||
|
|
73a12496d5 | ||
|
|
cea11daf15 | ||
|
|
bb6baf9bf4 | ||
|
|
5f1a61d4aa | ||
|
|
bbf28075c6 | ||
|
|
7dcf947527 | ||
|
|
4ee42a6cf6 | ||
|
|
91de6e6c1e | ||
|
|
e2a1f27b22 | ||
|
|
4e9ab645e6 | ||
|
|
7828ef2648 | ||
|
|
dfa27e2c0d | ||
|
|
9d2405bd21 | ||
|
|
e1515a118a | ||
|
|
961c343f5d | ||
|
|
6cbb9c07e4 | ||
|
|
833a99fe18 | ||
|
|
943c907d03 | ||
|
|
d28fb24d68 | ||
|
|
0c1b89ff41 | ||
|
|
cead97560c | ||
|
|
5f0446fa79 | ||
|
|
000692ca50 | ||
|
|
d8f9f03146 | ||
|
|
29035429bd | ||
|
|
5f8602b864 | ||
|
|
77558a5cd9 | ||
|
|
116efe6f72 | ||
|
|
8e0962adba | ||
|
|
d23a38960b | ||
|
|
d5f5921534 | ||
|
|
a52cc7861c | ||
|
|
0d6a0035aa | ||
|
|
0fa41f5690 | ||
|
|
45327ce01d | ||
|
|
11ce9e2644 | ||
|
|
00b8ffe87d | ||
|
|
4eda0991d6 | ||
|
|
9a869a49e3 | ||
|
|
7ef3286191 | ||
|
|
cb91fbb054 | ||
|
|
c6547a51fc | ||
|
|
24435613f8 | ||
|
|
4a29fc9dda | ||
|
|
2ef9fbb20e | ||
|
|
a09f7c935d | ||
|
|
29d9371cc3 | ||
|
|
ef82ec5af5 | ||
|
|
cf72c8b359 | ||
|
|
0705764385 | ||
|
|
ea6ae83919 | ||
|
|
478254e134 | ||
|
|
8f5814589e | ||
|
|
dab6985297 | ||
|
|
e40a9ebecd | ||
|
|
bc4708f405 | ||
|
|
99704a9dbb | ||
|
|
23b1f1ac73 | ||
|
|
a5cf63fe28 | ||
|
|
78ec4663cc | ||
|
|
a3b171f58d | ||
|
|
060fb91546 | ||
|
|
af1994cb62 | ||
|
|
bad7f71fea | ||
|
|
a355a64136 | ||
|
|
cf08627725 | ||
|
|
6962cdd214 | ||
|
|
a39da15be4 | ||
|
|
bb555f8296 | ||
|
|
9a0d63d4ee | ||
|
|
9bbab0f86c | ||
|
|
349145ba53 | ||
|
|
c12d8dae4e | ||
|
|
b1a2ba78cc | ||
|
|
2409ef2dd6 | ||
|
|
0055637602 | ||
|
|
547b75a55e | ||
|
|
c36082e82b | ||
|
|
0f3bebf859 | ||
|
|
96f3902b57 | ||
|
|
b9cd8c426d | ||
|
|
7c8e8a0e53 | ||
|
|
801abac06b | ||
|
|
3fe13d5235 | ||
|
|
eeb3289ae8 | ||
|
|
939d7a304d | ||
|
|
acccb3694c | ||
|
|
2724485989 | ||
|
|
2f4ff21986 | ||
|
|
83e00c640a | ||
|
|
abcaa5aedb | ||
|
|
4c663dc69c | ||
|
|
89eb841b20 | ||
|
|
7296195495 | ||
|
|
696b55de6c | ||
|
|
aa5fad39f3 | ||
|
|
9c38fa6a9c | ||
|
|
da5d1132d1 | ||
|
|
001be86181 | ||
|
|
ecfc797e7d | ||
|
|
dffbfc2dab | ||
|
|
e5f029830b | ||
|
|
1a33e6343a | ||
|
|
69441d890e | ||
|
|
46c82ecae3 | ||
|
|
0b469f5b3f | ||
|
|
3fc41480a2 | ||
|
|
e27776df3d | ||
|
|
abd8e09908 | ||
|
|
504283f227 | ||
|
|
ff7e09e15c | ||
|
|
deb42f6a81 | ||
|
|
95d018ea05 | ||
|
|
106b2e42c0 | ||
|
|
1c5ff58d2d | ||
|
|
d7bab9f443 | ||
|
|
902c76c759 | ||
|
|
5e50f24d70 | ||
|
|
4f0210d16a | ||
|
|
ddb8772692 | ||
|
|
787f8b9bf5 | ||
|
|
61ba324ca0 | ||
|
|
a230a33df5 | ||
|
|
84b234c9cf | ||
|
|
9bfc04c2a5 | ||
|
|
e84430471d | ||
|
|
2d60045784 | ||
|
|
e9137f2553 | ||
|
|
dbe0dd5dfb | ||
|
|
9d2796f2c9 | ||
|
|
972a19be04 | ||
|
|
c8da8fe314 | ||
|
|
353132b67a | ||
|
|
88b7cbfe95 | ||
|
|
3ed1d10c98 | ||
|
|
62693cfcc0 | ||
|
|
810708f775 | ||
|
|
08f6d6df65 | ||
|
|
da673c3f2b | ||
|
|
cb463bfdd0 | ||
|
|
7177171b75 | ||
|
|
9f0ab7fa38 | ||
|
|
a32374a3ac | ||
|
|
cb6534d9d9 | ||
|
|
2eaf175515 | ||
|
|
50376a0d66 | ||
|
|
4b2007b689 | ||
|
|
72fcaca4f3 | ||
|
|
2f48ddf942 | ||
|
|
62dfa6c83a | ||
|
|
27bb375460 | ||
|
|
cc4d5bdefb | ||
|
|
f55302c130 | ||
|
|
b8dbe3f9d9 | ||
|
|
20771f61a8 | ||
|
|
b9b8bbe871 | ||
|
|
b8e61007e3 | ||
|
|
49536032df | ||
|
|
9229cf3df6 | ||
|
|
58665a4e98 | ||
|
|
885d1537b6 | ||
|
|
198cfe5015 | ||
|
|
42189dd451 | ||
|
|
6122b3c001 | ||
|
|
cda7368d3d | ||
|
|
447cecd19d | ||
|
|
7321bd0088 | ||
|
|
67e898efe1 | ||
|
|
41e5de83a2 | ||
|
|
5c020a62d6 | ||
|
|
1393e967fa | ||
|
|
f07c14354f | ||
|
|
d42a426244 | ||
|
|
125bc29166 | ||
|
|
a6333bf5a2 | ||
|
|
e8e985ad6a | ||
|
|
1a598885cc | ||
|
|
d73f267245 | ||
|
|
7c1873249e | ||
|
|
09f33a0127 | ||
|
|
db00d7442d | ||
|
|
724159314c | ||
|
|
180f115b71 | ||
|
|
eb38eb219e | ||
|
|
3da701a53b | ||
|
|
6e5b2f1f67 | ||
|
|
812053d7a4 | ||
|
|
a929c7e3b3 | ||
|
|
c0179c8351 | ||
|
|
d5c7be54b0 | ||
|
|
32478f34c2 | ||
|
|
4daa09b340 | ||
|
|
346ce91f73 | ||
|
|
cee3a6d0ef | ||
|
|
e90f606f43 | ||
|
|
05fa344454 | ||
|
|
406c400bd2 | ||
|
|
1ae466899e | ||
|
|
5178e131ce | ||
|
|
0bd11bce5a | ||
|
|
fddde33977 | ||
|
|
1f5df845eb | ||
|
|
ef54af655e | ||
|
|
bb44862b7b | ||
|
|
9709dc82ea | ||
|
|
38f0699e19 | ||
|
|
6ca9f421eb | ||
|
|
935825571b | ||
|
|
9beaa78820 | ||
|
|
420c2c1afd | ||
|
|
7c0cb07b83 | ||
|
|
c6a7137f19 | ||
|
|
44f9ba0e7f | ||
|
|
1c61e64169 | ||
|
|
cf0eeebd31 | ||
|
|
f118597e47 | ||
|
|
6f2fcffd3e | ||
|
|
8f7748404c | ||
|
|
88c2605d4f | ||
|
|
c2d645612a | ||
|
|
b20f69c208 | ||
|
|
b9cedb70ff | ||
|
|
a11978aa33 | ||
|
|
b0efcc0d51 | ||
|
|
92b5f2226e | ||
|
|
98f2603525 | ||
|
|
cfb1d50c8e | ||
|
|
545ccf1938 | ||
|
|
0c79995107 | ||
|
|
9d3397a687 | ||
|
|
11c160835a | ||
|
|
e388b37aa6 | ||
|
|
1da882b807 | ||
|
|
d9d5a24b70 | ||
|
|
24e3cad882 | ||
|
|
323a4a17cf | ||
|
|
9968e0f7df | ||
|
|
2ccc53630b | ||
|
|
d7bb3defc3 | ||
|
|
ddb8bf8a5c | ||
|
|
6234d61ae5 | ||
|
|
a665ee3ec6 | ||
|
|
7ca3efe8b8 | ||
|
|
28f4952599 | ||
|
|
7e4022518d | ||
|
|
4d1656eaa8 | ||
|
|
5b2421cb0c | ||
|
|
0578b066f1 | ||
|
|
57fdcf3e60 | ||
|
|
eb7bdb6a85 | ||
|
|
ebd671e7b6 | ||
|
|
15a1a3ac15 | ||
|
|
9a0c7fe9c8 | ||
|
|
91bcbc3d6f | ||
|
|
b3d046f4ea | ||
|
|
0f13e34562 | ||
|
|
e18cd87180 | ||
|
|
421949a9f8 | ||
|
|
8c7c580f3f | ||
|
|
c616641044 | ||
|
|
fd16243287 | ||
|
|
7352bbe77a | ||
|
|
4d33908e01 | ||
|
|
adabe92f72 | ||
|
|
958f9e57e1 | ||
|
|
ac5032df83 | ||
|
|
5f4cc07473 | ||
|
|
38524bce88 | ||
|
|
64db2f19a7 | ||
|
|
8fe1e80bbd | ||
|
|
1c4506cf50 | ||
|
|
84fe7f6df6 | ||
|
|
5c7e650b3b | ||
|
|
6cac078e15 | ||
|
|
4e555021a7 | ||
|
|
b1e2f043b1 | ||
|
|
bc69852333 | ||
|
|
2c79ccc883 | ||
|
|
c240fab58a | ||
|
|
3c50022ac3 | ||
|
|
9201136cb1 | ||
|
|
ff52f75abf | ||
|
|
eed40f7875 | ||
|
|
754d4560ea | ||
|
|
f6d09f4ba2 | ||
|
|
a1f0dac42d | ||
|
|
fff935cf02 | ||
|
|
0849468fc2 | ||
|
|
6a57924fbf | ||
|
|
57802c2ea0 | ||
|
|
924df0dc9e | ||
|
|
d04001e052 | ||
|
|
92ec931aff | ||
|
|
30f92374d0 | ||
|
|
6bfd221cd1 | ||
|
|
ceb537ae91 | ||
|
|
81b197a9aa | ||
|
|
54b4ad0df8 | ||
|
|
e84c3ebe14 | ||
|
|
81acf1d947 | ||
|
|
80bfc231e0 | ||
|
|
b1409684db | ||
|
|
14d9448e4c | ||
|
|
924fa699eb | ||
|
|
999a8e39eb | ||
|
|
5a1c85d739 | ||
|
|
ba77ff4a4c | ||
|
|
05765495c4 | ||
|
|
f7cccc8c37 | ||
|
|
85e0f7993e | ||
|
|
d5a424ebe1 | ||
|
|
01441961c3 | ||
|
|
836f64d28f | ||
|
|
79bb4e585b | ||
|
|
409e88b727 | ||
|
|
5034a8981a | ||
|
|
e61d9f195d | ||
|
|
b3e213ba04 | ||
|
|
a7ea678683 | ||
|
|
791e16ce52 | ||
|
|
173da0e65b | ||
|
|
287aabfda7 | ||
|
|
d8656cc6b3 | ||
|
|
a3500c9bc9 | ||
|
|
b513cbe614 | ||
|
|
b5c525a9c2 | ||
|
|
648b560148 | ||
|
|
6eb34c3501 | ||
|
|
21544bd2dc | ||
|
|
3e115f84d7 | ||
|
|
ba586fc438 | ||
|
|
e6cbed14a9 | ||
|
|
f531e68b87 | ||
|
|
53f718e240 | ||
|
|
de36bfab99 | ||
|
|
1e2f57a4cd | ||
|
|
46aa3a3e24 | ||
|
|
0c627d1ade | ||
|
|
f20349fb2a | ||
|
|
dc72d63481 | ||
|
|
e9efed8067 | ||
|
|
71ce064008 | ||
|
|
b67b0ea633 | ||
|
|
bf3d46d190 | ||
|
|
a1fa3462eb | ||
|
|
c84175e763 | ||
|
|
0f9fe18379 | ||
|
|
76c0d35783 | ||
|
|
3ece0d1acc | ||
|
|
0473c9b676 | ||
|
|
1956227f63 | ||
|
|
c515d08d5c | ||
|
|
0bd9820c00 | ||
|
|
0c2299cfcd | ||
|
|
12fdfac467 | ||
|
|
3fc20ec593 | ||
|
|
69a6163e29 | ||
|
|
00294699f0 | ||
|
|
90ff980a00 | ||
|
|
17e7d2a2de | ||
|
|
d2a88df5bf | ||
|
|
9471f5c918 | ||
|
|
492d45f363 | ||
|
|
2951d68f9d | ||
|
|
4857bc0478 | ||
|
|
c794a1d1a1 | ||
|
|
d2a34acfb9 | ||
|
|
3dc60b6106 | ||
|
|
57587b9175 | ||
|
|
5ee7cb2647 | ||
|
|
911a3f8f1a | ||
|
|
d426001372 | ||
|
|
2d0c65aaf4 | ||
|
|
fd4605b956 | ||
|
|
3f84b6bbfd | ||
|
|
5ad10af303 | ||
|
|
9aa11faaaa | ||
|
|
bfa98574f1 | ||
|
|
dd2dc40ff1 | ||
|
|
8a3265d7b1 | ||
|
|
a240a031a8 | ||
|
|
979e41fe41 | ||
|
|
03dc404aa7 | ||
|
|
364320ffc9 | ||
|
|
2492f4cec9 | ||
|
|
1a643b3eef | ||
|
|
58ee3b958b | ||
|
|
2928cf5821 | ||
|
|
b21b276151 | ||
|
|
d80f25dc96 | ||
|
|
f5f5a081e6 | ||
|
|
f60474b4d7 | ||
|
|
364373df0c | ||
|
|
bb38533bb2 | ||
|
|
836801c524 | ||
|
|
b54cf5ede9 | ||
|
|
1a20c66c02 | ||
|
|
587bbb3b4d | ||
|
|
e95f7a1a03 | ||
|
|
9c75f6e2ca | ||
|
|
822042ab9c | ||
|
|
3a843b6e16 | ||
|
|
6072387c37 | ||
|
|
313162dbf2 | ||
|
|
495515abac | ||
|
|
09087040e9 | ||
|
|
4423829911 | ||
|
|
c8f469c4fb | ||
|
|
bc61b45f9f | ||
|
|
f530d9ea82 | ||
|
|
2046fa5310 | ||
|
|
9ea2327fa0 | ||
|
|
ff67b54a1b | ||
|
|
e6bd7a54be | ||
|
|
5827b5ffa3 | ||
|
|
572a1310e0 | ||
|
|
c1403d3826 | ||
|
|
29afe9b9e8 | ||
|
|
e9ff33d263 | ||
|
|
a62f60a436 | ||
|
|
838964c6ef | ||
|
|
800fc12c15 | ||
|
|
80175241e3 | ||
|
|
5d801f22f5 | ||
|
|
ba772add54 | ||
|
|
ff24f12cae | ||
|
|
487f5c1865 | ||
|
|
e0c90037fb | ||
|
|
aa5f603cba | ||
|
|
409db43973 | ||
|
|
cef1b29355 | ||
|
|
045750c87e | ||
|
|
85802e7af7 | ||
|
|
4bfdb66d46 | ||
|
|
81a6a52d9f | ||
|
|
7759fe1dc3 | ||
|
|
3b2acb29b5 | ||
|
|
5f2b949ecf | ||
|
|
1b956d563e | ||
|
|
c6a97f5082 | ||
|
|
7f512e47e9 | ||
|
|
5d725b0e76 | ||
|
|
fe63607260 | ||
|
|
0a1d4daf6e | ||
|
|
9e9e385bef | ||
|
|
6fed39e05b | ||
|
|
3dec53d13d | ||
|
|
f0ded9f5be | ||
|
|
7d55a1c2cd | ||
|
|
f3dc9663b8 | ||
|
|
05c7c481a9 | ||
|
|
adcc1543f0 | ||
|
|
95f873c752 | ||
|
|
ec90f8b295 | ||
|
|
f84195a98d | ||
|
|
5e98a68e2e | ||
|
|
b91dbca144 | ||
|
|
79a01da18d | ||
|
|
14951d3004 | ||
|
|
64c2061bea | ||
|
|
e3adc9a29a | ||
|
|
6b689ffcce | ||
|
|
c995a4c5c8 | ||
|
|
8d1e0f67d1 | ||
|
|
7877a5dca2 | ||
|
|
16db278ffd | ||
|
|
521b4381f2 | ||
|
|
9ae9d40f94 | ||
|
|
1d562d404c | ||
|
|
7ac1b268d9 | ||
|
|
4833e9dccf | ||
|
|
f28b7510fa | ||
|
|
37b717b142 | ||
|
|
fd8b40d9aa | ||
|
|
1d944781cf | ||
|
|
1f4c64d022 | ||
|
|
f69b5130a3 | ||
|
|
f8b143904b | ||
|
|
31a5413643 | ||
|
|
a95fc5ed07 | ||
|
|
fcd7bb790e | ||
|
|
008e10948e | ||
|
|
c97a4f1268 | ||
|
|
3eba95b8cc | ||
|
|
2bf8f0b937 | ||
|
|
9ae45d1258 | ||
|
|
1835a4cf3f | ||
|
|
2ab44b894d | ||
|
|
1108f49b07 | ||
|
|
cc69213beb | ||
|
|
460e557dd8 | ||
|
|
05e29468d2 | ||
|
|
4d3a311fb4 | ||
|
|
bc62d210ec | ||
|
|
43d3ea6553 | ||
|
|
882e3e1ef4 | ||
|
|
b33c86c99c | ||
|
|
cd0248e4c9 | ||
|
|
ecb3ed5003 | ||
|
|
0569339a41 | ||
|
|
3e9faead43 | ||
|
|
6e700b2385 | ||
|
|
464fc4993c | ||
|
|
4316c72809 | ||
|
|
ce0cebe09c | ||
|
|
23b90a0d56 | ||
|
|
3f8b3536b5 | ||
|
|
0dcf785b45 | ||
|
|
8cf4aff622 | ||
|
|
cefda7c42b | ||
|
|
0393b2382c | ||
|
|
23e900f7fd | ||
|
|
2d6aafc257 | ||
|
|
b191efece1 | ||
|
|
2a7f0043f5 | ||
|
|
607c7e3704 | ||
|
|
c246a443c5 | ||
|
|
fd495e1f5c | ||
|
|
621a06cafa | ||
|
|
f890b05151 | ||
|
|
567d8fdd6d | ||
|
|
7d996906ad | ||
|
|
b5ec076279 | ||
|
|
de8dfe3dba | ||
|
|
7249956d40 | ||
|
|
e6eb56466e | ||
|
|
8954700bcb | ||
|
|
eb595cea9e | ||
|
|
9a1a0a54e6 | ||
|
|
134396b602 | ||
|
|
2aa65fdb68 | ||
|
|
a9c4d7d5dd | ||
|
|
2cbbd5ee40 | ||
|
|
c84c55761c | ||
|
|
77eed36990 | ||
|
|
5c2d84d8b4 | ||
|
|
9883f0f82f | ||
|
|
e62b05b6f6 | ||
|
|
8e6ee8b770 | ||
|
|
666b51a28a | ||
|
|
1962097a66 | ||
|
|
7f010854b5 | ||
|
|
17288a4c02 | ||
|
|
ea48def9fc | ||
|
|
a1d5c29ffb | ||
|
|
bf99eb25c8 | ||
|
|
b35a440792 | ||
|
|
58f9eec8b1 | ||
|
|
26841aa10d | ||
|
|
e18a8d670e | ||
|
|
49d077db97 | ||
|
|
9dafe165b0 | ||
|
|
cce1953cb8 | ||
|
|
7e33b25593 | ||
|
|
78fb49a6fc | ||
|
|
f1e0d93bc5 | ||
|
|
195a178d15 | ||
|
|
b9257fce28 | ||
|
|
41eaf4ef1b | ||
|
|
93d0c08955 | ||
|
|
c5bc3454ff | ||
|
|
c33b4ef709 | ||
|
|
ce3ba7d070 | ||
|
|
639eb08291 | ||
|
|
6d109b4c4c | ||
|
|
6f3971dc47 | ||
|
|
2ccb503dc8 | ||
|
|
3cb9fdf102 | ||
|
|
40d81a4081 | ||
|
|
5a85f55be8 | ||
|
|
5455e211bc | ||
|
|
cb4cc989c7 | ||
|
|
037aa479bf | ||
|
|
08567f287a | ||
|
|
a57f1d890d | ||
|
|
3ab406e012 | ||
|
|
f36f4702a2 | ||
|
|
62697f7972 | ||
|
|
ec8d2bc0e8 | ||
|
|
d83664b6a3 | ||
|
|
6910a020d2 | ||
|
|
60e5c6e3e8 | ||
|
|
90b1432875 | ||
|
|
f1059aa381 | ||
|
|
01b4937f35 | ||
|
|
3e051815c5 | ||
|
|
e976daf8b0 | ||
|
|
422046dc03 | ||
|
|
9a270971d1 | ||
|
|
0742382ae1 | ||
|
|
763c38430e | ||
|
|
4d926bba8e | ||
|
|
4acc4ea9a9 | ||
|
|
565bf47818 | ||
|
|
176a0f30be | ||
|
|
6f4d983d89 | ||
|
|
6a0e258cf2 | ||
|
|
8d82064888 | ||
|
|
4300179b67 | ||
|
|
7e31ae2ebf | ||
|
|
7a27560b0d | ||
|
|
93655fef62 | ||
|
|
a581a95cb4 | ||
|
|
261fdda47c | ||
|
|
7a2a243a21 | ||
|
|
bead4256af | ||
|
|
e8dfd7e3b3 | ||
|
|
e456b7fcac | ||
|
|
fbe5e417ef | ||
|
|
5f80053a33 | ||
|
|
fa520a2d3e | ||
|
|
cf54f01945 | ||
|
|
44d2d58f12 | ||
|
|
daba2a352f | ||
|
|
d1ff2b1fad | ||
|
|
b1bd71f2e2 | ||
|
|
7f49816275 | ||
|
|
d73d460e88 | ||
|
|
ab1e852b6c | ||
|
|
117b7430db | ||
|
|
2e73f9e37a | ||
|
|
d3158983b4 | ||
|
|
dae7baa6ad | ||
|
|
e29f5e1adf | ||
|
|
e8d15c7dbb | ||
|
|
58be009da4 | ||
|
|
d4eb0ce3f2 | ||
|
|
d73324a141 | ||
|
|
7061be60f4 | ||
|
|
2a65f64ac1 | ||
|
|
120ba3e447 | ||
|
|
a527c7183a | ||
|
|
a0dfbb4e15 | ||
|
|
764f65ff61 | ||
|
|
1615e8623c | ||
|
|
d7bb9ff073 | ||
|
|
d896581e12 | ||
|
|
f833fa1fab | ||
|
|
2823517b26 | ||
|
|
3e0a8d0070 | ||
|
|
b3768d65aa | ||
|
|
d2e17c0051 | ||
|
|
d0354c2ef2 | ||
|
|
17fc1181c2 | ||
|
|
dbe7c5fb93 | ||
|
|
54b421d01f | ||
|
|
0e9611f802 | ||
|
|
4743a2439d | ||
|
|
242c167f82 | ||
|
|
e071b994cf | ||
|
|
60bb8aa0fa | ||
|
|
9f56f34ea4 | ||
|
|
d66b33e600 | ||
|
|
63b7c0361e | ||
|
|
dea66ff49d | ||
|
|
c29621741e | ||
|
|
6d7d013f7a | ||
|
|
ed8e2420a5 | ||
|
|
92ad66dd59 | ||
|
|
5699e34ae9 | ||
|
|
5f9dc26173 | ||
|
|
6d29ec2b90 | ||
|
|
f91ae5c7a0 | ||
|
|
33c69bf76f | ||
|
|
3e95bb259f | ||
|
|
64b6bee559 | ||
|
|
61cb029780 | ||
|
|
c02d823618 | ||
|
|
fec36919c2 | ||
|
|
072242704d | ||
|
|
9199ffdeee | ||
|
|
b92307eef5 | ||
|
|
6445d1647a | ||
|
|
3046fb9eed | ||
|
|
ad2c8b451a | ||
|
|
b39c5203fd | ||
|
|
6e11ca209b | ||
|
|
eeb3598255 | ||
|
|
c2063c28af | ||
|
|
23b63de91f | ||
|
|
05a9340fc3 | ||
|
|
16f0ac5771 | ||
|
|
ebebf76933 | ||
|
|
8c956d45c7 | ||
|
|
4040933fad | ||
|
|
63899f94fc | ||
|
|
7630ae87d4 | ||
|
|
127e2c3be6 | ||
|
|
2aacbc1f1a | ||
|
|
6f0673f428 | ||
|
|
1315dc6099 | ||
|
|
48bc19543e | ||
|
|
08f7f95ea0 | ||
|
|
6b72f188ef | ||
|
|
79ff9bedb9 | ||
|
|
d1c0f46325 | ||
|
|
909c2c6798 | ||
|
|
56dcd85aa1 | ||
|
|
7918f5754f | ||
|
|
519c24983a | ||
|
|
735db3d5f5 | ||
|
|
53627f20c7 | ||
|
|
181026567e | ||
|
|
db6ca23533 | ||
|
|
e0560afb6d | ||
|
|
7e081e6661 | ||
|
|
213caea5b6 | ||
|
|
abd439f131 | ||
|
|
c681848d60 | ||
|
|
46a0567881 | ||
|
|
0c80ef88b5 | ||
|
|
71252ddbea | ||
|
|
1ef2522089 | ||
|
|
f9652d7c06 | ||
|
|
1de59150bc | ||
|
|
2dd8cbb779 | ||
|
|
f2b9cb0478 | ||
|
|
e79ac7122a | ||
|
|
c1c4baf476 | ||
|
|
e023ba6a19 | ||
|
|
2ffeabe2a6 | ||
|
|
36c5bbc3fd | ||
|
|
da1ee3d631 | ||
|
|
86b54dbe9a | ||
|
|
296906758d | ||
|
|
cc2ea1244d | ||
|
|
4aaf223007 | ||
|
|
d283f1f321 | ||
|
|
f1731d732b | ||
|
|
33e4ba261c | ||
|
|
00f73bd3b8 | ||
|
|
5ebce0ebfc | ||
|
|
81f7f41b3b | ||
|
|
00182ebb3c | ||
|
|
58b2b2f130 | ||
|
|
d132ad481b | ||
|
|
dd1ec82a52 | ||
|
|
2edc062569 | ||
|
|
a9c4e69e01 | ||
|
|
5f987458ef | ||
|
|
376a19bac6 | ||
|
|
a1c07370ca | ||
|
|
1efd6b7e18 | ||
|
|
1a31b2c929 | ||
|
|
9623f238b3 | ||
|
|
fa5658fd81 | ||
|
|
0fa76f5d09 | ||
|
|
b4f0a084f1 | ||
|
|
7d49bb2f10 | ||
|
|
8dd99b7f32 | ||
|
|
eaddb696d4 | ||
|
|
898c4e5656 | ||
|
|
62900565fb | ||
|
|
e409ab805d | ||
|
|
463ff4a38a | ||
|
|
205552eda5 | ||
|
|
ca3ffdc603 | ||
|
|
be9e1e34f4 | ||
|
|
00f1c63c46 | ||
|
|
1c8437733c | ||
|
|
a658206cd4 | ||
|
|
95554e9832 | ||
|
|
fb31fb584b | ||
|
|
051faa06ac | ||
|
|
0e0a652dff | ||
|
|
1403a76b80 | ||
|
|
c4c51e83c2 | ||
|
|
c91fef9c5f | ||
|
|
9c33ef8248 | ||
|
|
05ce165b83 | ||
|
|
485fc2a3b6 | ||
|
|
f45f5f7a9a | ||
|
|
9b9a6998b7 | ||
|
|
82d9dc644b | ||
|
|
81678d4de5 | ||
|
|
032fd9853e | ||
|
|
bf60f1e5ac | ||
|
|
bda8f4e1b3 | ||
|
|
8c7160de2e | ||
|
|
2bd460effb | ||
|
|
fdadfe699c | ||
|
|
84c96371f5 | ||
|
|
799b77f09b | ||
|
|
1d67fa4c56 | ||
|
|
8534fec4b2 | ||
|
|
5483861055 | ||
|
|
bb60cbbc18 | ||
|
|
fe906c025e | ||
|
|
79e2e89a80 | ||
|
|
c30b926134 | ||
|
|
a87d83de04 | ||
|
|
7b3bd08c15 | ||
|
|
00375a4590 | ||
|
|
6619138b54 | ||
|
|
e9a7fcf95b | ||
|
|
be1f419d92 | ||
|
|
3a6b511de3 | ||
|
|
82f15afbd2 | ||
|
|
524867b4e2 | ||
|
|
d289e06c0b | ||
|
|
13f366472b | ||
|
|
830718cd2c | ||
|
|
8fffc7725c | ||
|
|
6fa6beb270 | ||
|
|
36846d2377 | ||
|
|
ef962f5f5d | ||
|
|
5cbccb06ad | ||
|
|
220a64ebdc | ||
|
|
3145e30cf1 | ||
|
|
ef198494b0 | ||
|
|
9f1e3c5fda | ||
|
|
ddf8dc7ebf | ||
|
|
8bdffdc7b0 | ||
|
|
915cdc3e06 | ||
|
|
4601388f3f | ||
|
|
66913bd221 | ||
|
|
f554c3d3e2 | ||
|
|
2104eebe02 | ||
|
|
caab570be6 | ||
|
|
ca93ac7143 | ||
|
|
9e895aed58 | ||
|
|
af4f53ed04 | ||
|
|
e021c48daa | ||
|
|
ed4aa3d62c | ||
|
|
2aa491e6f2 | ||
|
|
1098e0f0e9 | ||
|
|
8903371409 | ||
|
|
749eab85bd | ||
|
|
86d4defa3e | ||
|
|
41fd15e7e3 | ||
|
|
c1cff9e95f | ||
|
|
30a0e7d082 | ||
|
|
c387a28dbd | ||
|
|
207ae12522 | ||
|
|
22ebb06980 | ||
|
|
c0319d56b0 | ||
|
|
3aaac2c244 | ||
|
|
d8a66e7b22 | ||
|
|
00838e5cb8 | ||
|
|
6deaf9c342 | ||
|
|
5d6d91cfbd | ||
|
|
f35e0ab627 | ||
|
|
c5da9ea002 | ||
|
|
9334322f11 | ||
|
|
57a039b7d8 | ||
|
|
2621137e31 | ||
|
|
7276e9ddc9 | ||
|
|
4e60c0ac1e | ||
|
|
13df4923a1 | ||
|
|
0eb0bdc918 | ||
|
|
aaaa93f79e | ||
|
|
280dbfa53a | ||
|
|
3a5f976ff6 | ||
|
|
ea417435ac | ||
|
|
ecb69ba059 | ||
|
|
35f6a6cd3c | ||
|
|
64dc4c922d | ||
|
|
33a1e20338 | ||
|
|
9e1320b272 | ||
|
|
93649d0557 | ||
|
|
46181dfa08 | ||
|
|
44066b292e | ||
|
|
2f84fae344 | ||
|
|
d75548e219 | ||
|
|
ad416413fe | ||
|
|
f99ea0bf16 | ||
|
|
d97be1e7aa | ||
|
|
01019ad546 | ||
|
|
3d99061a07 | ||
|
|
4c6ed1b530 | ||
|
|
50f0d03735 | ||
|
|
9461a3e889 | ||
|
|
65a69b2009 | ||
|
|
c07e4f45fb | ||
|
|
2fc8169d00 | ||
|
|
a152943047 | ||
|
|
4444af6938 | ||
|
|
ed0b41a425 | ||
|
|
41879fa27c | ||
|
|
110108daf6 | ||
|
|
27deaf91cc | ||
|
|
37d548db8c | ||
|
|
67c2e1f3cf | ||
|
|
efc55e77ef | ||
|
|
a1a10777a5 | ||
|
|
7282bde765 | ||
|
|
1a384e53ec | ||
|
|
00c07290ad | ||
|
|
817f92d398 | ||
|
|
d943b67270 | ||
|
|
c171524dc6 | ||
|
|
0dcff37419 | ||
|
|
65ebfc95d0 | ||
|
|
e8609526b0 | ||
|
|
4bc0015b48 | ||
|
|
bfa667c1ab | ||
|
|
cadbd65cf6 | ||
|
|
eae6d75bca | ||
|
|
f4ab363901 | ||
|
|
7c806fee8a | ||
|
|
9f3fab6de4 | ||
|
|
2c3c9c441e | ||
|
|
a7644ee487 | ||
|
|
396b98da01 | ||
|
|
d0da1f4e39 | ||
|
|
a24e73da7e | ||
|
|
3aa082fec1 | ||
|
|
70fd31afb6 | ||
|
|
c299a794b2 | ||
|
|
d3429f31a6 | ||
|
|
7b951f3e3b | ||
|
|
b0bd1b9635 | ||
|
|
10ab864a43 | ||
|
|
6a6f0e9c53 | ||
|
|
8cd19bbc26 | ||
|
|
7404c4ce6b | ||
|
|
6d336fda23 | ||
|
|
b9c45d96c1 | ||
|
|
b0e1d5dafb | ||
|
|
05369a49a4 | ||
|
|
04916756c6 | ||
|
|
2581254a02 | ||
|
|
c1b509220e | ||
|
|
676ea0629b | ||
|
|
41d6ebe536 | ||
|
|
422b93495a | ||
|
|
7246ee34bd | ||
|
|
4d3e8bee84 | ||
|
|
7dffa1a701 | ||
|
|
ba16411bf1 | ||
|
|
de1da57286 | ||
|
|
6687a1eba0 | ||
|
|
f0998271ba | ||
|
|
a84b972121 | ||
|
|
e5b51564fd | ||
|
|
6ddcdf2812 | ||
|
|
bc177ad740 | ||
|
|
7b471588ab | ||
|
|
d7a4e4fde6 | ||
|
|
4986b69c62 | ||
|
|
7a22f4ac88 | ||
|
|
f059b6fd0d | ||
|
|
65fb41c562 | ||
|
|
c3d8002a76 | ||
|
|
6c98369719 | ||
|
|
f5b0ca63e8 | ||
|
|
90303689db | ||
|
|
17a5767108 | ||
|
|
e04b619071 | ||
|
|
858a93ccd2 | ||
|
|
e22d1f0a6c | ||
|
|
9994dd49f7 | ||
|
|
5cf1740977 | ||
|
|
297bce3a89 | ||
|
|
d8faef0146 | ||
|
|
57efcef072 | ||
|
|
5c58a86d86 | ||
|
|
ab06ed75c3 | ||
|
|
6f812dad90 | ||
|
|
aa50d88575 | ||
|
|
971e879744 | ||
|
|
dc2191f228 | ||
|
|
a270b926b3 | ||
|
|
051bcf1dc2 | ||
|
|
578e5ea6b7 | ||
|
|
56525f8008 | ||
|
|
32559bab5d | ||
|
|
cb1f3411ce | ||
|
|
6fb916eccd | ||
|
|
313736e3c6 | ||
|
|
f8eccde99b | ||
|
|
c5cc372d7f | ||
|
|
8b5ba1aa97 |
1
.github/unraid.svg
vendored
Normal file
1
.github/unraid.svg
vendored
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" version="1.2" viewBox="0 0 1000 1000"><defs><linearGradient id="a" x1="-900" x2="-100" y1="-100" y2="-900" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#e32929"/><stop offset="1" stop-color="#ff8d30"/></linearGradient></defs><path fill="url(#a)" d="M1000 500.1v376.4c0 57.5-43.4 110.1-100 120.9-8.4 1.6-17.1 2.5-25.6 2.5-250.1.1-500.2.1-750.3.1-61.3 0-114.8-47-122.8-108q-.3-2.1-.6-4.1-.2-2-.3-4.1-.2-2-.3-4v-4.1C0 624.9 0 374.2 0 123.5 0 66 43.4 13.3 100 2.6 108.4 1 117.1.1 125.6.1 375.9 0 626.2 0 876.5 0 934 0 986.7 43.4 997.4 100c1.5 8.4 2.5 17.1 2.5 25.6.1 124.8.1 249.7.1 374.5z"/><path fill="#fff" d="M481.6 392.1h36.5v216.2h-36.5zm-356 0h36.5v216.2h-36.5zm178 242h36.5v82.5h-36.5zm-89.3-92.7h36.5v133.7h-36.5zm178 0h36.5V675h-36.5zm445.8-149.3h36.5v216.1h-36.5zm-178-107.8h36.5v82.6h-36.5zm89.3 41.5h36.5v133.1h-36.5zm-178.6 0h36.5v133h-36.5z"/></svg>
|
||||
|
After Width: | Height: | Size: 915 B |
74
.github/workflows/lint-test-build-web.yml
vendored
74
.github/workflows/lint-test-build-web.yml
vendored
@@ -1,74 +0,0 @@
|
||||
name: Lint, Test, and Build Web Components
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
lint-web:
|
||||
defaults:
|
||||
run:
|
||||
working-directory: web
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Create env file
|
||||
run: |
|
||||
touch .env
|
||||
echo VITE_ACCOUNT=${{ vars.VITE_ACCOUNT }} >> .env
|
||||
echo VITE_CONNECT=${{ vars.VITE_CONNECT }} >> .env
|
||||
echo VITE_UNRAID_NET=${{ vars.VITE_UNRAID_NET }} >> .env
|
||||
echo VITE_CALLBACK_KEY=${{ vars.VITE_CALLBACK_KEY }} >> .env
|
||||
cat .env
|
||||
|
||||
- name: Install node
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
cache: "npm"
|
||||
cache-dependency-path: "web/package-lock.json"
|
||||
node-version-file: "web/.nvmrc"
|
||||
|
||||
- name: Installing node deps
|
||||
run: npm install
|
||||
|
||||
- name: Lint files
|
||||
run: npm run lint
|
||||
|
||||
build-web:
|
||||
defaults:
|
||||
run:
|
||||
working-directory: web
|
||||
runs-on: ubuntu-latest
|
||||
needs: [lint-web]
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Create env file
|
||||
run: |
|
||||
touch .env
|
||||
echo VITE_ACCOUNT=${{ vars.VITE_ACCOUNT }} >> .env
|
||||
echo VITE_CONNECT=${{ vars.VITE_CONNECT }} >> .env
|
||||
echo VITE_UNRAID_NET=${{ vars.VITE_UNRAID_NET }} >> .env
|
||||
echo VITE_CALLBACK_KEY=${{ vars.VITE_CALLBACK_KEY }} >> .env
|
||||
cat .env
|
||||
|
||||
- name: Install node
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
cache: "npm"
|
||||
cache-dependency-path: "web/package-lock.json"
|
||||
node-version-file: "web/.nvmrc"
|
||||
|
||||
- name: Installing node deps
|
||||
run: npm install
|
||||
|
||||
- name: Build
|
||||
run: npm run build
|
||||
|
||||
- name: Upload build to Github artifacts
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: unraid-web
|
||||
path: web/.nuxt/nuxt-custom-elements/dist/unraid-components
|
||||
396
.github/workflows/main.yml
vendored
396
.github/workflows/main.yml
vendored
@@ -1,6 +1,7 @@
|
||||
name: CI - Main (API)
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
@@ -12,6 +13,18 @@ concurrency:
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
release-please:
|
||||
if: startsWith(github.ref, 'refs/tags/')
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
steps:
|
||||
- id: release
|
||||
uses: googleapis/release-please-action@v4
|
||||
outputs:
|
||||
releases_created: ${{ steps.release.outputs.releases_created }}
|
||||
tag_name: ${{ steps.release.outputs.tag_name }}
|
||||
start:
|
||||
# This prevents a tag running twice as it'll have a "tag" and a "commit" event
|
||||
# We only want the tag to run the action as it'll be able to create the release notes
|
||||
@@ -21,202 +34,167 @@ jobs:
|
||||
- name: Validate branch and tag
|
||||
run: exit 0
|
||||
|
||||
lint-api:
|
||||
continue-on-error: true
|
||||
build-test-api:
|
||||
name: Build and Test API
|
||||
runs-on: ubuntu-latest
|
||||
defaults:
|
||||
run:
|
||||
working-directory: api
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Reconfigure git to use HTTP authenti:cation
|
||||
run: >
|
||||
git config --global url."https://github.com/".insteadOf
|
||||
ssh://git@github.com/
|
||||
|
||||
- name: Install node
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version-file: "api/.nvmrc"
|
||||
|
||||
# - name: Get npm cache directory
|
||||
# id: npm-cache
|
||||
# run: echo "::set-output name=dir::$(npm config get cache)"
|
||||
|
||||
# - name: Load npm cache
|
||||
# uses: actions/cache@v3
|
||||
# with:
|
||||
# path: ${{ steps.npm-cache.outputs.dir }}
|
||||
# key: ${{ runner.os }}-npm-cache-${{ hashFiles('**/package-lock.json') }}
|
||||
|
||||
- name: Install libvirt-dev
|
||||
run: sudo apt-get update && sudo apt-get install libvirt-dev
|
||||
|
||||
- name: Installing node deps
|
||||
run: npm install
|
||||
|
||||
- name: Lint files
|
||||
run: npm run lint
|
||||
|
||||
test-api:
|
||||
defaults:
|
||||
run:
|
||||
working-directory: api
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Reconfigure git to use HTTP authentication
|
||||
run: >
|
||||
git config --global url."https://github.com/".insteadOf
|
||||
ssh://git@github.com/
|
||||
|
||||
- name: Build Docker Compose
|
||||
run: |
|
||||
docker network create mothership_default
|
||||
docker-compose build builder
|
||||
|
||||
- name: Run Docker Compose
|
||||
run: docker-compose run builder npm run coverage
|
||||
|
||||
lint-web:
|
||||
defaults:
|
||||
run:
|
||||
working-directory: web
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Create env file
|
||||
run: |
|
||||
touch .env
|
||||
echo VITE_ACCOUNT=${{ vars.VITE_ACCOUNT }} >> .env
|
||||
echo VITE_CONNECT=${{ vars.VITE_CONNECT }} >> .env
|
||||
echo VITE_UNRAID_NET=${{ vars.VITE_UNRAID_NET }} >> .env
|
||||
echo VITE_CALLBACK_KEY=${{ vars.VITE_CALLBACK_KEY }} >> .env
|
||||
cat .env
|
||||
|
||||
- name: Install node
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
cache: "npm"
|
||||
cache-dependency-path: "web/package-lock.json"
|
||||
node-version-file: "web/.nvmrc"
|
||||
|
||||
- name: Installing node deps
|
||||
run: npm install
|
||||
|
||||
- name: Lint files
|
||||
run: npm run lint
|
||||
|
||||
build-api:
|
||||
defaults:
|
||||
run:
|
||||
working-directory: api
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
outputs:
|
||||
API_VERSION: ${{ steps.build-pack-binary.outputs.API_VERSION }}
|
||||
API_VERSION: ${{ steps.vars.outputs.API_VERSION }}
|
||||
API_MD5: ${{ steps.set-hashes.outputs.API_MD5 }}
|
||||
API_SHA256: ${{ steps.set-hashes.outputs.API_SHA256 }}
|
||||
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Add SSH deploy key
|
||||
uses: shimataro/ssh-key-action@v2
|
||||
uses: actions/checkout@v4
|
||||
- name: Build with Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
with:
|
||||
key: ${{ secrets.UNRAID_BOT_SSH_KEY }}
|
||||
known_hosts: ${{ secrets.KNOWN_HOSTS }}
|
||||
|
||||
- name: Install node
|
||||
uses: actions/setup-node@v3
|
||||
install: true
|
||||
platforms: linux/amd64
|
||||
- name: Build Builder
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
node-version-file: "api/.nvmrc"
|
||||
context: ./api
|
||||
push: false
|
||||
tags: builder:latest
|
||||
cache-from: type=gha,ref=builder:latest
|
||||
cache-to: type=gha,mode=max,ref=builder:latest
|
||||
load: true
|
||||
- name: Lint inside of the docker container
|
||||
continue-on-error: true
|
||||
run: |
|
||||
docker run --rm builder npm run lint
|
||||
|
||||
- name: Install libvirt-dev
|
||||
run: sudo apt-get update && sudo apt-get install libvirt-dev
|
||||
|
||||
- name: Installing node deps
|
||||
run: npm install
|
||||
|
||||
- name: Install pkg and node-prune
|
||||
run: npm i -g pkg && curl -sf https://gobinaries.com/tj/node-prune | sh
|
||||
|
||||
# See https://github.com/apollographql/subscriptions-transport-ws/issues/433
|
||||
- name: Patch subscriptions-transport-ws
|
||||
run: npm run patch:subscriptions-transport-ws
|
||||
|
||||
|
||||
- name: Build and Pack
|
||||
- name: Test inside of the docker container
|
||||
run: |
|
||||
git fetch --depth=2 origin main
|
||||
if git diff --name-only --relative=api origin/main HEAD | grep -q '.'; then
|
||||
docker run --rm builder npm run coverage
|
||||
else
|
||||
echo "No changes in /api folder, skipping coverage."
|
||||
fi
|
||||
- name: Get Git Short Sha and API version
|
||||
id: vars
|
||||
run: |
|
||||
GIT_SHA=$(git rev-parse --short HEAD)
|
||||
IS_TAGGED=$(git describe --tags --abbrev=0 --exact-match || echo '')
|
||||
PACKAGE_LOCK_VERSION=$(jq -r '.version' package-lock.json)
|
||||
echo "GIT_SHA=$GIT_SHA" >> $GITHUB_OUTPUT
|
||||
echo "IS_TAGGED=$IS_TAGGED" >> $GITHUB_OUTPUT
|
||||
echo "PACKAGE_LOCK_VERSION=$PACKAGE_LOCK_VERSION" >> $GITHUB_OUTPUT
|
||||
echo "API_VERSION=$([[ -n "$IS_TAGGED" ]] && echo "$PACKAGE_LOCK_VERSION" || echo "${PACKAGE_LOCK_VERSION}+${GIT_SHA}")" >> $GITHUB_OUTPUT
|
||||
- name: Build inside of the docker container
|
||||
id: build-pack-binary
|
||||
run: WORKDIR=${{ github.workspace }} && npm run build-pkg
|
||||
run: |
|
||||
docker run --rm -v ${{ github.workspace }}/api/deploy/release:/app/deploy/release -e API_VERSION=${{ steps.vars.outputs.API_VERSION }} builder npm run build-and-pack
|
||||
|
||||
- name: Set Hashes
|
||||
id: set-hashes
|
||||
run: |
|
||||
API_MD5=$(md5sum ${{ github.workspace }}/api/deploy/release/*.tgz | awk '{ print $1 }')
|
||||
API_SHA256=$(sha256sum ${{ github.workspace }}/api/deploy/release/*.tgz | awk '{ print $1 }')
|
||||
echo "::set-output name=API_MD5::${API_MD5}"
|
||||
echo "::set-output name=API_SHA256::${API_SHA256}"
|
||||
echo "API_MD5=$(md5sum ${{ github.workspace }}/api/deploy/release/*.tgz | awk '{ print $1 }')" >> $GITHUB_OUTPUT
|
||||
echo "API_SHA256=$(sha256sum ${{ github.workspace }}/api/deploy/release/*.tgz | awk '{ print $1 }')" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Upload tgz to Github artifacts
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: unraid-api
|
||||
path: ${{ github.workspace }}/api/deploy/release/*.tgz
|
||||
|
||||
build-web:
|
||||
build-unraid-ui:
|
||||
name: Build Unraid UI Library
|
||||
defaults:
|
||||
run:
|
||||
working-directory: web
|
||||
working-directory: unraid-ui
|
||||
runs-on: ubuntu-latest
|
||||
environment:
|
||||
name: production
|
||||
needs: [lint-web]
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Create env file
|
||||
run: |
|
||||
touch .env
|
||||
echo VITE_ACCOUNT=${{ vars.VITE_ACCOUNT }} >> .env
|
||||
echo VITE_CONNECT=${{ vars.VITE_CONNECT }} >> .env
|
||||
echo VITE_UNRAID_NET=${{ vars.VITE_UNRAID_NET }} >> .env
|
||||
echo VITE_CALLBACK_KEY=${{ vars.VITE_CALLBACK_KEY }} >> .env
|
||||
cat .env
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install node
|
||||
uses: actions/setup-node@v3
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
cache: "npm"
|
||||
cache-dependency-path: "web/package-lock.json"
|
||||
node-version-file: "web/.nvmrc"
|
||||
cache-dependency-path: |
|
||||
unraid-ui/package-lock.json
|
||||
node-version-file: ".nvmrc"
|
||||
|
||||
- name: Installing node deps
|
||||
- name: Install dependencies
|
||||
run: npm install
|
||||
|
||||
- name: Build
|
||||
run: npm run build
|
||||
|
||||
- name: Make Built Node Artifact
|
||||
run: |
|
||||
mkdir unraid-ui-dist
|
||||
mv dist/ unraid-ui-dist/dist/
|
||||
mv src unraid-ui-dist/src
|
||||
ln -s unraid-ui-dist/dist/node_modules unraid-ui-dist/node_modules
|
||||
mv tailwind.config.ts unraid-ui-dist/tailwind.config.ts
|
||||
mv package.json unraid-ui-dist/package.json
|
||||
ls unraid-ui-dist
|
||||
|
||||
- name: Upload Artifact to Github
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: unraid-ui
|
||||
path: unraid-ui/unraid-ui-dist
|
||||
|
||||
build-web:
|
||||
needs: [build-unraid-ui]
|
||||
name: Build Web App
|
||||
environment:
|
||||
name: production
|
||||
defaults:
|
||||
run:
|
||||
working-directory: web
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Create env file
|
||||
run: |
|
||||
touch .env
|
||||
echo VITE_ACCOUNT=${{ vars.VITE_ACCOUNT }} >> .env
|
||||
echo VITE_CONNECT=${{ vars.VITE_CONNECT }} >> .env
|
||||
echo VITE_UNRAID_NET=${{ vars.VITE_UNRAID_NET }} >> .env
|
||||
echo VITE_CALLBACK_KEY=${{ vars.VITE_CALLBACK_KEY }} >> .env
|
||||
cat .env
|
||||
|
||||
- name: Install node
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
cache: "npm"
|
||||
cache-dependency-path: |
|
||||
web/package-lock.json
|
||||
node-version-file: "web/.nvmrc"
|
||||
|
||||
- name: Setup Just
|
||||
uses: extractions/setup-just@v2
|
||||
|
||||
- name: Installing deps
|
||||
run: just setup
|
||||
|
||||
- name: Lint files
|
||||
continue-on-error: true
|
||||
run: npm run lint
|
||||
|
||||
- name: Test
|
||||
run: npm run test:ci
|
||||
|
||||
- name: Build
|
||||
run: npm run build
|
||||
|
||||
- name: Upload build to Github artifacts
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: unraid-web
|
||||
path: web/.nuxt/nuxt-custom-elements/dist/unraid-components
|
||||
|
||||
build-plugin:
|
||||
needs: [lint-api, lint-web, test-api, build-api, build-web]
|
||||
needs: [build-test-api, build-web]
|
||||
defaults:
|
||||
run:
|
||||
working-directory: plugin
|
||||
@@ -227,22 +205,29 @@ jobs:
|
||||
with:
|
||||
timezoneLinux: "America/Los_Angeles"
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
- name: Download unraid web components
|
||||
uses: actions/download-artifact@v3
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: unraid-web
|
||||
path: ./plugin/source/dynamix.unraid.net/usr/local/emhttp/plugins/dynamix.my.servers/unraid-components
|
||||
- name: Build Plugin
|
||||
run: |
|
||||
cd source/dynamix.unraid.net
|
||||
export API_VERSION=${{needs.build-api.outputs.API_VERSION}}
|
||||
export API_MD5=${{needs.build-api.outputs.API_MD5}}
|
||||
export API_SHA256=${{needs.build-api.outputs.API_SHA256}}
|
||||
bash ./pkg_build.sh s
|
||||
export API_VERSION=${{needs.build-test-api.outputs.API_VERSION}}
|
||||
export API_MD5=${{needs.build-test-api.outputs.API_MD5}}
|
||||
export API_SHA256=${{needs.build-test-api.outputs.API_SHA256}}
|
||||
if [ -z "${API_VERSION}" ] ||
|
||||
[ -z "${API_MD5}" ] ||
|
||||
[ -z "${API_SHA256}" ]; then
|
||||
echo "Error: One or more required variables are not set."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
bash ./pkg_build.sh s ${{github.event.pull_request.number}}
|
||||
bash ./pkg_build.sh p
|
||||
- name: Upload binary txz and plg to Github artifacts
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: connect-files
|
||||
path: |
|
||||
@@ -251,7 +236,69 @@ jobs:
|
||||
retention-days: 5
|
||||
if-no-files-found: error
|
||||
|
||||
release-pull-request:
|
||||
if: |
|
||||
github.event_name == 'pull_request' &&
|
||||
github.event.pull_request.base.ref == 'main'
|
||||
runs-on: ubuntu-latest
|
||||
needs: [build-plugin]
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Make PR Release Folder
|
||||
run: mkdir pr-release/
|
||||
|
||||
- name: Download unraid-api binary tgz
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: unraid-api
|
||||
path: pr-release
|
||||
|
||||
- name: Download plugin binary tgz
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: connect-files
|
||||
|
||||
- name: Write Changelog to Plugin XML
|
||||
run: |
|
||||
# Capture the pull request number and latest commit message
|
||||
pr_number="${{ github.event.pull_request.number }}"
|
||||
commit_message=$(git log -1 --pretty=%B)
|
||||
|
||||
# Clean up newlines, escape special characters, and handle line breaks
|
||||
notes=$(echo -e "Pull Request Build: ${pr_number}\n${commit_message}" | \
|
||||
sed ':a;N;$!ba;s/\n/\\n/g' | \
|
||||
sed -e 's/[&\\/]/\\&/g')
|
||||
|
||||
# Replace <CHANGES> tag content in the file
|
||||
sed -i -z -E "s/<CHANGES>(.*)<\/CHANGES>/<CHANGES>\n${notes}\n<\/CHANGES>/g" "plugins/dynamix.unraid.net.staging.plg"
|
||||
|
||||
- name: Copy other release files to pr-release
|
||||
run: |
|
||||
cp archive/*.txz pr-release/
|
||||
cp plugins/dynamix.unraid.net.staging.plg pr-release/
|
||||
|
||||
- name: Upload to Cloudflare
|
||||
uses: jakejarvis/s3-sync-action@v0.5.1
|
||||
env:
|
||||
AWS_S3_ENDPOINT: ${{ secrets.CF_ENDPOINT }}
|
||||
AWS_S3_BUCKET: ${{ secrets.CF_BUCKET_PREVIEW }}
|
||||
AWS_ACCESS_KEY_ID: ${{ secrets.CF_ACCESS_KEY_ID }}
|
||||
AWS_SECRET_ACCESS_KEY: ${{ secrets.CF_SECRET_ACCESS_KEY }}
|
||||
AWS_REGION: "auto"
|
||||
SOURCE_DIR: pr-release
|
||||
DEST_DIR: unraid-api/pr/${{ github.event.pull_request.number }}
|
||||
- name: Comment URL
|
||||
uses: thollander/actions-comment-pull-request@v3
|
||||
with:
|
||||
message: |
|
||||
This plugin has been deployed to Cloudflare R2 and is available for testing.
|
||||
Download it at this URL: [https://preview.dl.unraid.net/unraid-api/pr/${{ github.event.pull_request.number }}/dynamix.unraid.net.staging.plg](https://preview.dl.unraid.net/unraid-api/pr/${{ github.event.pull_request.number }}/dynamix.unraid.net.staging.plg)
|
||||
|
||||
release-staging:
|
||||
environment:
|
||||
name: staging
|
||||
# Only release if this is a push to the main branch
|
||||
if: startsWith(github.ref, 'refs/heads/main')
|
||||
runs-on: ubuntu-latest
|
||||
@@ -259,19 +306,19 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Make Staging Release Folder
|
||||
run: mkdir staging-release/
|
||||
|
||||
- name: Download unraid-api binary tgz
|
||||
uses: actions/download-artifact@v3
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: unraid-api
|
||||
path: staging-release
|
||||
|
||||
- name: Download plugin binary tgz
|
||||
uses: actions/download-artifact@v3
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: connect-files
|
||||
|
||||
@@ -282,9 +329,9 @@ jobs:
|
||||
removeMarkdown: false
|
||||
filePath: "./api/CHANGELOG.md"
|
||||
|
||||
- name: Run LS in unraid-api folder
|
||||
- name: Copy Files for Staging Release
|
||||
run: |
|
||||
cp archive/dynamix.unraid.net.staging-*.txz staging-release/
|
||||
cp archive/*.txz staging-release/
|
||||
cp plugins/dynamix.unraid.net.staging.plg staging-release/
|
||||
ls -al staging-release
|
||||
|
||||
@@ -298,6 +345,17 @@ jobs:
|
||||
source: staging-release
|
||||
out_dir: unraid-api
|
||||
|
||||
- name: Upload Staging Plugin to Cloudflare Bucket
|
||||
uses: jakejarvis/s3-sync-action@v0.5.1
|
||||
env:
|
||||
AWS_S3_ENDPOINT: ${{ secrets.CF_ENDPOINT }}
|
||||
AWS_S3_BUCKET: ${{ secrets.CF_BUCKET_PREVIEW }}
|
||||
AWS_ACCESS_KEY_ID: ${{ secrets.CF_ACCESS_KEY_ID }}
|
||||
AWS_SECRET_ACCESS_KEY: ${{ secrets.CF_SECRET_ACCESS_KEY }}
|
||||
AWS_REGION: "auto"
|
||||
SOURCE_DIR: staging-release
|
||||
DEST_DIR: unraid-api
|
||||
|
||||
create-draft-release:
|
||||
# Only create new draft if this is a version tag
|
||||
if: |
|
||||
@@ -307,15 +365,15 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Download unraid-api binary tgz
|
||||
uses: actions/download-artifact@v3
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: unraid-api
|
||||
|
||||
- name: Download plugin binary tgz
|
||||
uses: actions/download-artifact@v3
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: connect-files
|
||||
|
||||
@@ -327,6 +385,6 @@ jobs:
|
||||
files: |
|
||||
unraid-api-*.tgz
|
||||
plugins/dynamix.unraid.net*
|
||||
archive/dynamix.unraid.net*
|
||||
archive/*
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
82
.github/workflows/pull-request-web.yml
vendored
82
.github/workflows/pull-request-web.yml
vendored
@@ -1,82 +0,0 @@
|
||||
name: Pull Request Web
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- 'web/**'
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}-web
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
lint-web:
|
||||
defaults:
|
||||
run:
|
||||
working-directory: web
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Create env file
|
||||
run: |
|
||||
touch .env
|
||||
echo VITE_ACCOUNT=${{ vars.VITE_ACCOUNT }} >> .env
|
||||
echo VITE_CONNECT=${{ vars.VITE_CONNECT }} >> .env
|
||||
echo VITE_UNRAID_NET=${{ vars.VITE_UNRAID_NET }} >> .env
|
||||
echo VITE_CALLBACK_KEY=${{ vars.VITE_CALLBACK_KEY }} >> .env
|
||||
cat .env
|
||||
|
||||
- name: Install node
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
cache: "npm"
|
||||
cache-dependency-path: "web/package-lock.json"
|
||||
node-version-file: "web/.nvmrc"
|
||||
|
||||
- name: Installing node deps
|
||||
run: npm install
|
||||
|
||||
- name: Lint files
|
||||
run: npm run lint
|
||||
|
||||
build-web:
|
||||
defaults:
|
||||
run:
|
||||
working-directory: web
|
||||
runs-on: ubuntu-latest
|
||||
environment:
|
||||
name: production
|
||||
needs: [lint-web]
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Create env file
|
||||
run: |
|
||||
touch .env
|
||||
echo VITE_ACCOUNT=${{ vars.VITE_ACCOUNT }} >> .env
|
||||
echo VITE_CONNECT=${{ vars.VITE_CONNECT }} >> .env
|
||||
echo VITE_UNRAID_NET=${{ vars.VITE_UNRAID_NET }} >> .env
|
||||
echo VITE_CALLBACK_KEY=${{ vars.VITE_CALLBACK_KEY }} >> .env
|
||||
cat .env
|
||||
|
||||
- name: Install node
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
cache: "npm"
|
||||
cache-dependency-path: "web/package-lock.json"
|
||||
node-version-file: "web/.nvmrc"
|
||||
|
||||
- name: Installing node deps
|
||||
run: npm install
|
||||
|
||||
- name: Build
|
||||
run: npm run build
|
||||
|
||||
- name: Upload build to Github artifacts
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: unraid-web
|
||||
path: web/.nuxt/nuxt-custom-elements/dist/unraid-components
|
||||
183
.github/workflows/pull-request.yml
vendored
183
.github/workflows/pull-request.yml
vendored
@@ -1,183 +0,0 @@
|
||||
name: Pull Request
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- api/**
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
lint-api:
|
||||
services:
|
||||
registry: # Using a local registry is ~3x faster than exporting the image to docker agent
|
||||
image: registry:2
|
||||
ports:
|
||||
- 5000:5000
|
||||
|
||||
continue-on-error: true
|
||||
defaults:
|
||||
run:
|
||||
working-directory: api
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
persist-credentials: true
|
||||
- uses: docker/setup-buildx-action@v2
|
||||
with:
|
||||
# network=host driver-opt needed to push to local registry
|
||||
driver-opts: network=host
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@v4
|
||||
with:
|
||||
context: api
|
||||
target: builder
|
||||
push: true
|
||||
tags: localhost:5000/unraid-api:builder
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
- name: Lint
|
||||
run: |
|
||||
docker run localhost:5000/unraid-api:builder npm run lint
|
||||
|
||||
test-api:
|
||||
services:
|
||||
registry: # Using a local registry is ~3x faster than exporting the image to docker agent
|
||||
image: registry:2
|
||||
ports:
|
||||
- 5000:5000
|
||||
|
||||
defaults:
|
||||
run:
|
||||
working-directory: api
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
persist-credentials: true
|
||||
- uses: docker/setup-buildx-action@v2
|
||||
with:
|
||||
# network=host driver-opt needed to push to local registry
|
||||
driver-opts: network=host
|
||||
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@v4
|
||||
with:
|
||||
context: api
|
||||
target: builder
|
||||
push: true
|
||||
tags: localhost:5000/unraid-api:builder
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
- name: Test
|
||||
run: |
|
||||
docker run localhost:5000/unraid-api:builder npm run coverage
|
||||
|
||||
build-api:
|
||||
services:
|
||||
registry: # Using a local registry is ~3x faster than exporting the image to docker agent
|
||||
image: registry:2
|
||||
ports:
|
||||
- 5000:5000
|
||||
|
||||
defaults:
|
||||
run:
|
||||
working-directory: api
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
outputs:
|
||||
API_VERSION: ${{ steps.build-pack-binary.outputs.API_VERSION }}
|
||||
API_MD5: ${{ steps.set-hashes.outputs.API_MD5 }}
|
||||
API_SHA256: ${{ steps.set-hashes.outputs.API_SHA256 }}
|
||||
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
persist-credentials: true
|
||||
- uses: docker/setup-buildx-action@v2
|
||||
with:
|
||||
# network=host driver-opt needed to push to local registry
|
||||
driver-opts: network=host
|
||||
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@v4
|
||||
with:
|
||||
context: api
|
||||
target: builder
|
||||
push: true
|
||||
tags: localhost:5000/unraid-api:builder
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
|
||||
- name: Run Build
|
||||
run: docker run -e GIT_SHA=$(git rev-parse --short HEAD) -e IS_TAGGED=$(git describe --tags --abbrev=0 --exact-match) -v $(pwd)/deploy:/app/deploy/ localhost:5000/unraid-api:builder npm run build-pkg
|
||||
|
||||
- name: Set Hashes
|
||||
id: set-hashes
|
||||
run: |
|
||||
API_MD5=$(md5sum ${{ github.workspace }}/api/deploy/release/*.tgz | awk '{ print $1 }')
|
||||
API_SHA256=$(sha256sum ${{ github.workspace }}/api/deploy/release/*.tgz | awk '{ print $1 }')
|
||||
echo "::set-output name=API_MD5::${API_MD5}"
|
||||
echo "::set-output name=API_SHA256::${API_SHA256}"
|
||||
|
||||
- name: Upload tgz to Github artifacts
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: unraid-api
|
||||
path: ${{ github.workspace }}/api/deploy/release/*.tgz
|
||||
|
||||
- name: Parse Changelog
|
||||
id: changelog
|
||||
uses: ocavue/changelog-parser-action@v1
|
||||
with:
|
||||
removeMarkdown: false
|
||||
filePath: "./api/CHANGELOG.md"
|
||||
|
||||
- name: View release notes
|
||||
run: |
|
||||
escapedNotes=$(sed -e 's/[&\\/]/\\&/g; s/$/\\/' -e '$s/\\$//' <<<"${{steps.changelog.outputs.latestBody}}")
|
||||
echo "${escapedNotes}"
|
||||
build-plugin:
|
||||
defaults:
|
||||
run:
|
||||
working-directory: plugin
|
||||
runs-on: ubuntu-latest
|
||||
needs: [lint-api, test-api, build-api]
|
||||
steps:
|
||||
- name: Set Timezone
|
||||
uses: szenius/set-timezone@v1.2
|
||||
with:
|
||||
timezoneLinux: "America/Los_Angeles"
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@v3
|
||||
- name: Build Plugin
|
||||
run: |
|
||||
cd source/dynamix.unraid.net
|
||||
export API_VERSION=${{needs.build-api.outputs.API_VERSION}}
|
||||
export API_MD5=${{needs.build-api.outputs.API_MD5}}
|
||||
export API_SHA256=${{needs.build-api.outputs.API_SHA256}}
|
||||
bash ./pkg_build.sh s
|
||||
bash ./pkg_build.sh p
|
||||
- name: Create release notes
|
||||
run: |
|
||||
LAST_RELEASE=$(git tag --list --sort=v:refname | tail -1)
|
||||
echo ${LAST_RELEASE}
|
||||
RELEASE_NOTES=$(git log "$LAST_RELEASE...HEAD" --pretty=format:"- %s [\`%h\`](http://github.com/$GITHUB_REPOSITORY/commit/%H)" --reverse)
|
||||
echo "${RELEASE_NOTES}"
|
||||
# escapedNotes=$(sed -e 's/[&\\/]/\\&/g; s/$/\\/' -e '$s/\\$//' <<<"${RELEASE_NOTES}")
|
||||
# sed -i -z -E "s/<CHANGES>(.*)<\/CHANGES>/<CHANGES>\n${escapedNotes}\n<\/CHANGES>/g" "plugins/dynamix.unraid.net.staging.plg"
|
||||
- name: Upload binary txz and plg to Github artifacts
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: connect-files
|
||||
path: |
|
||||
${{ github.workspace }}/plugin/archive/*.txz
|
||||
${{ github.workspace }}/plugin/plugins/*.plg
|
||||
retention-days: 5
|
||||
if-no-files-found: error
|
||||
57
.github/workflows/release-production.yml
vendored
Normal file
57
.github/workflows/release-production.yml
vendored
Normal file
@@ -0,0 +1,57 @@
|
||||
name: Publish Release to Digital Ocean
|
||||
|
||||
on:
|
||||
release:
|
||||
types: [published]
|
||||
|
||||
jobs:
|
||||
publish-to-digital-ocean:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Download Release Artifacts (Plugins)
|
||||
uses: dsaltares/fetch-gh-release-asset@master
|
||||
with:
|
||||
file: ".*"
|
||||
regex: true
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
target: "./"
|
||||
version: "latest"
|
||||
|
||||
- uses: cardinalby/git-get-release-action@v1
|
||||
id: release-info
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
latest: true
|
||||
prerelease: false
|
||||
- name: Get Release Changelog
|
||||
run: |
|
||||
notes=$(cat << EOF
|
||||
${{ steps.release-info.outputs.body }}
|
||||
EOF
|
||||
)
|
||||
escapedNotes=$(sed -e 's/[&\\/]/\\&/g; s/$/\\/' -e '$s/\\$//' <<<"$notes")
|
||||
sed -i -z -E "s/<CHANGES>(.*)<\/CHANGES>/<CHANGES>\n${escapedNotes}\n<\/CHANGES>/g" "dynamix.unraid.net.plg"
|
||||
sed -i -z -E "s/<CHANGES>(.*)<\/CHANGES>/<CHANGES>\n${escapedNotes}\n<\/CHANGES>/g" "dynamix.unraid.net.staging.plg"
|
||||
|
||||
- name: Upload All Release Files to DO Spaces
|
||||
uses: BetaHuhn/do-spaces-action@v2
|
||||
with:
|
||||
access_key: ${{ secrets.DO_ACCESS_KEY }}
|
||||
secret_key: ${{ secrets.DO_SECRET_KEY }}
|
||||
space_name: ${{ secrets.DO_SPACE_NAME }}
|
||||
space_region: ${{ secrets.DO_SPACE_REGION }}
|
||||
source: "."
|
||||
out_dir: unraid-api
|
||||
|
||||
- name: Upload Staging Plugin to Cloudflare Bucket
|
||||
uses: jakejarvis/s3-sync-action@v0.5.1
|
||||
env:
|
||||
AWS_S3_ENDPOINT: ${{ secrets.CF_ENDPOINT }}
|
||||
AWS_S3_BUCKET: ${{ secrets.CF_BUCKET }}
|
||||
AWS_ACCESS_KEY_ID: ${{ secrets.CF_ACCESS_KEY_ID }}
|
||||
AWS_SECRET_ACCESS_KEY: ${{ secrets.CF_SECRET_ACCESS_KEY }}
|
||||
AWS_REGION: 'auto'
|
||||
SOURCE_DIR: "."
|
||||
DEST_DIR: unraid-api
|
||||
15
.gitignore
vendored
15
.gitignore
vendored
@@ -24,6 +24,7 @@ build/Release
|
||||
# Dependency directories
|
||||
node_modules/
|
||||
jspm_packages/
|
||||
unraid-ui/node_modules/
|
||||
|
||||
# TypeScript v1 declaration files
|
||||
typings/
|
||||
@@ -50,17 +51,21 @@ typings/
|
||||
.next
|
||||
|
||||
# Visual Studio Code workspace
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
.vscode/sftp.json
|
||||
.history/
|
||||
|
||||
# OSX
|
||||
.DS_Store
|
||||
|
||||
# Jetbrains Settings Files
|
||||
.idea
|
||||
|
||||
# Temp dir for tests
|
||||
test/__temp__/*
|
||||
|
||||
# Built files
|
||||
dist
|
||||
unraid-ui/storybook-static
|
||||
|
||||
# Typescript
|
||||
typescript
|
||||
@@ -71,7 +76,7 @@ typescript
|
||||
# Github actions
|
||||
RELEASE_NOTES.md
|
||||
|
||||
# Docker Deploy Folder
|
||||
# Docker Deploy Folder
|
||||
deploy/*
|
||||
!deploy/.gitkeep
|
||||
|
||||
@@ -84,4 +89,6 @@ deploy/*
|
||||
.cache
|
||||
.output
|
||||
.env*
|
||||
!.env.example
|
||||
!.env.example
|
||||
|
||||
fb_keepalive
|
||||
|
||||
1
.release-please-manifest.json
Normal file
1
.release-please-manifest.json
Normal file
@@ -0,0 +1 @@
|
||||
{"api":"3.10.0","web":"3.10.0"}
|
||||
10
.vscode/extensions.json
vendored
Normal file
10
.vscode/extensions.json
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"recommendations": [
|
||||
"natizyskunk.sftp",
|
||||
"davidanson.vscode-markdownlint",
|
||||
"bmewburn.vscode-intelephense-client",
|
||||
"foxundermoon.shell-format",
|
||||
"timonwong.shellcheck",
|
||||
"esbenp.prettier-vscode"
|
||||
]
|
||||
}
|
||||
43
.vscode/settings.json
vendored
43
.vscode/settings.json
vendored
@@ -1,30 +1,15 @@
|
||||
{
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.fixAll": false,
|
||||
"source.fixAll.eslint": true
|
||||
},
|
||||
"workbench.colorCustomizations": {
|
||||
"activityBar.activeBackground": "#78797d",
|
||||
"activityBar.background": "#78797d",
|
||||
"activityBar.foreground": "#e7e7e7",
|
||||
"activityBar.inactiveForeground": "#e7e7e799",
|
||||
"activityBarBadge.background": "#df9fac",
|
||||
"activityBarBadge.foreground": "#15202b",
|
||||
"commandCenter.border": "#e7e7e799",
|
||||
"sash.hoverBorder": "#78797d",
|
||||
"statusBar.background": "#5f6063",
|
||||
"statusBar.foreground": "#e7e7e7",
|
||||
"statusBarItem.hoverBackground": "#78797d",
|
||||
"statusBarItem.remoteBackground": "#5f6063",
|
||||
"statusBarItem.remoteForeground": "#e7e7e7",
|
||||
"titleBar.activeBackground": "#5f6063",
|
||||
"titleBar.activeForeground": "#e7e7e7",
|
||||
"titleBar.inactiveBackground": "#5f606399",
|
||||
"titleBar.inactiveForeground": "#e7e7e799"
|
||||
},
|
||||
"peacock.color": "#5f6063",
|
||||
"i18n-ally.localesPaths": [
|
||||
"locales"
|
||||
],
|
||||
"i18n-ally.keystyle": "flat"
|
||||
}
|
||||
"files.associations": {
|
||||
"*.page": "php"
|
||||
},
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.fixAll": "never",
|
||||
"source.fixAll.eslint": "explicit"
|
||||
},
|
||||
"i18n-ally.localesPaths": [
|
||||
"locales"
|
||||
],
|
||||
"i18n-ally.keystyle": "flat",
|
||||
"eslint.experimental.useFlatConfig": true
|
||||
}
|
||||
|
||||
22
.vscode/sftp-template.json
vendored
Normal file
22
.vscode/sftp-template.json
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"_comment": "rename this file to .vscode/sftp.json and replace name/host/privateKeyPath for your system",
|
||||
"name": "Tower",
|
||||
"host": "Tower.local",
|
||||
"protocol": "sftp",
|
||||
"port": 22,
|
||||
"username": "root",
|
||||
"privateKeyPath": "C:/Users/username/.ssh/tower",
|
||||
"remotePath": "/",
|
||||
"context": "plugin/source/dynamix.unraid.net/",
|
||||
"uploadOnSave": true,
|
||||
"useTempFile": false,
|
||||
"openSsh": false,
|
||||
"ignore": [
|
||||
"// comment: ignore dot files/dirs in root of repo",
|
||||
".github",
|
||||
".vscode",
|
||||
".git",
|
||||
".DS_Store"
|
||||
]
|
||||
}
|
||||
|
||||
1
api/.dockerignore
Normal file
1
api/.dockerignore
Normal file
@@ -0,0 +1 @@
|
||||
node_modules/
|
||||
@@ -1,11 +1,14 @@
|
||||
PATHS_UNRAID_DATA=./dev/data # Where we store plugin data (e.g. permissions.json)
|
||||
PATHS_STATES=./dev/states # Where .ini files live (e.g. vars.ini)
|
||||
PATHS_AUTH_SESSIONS=./dev/sessions # Where user sessions live
|
||||
PATHS_AUTH_KEY=./dev/keys # Auth key directory
|
||||
PATHS_DYNAMIX_BASE=./dev/dynamix # Dynamix's data directory
|
||||
PATHS_DYNAMIX_CONFIG_DEFAULT=./dev/dynamix/default.cfg # Dynamix's default config file, which ships with unraid
|
||||
PATHS_DYNAMIX_CONFIG=./dev/dynamix/dynamix.cfg # Dynamix's config file
|
||||
PATHS_MY_SERVERS_CONFIG=./dev/Unraid.net/myservers.cfg # My servers config file
|
||||
PATHS_MY_SERVERS_FB=./dev/Unraid.net/fb_keepalive # My servers flashbackup timekeeper file
|
||||
PATHS_KEYFILE_BASE=./dev/Unraid.net # Keyfile location
|
||||
PATHS_MACHINE_ID=./dev/data/machine-id
|
||||
|
||||
ENVIRONMENT="development"
|
||||
NODE_ENV="development"
|
||||
PORT="3001"
|
||||
@@ -13,6 +16,8 @@ PLAYGROUND=true
|
||||
INTROSPECTION=true
|
||||
MOTHERSHIP_GRAPHQL_LINK="http://authenticator:3000/graphql"
|
||||
NODE_TLS_REJECT_UNAUTHORIZED=0
|
||||
BYPASS_PERMISSION_CHECKS=true
|
||||
BYPASS_CORS_CHECKS=false
|
||||
BYPASS_PERMISSION_CHECKS=false
|
||||
BYPASS_CORS_CHECKS=true
|
||||
CHOKIDAR_USEPOLLING=true
|
||||
LOG_TRANSPORT=console
|
||||
LOG_LEVEL=trace
|
||||
11
api/.env.test
Normal file
11
api/.env.test
Normal file
@@ -0,0 +1,11 @@
|
||||
VERSION="THIS_WILL_BE_REPLACED_WHEN_BUILT"
|
||||
|
||||
PATHS_UNRAID_DATA=./dev/data # Where we store plugin data (e.g. permissions.json)
|
||||
PATHS_STATES=./dev/states # Where .ini files live (e.g. vars.ini)
|
||||
PATHS_DYNAMIX_BASE=./dev/dynamix # Dynamix's data directory
|
||||
PATHS_DYNAMIX_CONFIG=./dev/dynamix/dynamix.cfg # Dynamix's config file
|
||||
PATHS_MY_SERVERS_CONFIG=./dev/Unraid.net/myservers.cfg # My servers config file
|
||||
PATHS_MY_SERVERS_FB=./dev/Unraid.net/fb_keepalive # My servers flashbackup timekeeper file
|
||||
PATHS_KEYFILE_BASE=./dev/Unraid.net # Keyfile location
|
||||
PORT=5000
|
||||
NODE_ENV=test
|
||||
@@ -1,47 +0,0 @@
|
||||
/** @type {import('eslint').Linter.Config} */
|
||||
module.exports = {
|
||||
root: true,
|
||||
plugins: [
|
||||
'@typescript-eslint/eslint-plugin',
|
||||
'unused-imports',
|
||||
'eslint-plugin-unicorn',
|
||||
],
|
||||
ignorePatterns: ['src/graphql/generated/**/*.ts', '*.test.ts', 'tsup.config.ts', 'vite.config.ts'],
|
||||
parser: '@typescript-eslint/parser',
|
||||
rules: {
|
||||
'@typescript-eslint/no-redundant-type-constituents': 'off',
|
||||
'@typescript-eslint/no-unsafe-call': 'off',
|
||||
'@typescript-eslint/naming-convention': 'off',
|
||||
'@typescript-eslint/no-unsafe-assignment': 'off',
|
||||
'@typescript-eslint/no-unsafe-return': 'off',
|
||||
'@typescript-eslint/ban-types': 'off',
|
||||
'@typescript-eslint/no-explicit-any': 'off',
|
||||
'@typescript-eslint/consistent-type-imports': [
|
||||
'warn',
|
||||
{ fixStyle: 'inline-type-imports' },
|
||||
],
|
||||
'unicorn/numeric-separators-style': [
|
||||
'error',
|
||||
{ number: { minimumDigits: 0, groupLength: 3 } },
|
||||
],
|
||||
'import/no-cycle': 'off', // Change this to "error" to find circular imports
|
||||
'@typescript-eslint/no-use-before-define': ['error'],
|
||||
'no-multiple-empty-lines': ['error', { max: 1, maxBOF: 0, maxEOF: 1 }],
|
||||
},
|
||||
overrides: [
|
||||
{
|
||||
files: ['*.ts'],
|
||||
extends: [
|
||||
'eslint:recommended',
|
||||
'plugin:@typescript-eslint/recommended',
|
||||
],
|
||||
parserOptions: {
|
||||
project: true,
|
||||
tsconfigRootDir: __dirname,
|
||||
},
|
||||
rules: {
|
||||
'@typescript-eslint/no-explicit-any': 'off',
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
21
api/.eslintrc.ts
Normal file
21
api/.eslintrc.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
|
||||
import type { Linter } from 'eslint';
|
||||
import eslint from '@eslint/js';
|
||||
import tseslint from 'typescript-eslint';
|
||||
|
||||
export default tseslint.config(eslint.configs.recommended, ...tseslint.configs.recommended, {
|
||||
rules: {
|
||||
'@typescript-eslint/no-redundant-type-constituents': 'off',
|
||||
'@typescript-eslint/no-unsafe-call': 'off',
|
||||
'@typescript-eslint/naming-convention': 'off',
|
||||
'@typescript-eslint/no-unsafe-assignment': 'off',
|
||||
'@typescript-eslint/no-unsafe-return': 'off',
|
||||
'@typescript-eslint/ban-types': 'off',
|
||||
'@typescript-eslint/no-explicit-any': 'off',
|
||||
'@typescript-eslint/no-empty-object-type': 'off',
|
||||
'no-use-before-define': ['off'],
|
||||
'no-multiple-empty-lines': ['error', { max: 1, maxBOF: 0, maxEOF: 1 }],
|
||||
'@typescript-eslint/no-unused-vars': 'off',
|
||||
'@typescript-eslint/no-unused-expressions': 'off',
|
||||
},
|
||||
});
|
||||
82
api/.gitignore
vendored
Normal file
82
api/.gitignore
vendored
Normal file
@@ -0,0 +1,82 @@
|
||||
# Logs
|
||||
./logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||
lib-cov
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage
|
||||
coverage-ts
|
||||
|
||||
# nyc test coverage
|
||||
.nyc_output
|
||||
|
||||
# node-waf configuration
|
||||
.lock-wscript
|
||||
|
||||
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||
build/Release
|
||||
|
||||
# Dependency directories
|
||||
node_modules/
|
||||
jspm_packages/
|
||||
|
||||
# TypeScript v1 declaration files
|
||||
typings/
|
||||
|
||||
# Optional npm cache directory
|
||||
.npm
|
||||
|
||||
# Optional eslint cache
|
||||
.eslintcache
|
||||
|
||||
# Optional REPL history
|
||||
.node_repl_history
|
||||
|
||||
# Output of 'npm pack'
|
||||
*.tgz
|
||||
|
||||
# Yarn Integrity file
|
||||
.yarn-integrity
|
||||
|
||||
# dotenv environment variables file
|
||||
.env
|
||||
|
||||
# next.js build output
|
||||
.next
|
||||
|
||||
# Visual Studio Code workspace
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
|
||||
# OSX
|
||||
.DS_Store
|
||||
|
||||
# Temp dir for tests
|
||||
test/__temp__/*
|
||||
|
||||
# Built files
|
||||
dist
|
||||
|
||||
# Typescript
|
||||
typescript
|
||||
|
||||
# Ultra runner
|
||||
.ultra.cache.json
|
||||
|
||||
# Github actions
|
||||
RELEASE_NOTES.md
|
||||
|
||||
# Docker Deploy Folder
|
||||
deploy/*
|
||||
!deploy/.gitkeep
|
||||
|
||||
# pkg cache
|
||||
.pkg-cache
|
||||
|
||||
# IDE Settings Files
|
||||
.idea
|
||||
@@ -1 +1 @@
|
||||
18.17.1
|
||||
v20
|
||||
38
api/.prettierrc.cjs
Normal file
38
api/.prettierrc.cjs
Normal file
@@ -0,0 +1,38 @@
|
||||
/**
|
||||
* @see https://prettier.io/docs/en/configuration.html
|
||||
* @type {import("prettier").Config}
|
||||
*/
|
||||
module.exports = {
|
||||
trailingComma: 'es5',
|
||||
tabWidth: 4,
|
||||
semi: true,
|
||||
singleQuote: true,
|
||||
printWidth: 105,
|
||||
plugins: ['@ianvs/prettier-plugin-sort-imports'],
|
||||
// decorators-legacy lets the import sorter transform files with decorators
|
||||
importOrderParserPlugins: ['typescript', 'decorators-legacy'],
|
||||
importOrder: [
|
||||
/**----------------------
|
||||
* Nest.js & node.js imports
|
||||
*------------------------**/
|
||||
'<TYPES>^@nestjs(/.*)?$',
|
||||
'^@nestjs(/.*)?$', // matches imports starting with @nestjs
|
||||
'<TYPES>^(node:)',
|
||||
'<BUILTIN_MODULES>', // Node.js built-in modules
|
||||
'',
|
||||
/**----------------------
|
||||
* Third party packages
|
||||
*------------------------**/
|
||||
'<TYPES>',
|
||||
'<THIRD_PARTY_MODULES>', // Imports not matched by other special words or groups.
|
||||
'',
|
||||
/**----------------------
|
||||
* Application Code
|
||||
*------------------------**/
|
||||
'<TYPES>^@app(/.*)?$', // matches type imports starting with @app
|
||||
'^@app(/.*)?$',
|
||||
'',
|
||||
'<TYPES>^[.]',
|
||||
'^[.]', // relative imports
|
||||
],
|
||||
};
|
||||
7
api/.vscode/settings.json
vendored
Normal file
7
api/.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"eslint.lintTask.options": "--flag unstable_ts_config",
|
||||
"eslint.options": {
|
||||
"flags": ["unstable_ts_config"],
|
||||
"overrideConfigFile": ".eslintrc.ts"
|
||||
}
|
||||
}
|
||||
455
api/CHANGELOG.md
455
api/CHANGELOG.md
@@ -2,6 +2,459 @@
|
||||
|
||||
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
|
||||
|
||||
## [3.11.0](https://github.com/unraid/api/compare/v3.10.1...v3.11.0) (2024-09-11)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* reduce how often rc.flashbackup checks for changes ([793d368](https://github.com/unraid/api/commit/793d3681404018e0ae933df0ad111809220ad138))
|
||||
* send api_version to flash/activate endpoint ([d8ec20e](https://github.com/unraid/api/commit/d8ec20ea6aa35aa241abd8424c4d884bcbb8f590))
|
||||
* update ProvisionCert.php to clean hosts file when it runs ([fbe20c9](https://github.com/unraid/api/commit/fbe20c97b327849c15a4b34f5f53476edaefbeb6))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* remove local flash backup ratelimit file on uninstall/update ([abf207b](https://github.com/unraid/api/commit/abf207b077861798c53739b1965207f87d5633b3))
|
||||
|
||||
### [3.10.1](https://github.com/unraid/api/compare/v3.10.0...v3.10.1) (2024-09-03)
|
||||
|
||||
## [3.10.0](https://github.com/unraid/api/compare/v3.9.0...v3.10.0) (2024-09-03)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* add a timestamp to flash backup ([#877](https://github.com/unraid/api/issues/877)) ([b868fd4](https://github.com/unraid/api/commit/b868fd46c3886b2182245a61f20be6df65e46abe))
|
||||
* add environment to docker-compose ([2ee4683](https://github.com/unraid/api/commit/2ee46839095e3b8ee287cfe10f29ae9a39dcff68))
|
||||
* add global agent ([#897](https://github.com/unraid/api/issues/897)) ([8b0dc69](https://github.com/unraid/api/commit/8b0dc69f65bd3e280a21c50aab221334f7341b1c))
|
||||
* add logrotate to cron in nestjs ([#839](https://github.com/unraid/api/issues/839)) ([5c91524](https://github.com/unraid/api/commit/5c91524d849147c0ac7925f3a2f1cce67ffe75de))
|
||||
* add new staging url for connect website ([#841](https://github.com/unraid/api/issues/841)) ([4cfc07b](https://github.com/unraid/api/commit/4cfc07b6763dbb79b68cf01f7eaf7cf33370d4db))
|
||||
* add support for expiration in var.ini ([#833](https://github.com/unraid/api/issues/833)) ([0474c2e](https://github.com/unraid/api/commit/0474c2e14fa462d2e1ec6d9a7f974660385d073e))
|
||||
* always show DRA even if disabled ([ab708c0](https://github.com/unraid/api/commit/ab708c0df634e21bf81595412d7de0be3ff7c392))
|
||||
* close log on exit ([d6ede86](https://github.com/unraid/api/commit/d6ede86eca6301342cdf35bf1f9365896b5e5009))
|
||||
* create stable hash based on apikey rather than hostname ([ecf5554](https://github.com/unraid/api/commit/ecf5554e304cc7dee78cb1f206ef4e80222c3e64))
|
||||
* disable all legacy dashboard and network logic ([6784f4b](https://github.com/unraid/api/commit/6784f4b6e1a12b2f30bfa9ab4fe6310994bd18ae))
|
||||
* dynamic remote access using remote queries ([f7fc0c4](https://github.com/unraid/api/commit/f7fc0c431561978054d2ff37d1aa644865e846ec))
|
||||
* extraOrigins public, remove origin listener ([91f96ba](https://github.com/unraid/api/commit/91f96ba818773d6e71dde1ff52a4c8ec21ba6b5d))
|
||||
* fix codegen ([d0bf5bb](https://github.com/unraid/api/commit/d0bf5bb8197b11f7a250ca5392890184a1dbeff7))
|
||||
* fix exit hook and cleanup docker scripts ([#758](https://github.com/unraid/api/issues/758)) ([a9ff73e](https://github.com/unraid/api/commit/a9ff73e0a04c67e9ec9d5551cf0b1f124be6f381))
|
||||
* fix logging format on start and stop ([c6720c3](https://github.com/unraid/api/commit/c6720c331df055480d2d65b37290f4978fe429da))
|
||||
* local start command ([99b6007](https://github.com/unraid/api/commit/99b6007ba30353084a8bea54cc0e782fcc1bfea4))
|
||||
* log config recreation reason ([f36c72f](https://github.com/unraid/api/commit/f36c72f5ad44b7e41d1726fa181dc2b9f594c72c))
|
||||
* move dynamic remote access to be fully api controlled ([206eb6b](https://github.com/unraid/api/commit/206eb6b74aa83047237e5f6c94c46b08c6507168))
|
||||
* move FQDN urls to a generic parser ([#899](https://github.com/unraid/api/issues/899)) ([246595e](https://github.com/unraid/api/commit/246595ee7acd8370906a759cbe618def4f52c173))
|
||||
* nestjs initial query implementation ([#748](https://github.com/unraid/api/issues/748)) ([075d7f2](https://github.com/unraid/api/commit/075d7f25785bf686779b7fee1d5ea39f09ff3ea8))
|
||||
* new key types in API ([e42f9dc](https://github.com/unraid/api/commit/e42f9dc95be03e8389aac443f2147c07a316d48d))
|
||||
* regTy swapped ([564b25c](https://github.com/unraid/api/commit/564b25cf5ce0a62d40f8d63d44c81e9c8560e0be))
|
||||
* remove dashboard resolver completely in favor of direct field resolvers ([1cd1ee5](https://github.com/unraid/api/commit/1cd1ee534825ccf775208c438ae0bd777bbe4d39))
|
||||
* remove dashboard types ([2f0167d](https://github.com/unraid/api/commit/2f0167dc89835bcf8aa946425c5c6683221fd763))
|
||||
* run codegen and update build script ([07512ad](https://github.com/unraid/api/commit/07512adc13ee0d819db45ff6c5c5f58a0ba31141))
|
||||
* settings through the API ([#867](https://github.com/unraid/api/issues/867)) ([e73624b](https://github.com/unraid/api/commit/e73624be6be8bc2c70d898b8601a88cc8d20a3e4))
|
||||
* swap to docker compose from docker-compose ([ec16a6a](https://github.com/unraid/api/commit/ec16a6aab1a2d5c836387da438fbeade07d23425))
|
||||
* swap to fragement usage on webcomponent ([42733ab](https://github.com/unraid/api/commit/42733abf6e443516ff715569333422ce80d3b1d2))
|
||||
* update tests and snapshots ([c39aa17](https://github.com/unraid/api/commit/c39aa17e4302ed56b3097ab3244d840f11eb686b))
|
||||
* upgrade a ton of dependencies ([#842](https://github.com/unraid/api/issues/842)) ([94c1746](https://github.com/unraid/api/commit/94c174620c2347a3cf3d100404635f99a5b47287))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* add serverName / description to dashboard payload ([9677aff](https://github.com/unraid/api/commit/9677aff1cd0942f36a2845f3f105601c494efd9e))
|
||||
* allow failure for log deletion ([eff3142](https://github.com/unraid/api/commit/eff31423927644be436a831126678719c2eb0621))
|
||||
* allowed origins check not working without spaces ([#838](https://github.com/unraid/api/issues/838)) ([b998b38](https://github.com/unraid/api/commit/b998b38355fab77ecc2f62bc64896766218db3d4))
|
||||
* **api:** readme discord url ([ffd5c6a](https://github.com/unraid/api/commit/ffd5c6afb64956e76df22c77104a21bc22798008))
|
||||
* build docker command updated to use dc.sh script ([0b40886](https://github.com/unraid/api/commit/0b40886e84f27a94dbf67ef4ca0cd8539ef3913e))
|
||||
* codegen on web run ([e2e67c2](https://github.com/unraid/api/commit/e2e67c21067a138d963f5f10760b84cf6a533542))
|
||||
* **deps:** update dependency @apollo/client to v3.9.5 ([#785](https://github.com/unraid/api/issues/785)) ([75b98bc](https://github.com/unraid/api/commit/75b98bc1cbca5b66ae72f52a0b6f5f58230a2473))
|
||||
* **deps:** update dependency graphql to v16.8.1 ([bff1b19](https://github.com/unraid/api/commit/bff1b19706bee1e3103e3a0a1d2fceb3154f9bba))
|
||||
* **deps:** update dependency graphql-ws to v5.15.0 ([#790](https://github.com/unraid/api/issues/790)) ([4773b13](https://github.com/unraid/api/commit/4773b132167d740d4c996efe22e0f1b99576fb9b))
|
||||
* **deps:** update dependency ws to v8.16.0 ([#815](https://github.com/unraid/api/issues/815)) ([212020e](https://github.com/unraid/api/commit/212020e78d4de0576137058a3374837b4a43e02d))
|
||||
* **deps:** update dependency wtfnode to v0.9.3 ([#901](https://github.com/unraid/api/issues/901)) ([a88482b](https://github.com/unraid/api/commit/a88482bfcbf134f55330f8728bc5c7f67c521773))
|
||||
* **deps:** update graphql-tools monorepo ([3447eb0](https://github.com/unraid/api/commit/3447eb047a1dcd575b88a96bbcef9946aca366a1))
|
||||
* **deps:** update graphql-tools monorepo (major) ([#693](https://github.com/unraid/api/issues/693)) ([3447eb0](https://github.com/unraid/api/commit/3447eb047a1dcd575b88a96bbcef9946aca366a1))
|
||||
* **deps:** update nest monorepo ([#816](https://github.com/unraid/api/issues/816)) ([4af3699](https://github.com/unraid/api/commit/4af36991b8b376f816ed51fd503a66e99675a3e7))
|
||||
* excessive logging ([89cb254](https://github.com/unraid/api/commit/89cb2544ed0e0edd33b59f15d487487e22c0ae32))
|
||||
* exit with process.exit not process.exitcode ([dcb6def](https://github.com/unraid/api/commit/dcb6def1cf3365dca819feed101160c8ad0125dc))
|
||||
* lint ([919873d](https://github.com/unraid/api/commit/919873d9edee304d99036a4a810db3789c734fbf))
|
||||
* local container startup commands cleaned up ([6c0ccb2](https://github.com/unraid/api/commit/6c0ccb2b24f98282be4db2e0b2e6362f4a187def))
|
||||
* logrotate not working due to invalid ownership of unraid-api folder ([ec0581a](https://github.com/unraid/api/commit/ec0581abf58a217f698d52d5337f2b312e5a645b))
|
||||
* optional check on api.version to allow fallback to save value ([0ac4455](https://github.com/unraid/api/commit/0ac4455f78407eca7aa1d6ee360830067a1c5c3e))
|
||||
* permission for dashboard payload ([704a530](https://github.com/unraid/api/commit/704a530653dac415766bded5e96f6060f931e591))
|
||||
* rearrange exit hook to try to fix closing ([843d3f4](https://github.com/unraid/api/commit/843d3f41162c5dbcfd7803912b1879d7a182231a))
|
||||
* revert myservers.cfg to fix test ([a7705be](https://github.com/unraid/api/commit/a7705beb0a5b32660367ad8de9b46b06f7a3bec7))
|
||||
* run hourly ([0425794](https://github.com/unraid/api/commit/0425794356a01262222e7dff2645d3629e00d0f6))
|
||||
* unused import ([065fe57](https://github.com/unraid/api/commit/065fe575f578a74d593805c3121dd7fbdfc3e5ae))
|
||||
* update snapshots ([c8a0a8e](https://github.com/unraid/api/commit/c8a0a8ec007abc0372464c7e2b44bd47b6babd94))
|
||||
|
||||
## [3.9.0](https://github.com/unraid/api/compare/api-v3.8.1...api-v3.9.0) (2024-09-03)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* add a timestamp to flash backup ([#877](https://github.com/unraid/api/issues/877)) ([b868fd4](https://github.com/unraid/api/commit/b868fd46c3886b2182245a61f20be6df65e46abe))
|
||||
* add environment to docker-compose ([2ee4683](https://github.com/unraid/api/commit/2ee46839095e3b8ee287cfe10f29ae9a39dcff68))
|
||||
* add global agent ([#897](https://github.com/unraid/api/issues/897)) ([8b0dc69](https://github.com/unraid/api/commit/8b0dc69f65bd3e280a21c50aab221334f7341b1c))
|
||||
* add logrotate to cron in nestjs ([#839](https://github.com/unraid/api/issues/839)) ([5c91524](https://github.com/unraid/api/commit/5c91524d849147c0ac7925f3a2f1cce67ffe75de))
|
||||
* add new staging url for connect website ([#841](https://github.com/unraid/api/issues/841)) ([4cfc07b](https://github.com/unraid/api/commit/4cfc07b6763dbb79b68cf01f7eaf7cf33370d4db))
|
||||
* add support for expiration in var.ini ([#833](https://github.com/unraid/api/issues/833)) ([0474c2e](https://github.com/unraid/api/commit/0474c2e14fa462d2e1ec6d9a7f974660385d073e))
|
||||
* always show DRA even if disabled ([ab708c0](https://github.com/unraid/api/commit/ab708c0df634e21bf81595412d7de0be3ff7c392))
|
||||
* close log on exit ([d6ede86](https://github.com/unraid/api/commit/d6ede86eca6301342cdf35bf1f9365896b5e5009))
|
||||
* create stable hash based on apikey rather than hostname ([ecf5554](https://github.com/unraid/api/commit/ecf5554e304cc7dee78cb1f206ef4e80222c3e64))
|
||||
* disable all legacy dashboard and network logic ([6784f4b](https://github.com/unraid/api/commit/6784f4b6e1a12b2f30bfa9ab4fe6310994bd18ae))
|
||||
* dynamic remote access using remote queries ([f7fc0c4](https://github.com/unraid/api/commit/f7fc0c431561978054d2ff37d1aa644865e846ec))
|
||||
* extraOrigins public, remove origin listener ([91f96ba](https://github.com/unraid/api/commit/91f96ba818773d6e71dde1ff52a4c8ec21ba6b5d))
|
||||
* fix codegen ([d0bf5bb](https://github.com/unraid/api/commit/d0bf5bb8197b11f7a250ca5392890184a1dbeff7))
|
||||
* fix exit hook and cleanup docker scripts ([#758](https://github.com/unraid/api/issues/758)) ([a9ff73e](https://github.com/unraid/api/commit/a9ff73e0a04c67e9ec9d5551cf0b1f124be6f381))
|
||||
* fix logging format on start and stop ([c6720c3](https://github.com/unraid/api/commit/c6720c331df055480d2d65b37290f4978fe429da))
|
||||
* local start command ([99b6007](https://github.com/unraid/api/commit/99b6007ba30353084a8bea54cc0e782fcc1bfea4))
|
||||
* log config recreation reason ([f36c72f](https://github.com/unraid/api/commit/f36c72f5ad44b7e41d1726fa181dc2b9f594c72c))
|
||||
* move dynamic remote access to be fully api controlled ([206eb6b](https://github.com/unraid/api/commit/206eb6b74aa83047237e5f6c94c46b08c6507168))
|
||||
* move FQDN urls to a generic parser ([#899](https://github.com/unraid/api/issues/899)) ([246595e](https://github.com/unraid/api/commit/246595ee7acd8370906a759cbe618def4f52c173))
|
||||
* nestjs initial query implementation ([#748](https://github.com/unraid/api/issues/748)) ([075d7f2](https://github.com/unraid/api/commit/075d7f25785bf686779b7fee1d5ea39f09ff3ea8))
|
||||
* new key types in API ([e42f9dc](https://github.com/unraid/api/commit/e42f9dc95be03e8389aac443f2147c07a316d48d))
|
||||
* regTy swapped ([564b25c](https://github.com/unraid/api/commit/564b25cf5ce0a62d40f8d63d44c81e9c8560e0be))
|
||||
* remove dashboard resolver completely in favor of direct field resolvers ([1cd1ee5](https://github.com/unraid/api/commit/1cd1ee534825ccf775208c438ae0bd777bbe4d39))
|
||||
* remove dashboard types ([2f0167d](https://github.com/unraid/api/commit/2f0167dc89835bcf8aa946425c5c6683221fd763))
|
||||
* run codegen and update build script ([07512ad](https://github.com/unraid/api/commit/07512adc13ee0d819db45ff6c5c5f58a0ba31141))
|
||||
* settings through the API ([#867](https://github.com/unraid/api/issues/867)) ([e73624b](https://github.com/unraid/api/commit/e73624be6be8bc2c70d898b8601a88cc8d20a3e4))
|
||||
* swap to docker compose from docker-compose ([ec16a6a](https://github.com/unraid/api/commit/ec16a6aab1a2d5c836387da438fbeade07d23425))
|
||||
* swap to fragement usage on webcomponent ([42733ab](https://github.com/unraid/api/commit/42733abf6e443516ff715569333422ce80d3b1d2))
|
||||
* update tests and snapshots ([c39aa17](https://github.com/unraid/api/commit/c39aa17e4302ed56b3097ab3244d840f11eb686b))
|
||||
* upgrade a ton of dependencies ([#842](https://github.com/unraid/api/issues/842)) ([94c1746](https://github.com/unraid/api/commit/94c174620c2347a3cf3d100404635f99a5b47287))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* add serverName / description to dashboard payload ([9677aff](https://github.com/unraid/api/commit/9677aff1cd0942f36a2845f3f105601c494efd9e))
|
||||
* allow failure for log deletion ([eff3142](https://github.com/unraid/api/commit/eff31423927644be436a831126678719c2eb0621))
|
||||
* allowed origins check not working without spaces ([#838](https://github.com/unraid/api/issues/838)) ([b998b38](https://github.com/unraid/api/commit/b998b38355fab77ecc2f62bc64896766218db3d4))
|
||||
* **api:** readme discord url ([ffd5c6a](https://github.com/unraid/api/commit/ffd5c6afb64956e76df22c77104a21bc22798008))
|
||||
* build docker command updated to use dc.sh script ([0b40886](https://github.com/unraid/api/commit/0b40886e84f27a94dbf67ef4ca0cd8539ef3913e))
|
||||
* codegen on web run ([e2e67c2](https://github.com/unraid/api/commit/e2e67c21067a138d963f5f10760b84cf6a533542))
|
||||
* **deps:** update dependency @apollo/client to v3.9.5 ([#785](https://github.com/unraid/api/issues/785)) ([75b98bc](https://github.com/unraid/api/commit/75b98bc1cbca5b66ae72f52a0b6f5f58230a2473))
|
||||
* **deps:** update dependency graphql to v16.8.1 ([bff1b19](https://github.com/unraid/api/commit/bff1b19706bee1e3103e3a0a1d2fceb3154f9bba))
|
||||
* **deps:** update dependency graphql-ws to v5.15.0 ([#790](https://github.com/unraid/api/issues/790)) ([4773b13](https://github.com/unraid/api/commit/4773b132167d740d4c996efe22e0f1b99576fb9b))
|
||||
* **deps:** update dependency ws to v8.16.0 ([#815](https://github.com/unraid/api/issues/815)) ([212020e](https://github.com/unraid/api/commit/212020e78d4de0576137058a3374837b4a43e02d))
|
||||
* **deps:** update dependency wtfnode to v0.9.3 ([#901](https://github.com/unraid/api/issues/901)) ([a88482b](https://github.com/unraid/api/commit/a88482bfcbf134f55330f8728bc5c7f67c521773))
|
||||
* **deps:** update graphql-tools monorepo ([3447eb0](https://github.com/unraid/api/commit/3447eb047a1dcd575b88a96bbcef9946aca366a1))
|
||||
* **deps:** update graphql-tools monorepo (major) ([#693](https://github.com/unraid/api/issues/693)) ([3447eb0](https://github.com/unraid/api/commit/3447eb047a1dcd575b88a96bbcef9946aca366a1))
|
||||
* **deps:** update nest monorepo ([#816](https://github.com/unraid/api/issues/816)) ([4af3699](https://github.com/unraid/api/commit/4af36991b8b376f816ed51fd503a66e99675a3e7))
|
||||
* excessive logging ([89cb254](https://github.com/unraid/api/commit/89cb2544ed0e0edd33b59f15d487487e22c0ae32))
|
||||
* exit with process.exit not process.exitcode ([dcb6def](https://github.com/unraid/api/commit/dcb6def1cf3365dca819feed101160c8ad0125dc))
|
||||
* lint ([919873d](https://github.com/unraid/api/commit/919873d9edee304d99036a4a810db3789c734fbf))
|
||||
* local container startup commands cleaned up ([6c0ccb2](https://github.com/unraid/api/commit/6c0ccb2b24f98282be4db2e0b2e6362f4a187def))
|
||||
* logrotate not working due to invalid ownership of unraid-api folder ([ec0581a](https://github.com/unraid/api/commit/ec0581abf58a217f698d52d5337f2b312e5a645b))
|
||||
* optional check on api.version to allow fallback to save value ([0ac4455](https://github.com/unraid/api/commit/0ac4455f78407eca7aa1d6ee360830067a1c5c3e))
|
||||
* permission for dashboard payload ([704a530](https://github.com/unraid/api/commit/704a530653dac415766bded5e96f6060f931e591))
|
||||
* rearrange exit hook to try to fix closing ([843d3f4](https://github.com/unraid/api/commit/843d3f41162c5dbcfd7803912b1879d7a182231a))
|
||||
* revert myservers.cfg to fix test ([a7705be](https://github.com/unraid/api/commit/a7705beb0a5b32660367ad8de9b46b06f7a3bec7))
|
||||
* run hourly ([0425794](https://github.com/unraid/api/commit/0425794356a01262222e7dff2645d3629e00d0f6))
|
||||
* unused import ([065fe57](https://github.com/unraid/api/commit/065fe575f578a74d593805c3121dd7fbdfc3e5ae))
|
||||
* update snapshots ([c8a0a8e](https://github.com/unraid/api/commit/c8a0a8ec007abc0372464c7e2b44bd47b6babd94))
|
||||
|
||||
### [3.8.1](https://github.com/unraid/api/compare/v3.8.0...v3.8.1) (2024-08-13)
|
||||
|
||||
## [3.8.0](https://github.com/unraid/api/compare/v3.7.1...v3.8.0) (2024-08-13)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* always force push ([662f3ce](https://github.com/unraid/api/commit/662f3ce440593e609c64364726f7da16dda0972b))
|
||||
* don't allow flash backup repos larger than 500MB ([#890](https://github.com/unraid/api/issues/890)) ([30a32f5](https://github.com/unraid/api/commit/30a32f5fe684bb32c084c4125aade5e63ffd788b))
|
||||
* downgradeOs callback for non stable osCurrentBranch ([17c4489](https://github.com/unraid/api/commit/17c4489e97bda504ca45e360591655ded166c355))
|
||||
* settings through the API ([#867](https://github.com/unraid/api/issues/867)) ([e73624b](https://github.com/unraid/api/commit/e73624be6be8bc2c70d898b8601a88cc8d20a3e4))
|
||||
* swap to docker compose from docker-compose ([ec16a6a](https://github.com/unraid/api/commit/ec16a6aab1a2d5c836387da438fbeade07d23425))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* apolloClient types ([f14c767](https://github.com/unraid/api/commit/f14c7673735b92aa167e9e8dcb14a045bcfea994))
|
||||
* **deps:** update dependency @vue/apollo-composable to v4.0.2 ([#787](https://github.com/unraid/api/issues/787)) ([edfc846](https://github.com/unraid/api/commit/edfc8464b0e0c2f38003ae8420e81532fd18351f))
|
||||
* formattedRegTm type ([748906e](https://github.com/unraid/api/commit/748906e15d30c6162e2f08f28724c9104c81d123))
|
||||
* i18n t prop type ([96d519f](https://github.com/unraid/api/commit/96d519f3e6b96ea7c4dc60616522216de20ee140))
|
||||
* lint error for web components ([bc27b20](https://github.com/unraid/api/commit/bc27b20524934cf896efb84a131cd270431c508c))
|
||||
* lint issues ([853dc19](https://github.com/unraid/api/commit/853dc195b13fff29160afb44f9ff11d4dd6a3232))
|
||||
* swap undefined to null ([ebba976](https://github.com/unraid/api/commit/ebba9769873a6536e3fce65978e6475d93280560))
|
||||
* tailwind config types ([0f77e55](https://github.com/unraid/api/commit/0f77e5596db3356b5dc05129b3ce215a8809e1dc))
|
||||
* ts-expect-error unneeded ([ee4d4e9](https://github.com/unraid/api/commit/ee4d4e9f12b4488ff39445bc72c1b83a9d93e993))
|
||||
* type check ([606aad7](https://github.com/unraid/api/commit/606aad703d91b72a14e15da3100dfa355052ed58))
|
||||
* type errors round 1 ([977d5da](https://github.com/unraid/api/commit/977d5daf04012f16e7b6602167338f0bc363735a))
|
||||
* update status button alignment ([4f2deaf](https://github.com/unraid/api/commit/4f2deaf70e5caa9f29fc5b2974b278f80b7b3a8a))
|
||||
|
||||
### [3.7.1](https://github.com/unraid/api/compare/v3.7.0...v3.7.1) (2024-05-15)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* reboot required and available edge case ([#885](https://github.com/unraid/api/issues/885)) ([76e9cdf](https://github.com/unraid/api/commit/76e9cdf81f06a19c2e4c9a40a4d8e062bad2a607))
|
||||
|
||||
## [3.7.0](https://github.com/unraid/api/compare/v3.6.0...v3.7.0) (2024-05-14)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* add a timestamp to flash backup ([#877](https://github.com/unraid/api/issues/877)) ([b868fd4](https://github.com/unraid/api/commit/b868fd46c3886b2182245a61f20be6df65e46abe))
|
||||
* add support for outgoing proxies ([#863](https://github.com/unraid/api/issues/863)) ([223693e](https://github.com/unraid/api/commit/223693e0981d5f2884a1f8b8baf03d4dc58e8cb2))
|
||||
* array state on registration page ([d36fef0](https://github.com/unraid/api/commit/d36fef0545ddb820e67e8bc6cb42ea3644021d66))
|
||||
* downgradeOs callback ([154a976](https://github.com/unraid/api/commit/154a976109f0a32653a2851988420707631327ca))
|
||||
* Flash Backup requires connection to mothership ([#868](https://github.com/unraid/api/issues/868)) ([d127208](https://github.com/unraid/api/commit/d127208b5e0f7f9991f515f95b0e266d38cf3287))
|
||||
* **plg:** install prevent downgrade of shared page & php files ([#873](https://github.com/unraid/api/issues/873)) ([4ac72b1](https://github.com/unraid/api/commit/4ac72b16692c4246c9d2c0b53b23d8b2d95f5de6))
|
||||
* **plg:** plg install prevent web component downgrade ([8703bd4](https://github.com/unraid/api/commit/8703bd498108f5c05562584a708bd2306e53f7a6))
|
||||
* postbuild script to add timestamp to web component manifest ([47f08ea](https://github.com/unraid/api/commit/47f08ea3594a91098f67718c0123110c7b5f86f7))
|
||||
* registration page server error heading + subheading ([6038ebd](https://github.com/unraid/api/commit/6038ebdf39bf47f2cb5c0b1de84764795374f018))
|
||||
* remove cron to download JS daily ([#864](https://github.com/unraid/api/issues/864)) ([33f6d6b](https://github.com/unraid/api/commit/33f6d6b343de07dbe70de863926906736d42f371)), closes [#529](https://github.com/unraid/api/issues/529)
|
||||
* ui to allow second update without reboot ([b0f2d10](https://github.com/unraid/api/commit/b0f2d102917f54ab33f0ad10863522b8ff8e3ce5))
|
||||
* UI Update OS Cancel ([7c02308](https://github.com/unraid/api/commit/7c02308964d5e21990427a2c626c9db2d9e1fed0))
|
||||
* UnraidUpdateCancel script ([b73bdc0](https://github.com/unraid/api/commit/b73bdc021764762ed12dca494e1345412a45c677))
|
||||
* **web:** callback types myKeys & linkKey ([c88ee01](https://github.com/unraid/api/commit/c88ee01827396c3fa8a30bb88c4be712c80b1f4f))
|
||||
* **web:** Registration key linked to account status ([8f6182d](https://github.com/unraid/api/commit/8f6182d426453b73aa19c5f0f59469fa07571694))
|
||||
* **web:** registration page array status messaging ([23ef5a9](https://github.com/unraid/api/commit/23ef5a975e0d5ff0c246c2df5e6c2cb38979d12a))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **api:** readme discord url ([ffd5c6a](https://github.com/unraid/api/commit/ffd5c6afb64956e76df22c77104a21bc22798008))
|
||||
* keep minor enhancements from [#872](https://github.com/unraid/api/issues/872) ([#878](https://github.com/unraid/api/issues/878)) ([94a5aa8](https://github.com/unraid/api/commit/94a5aa87b9979fe0f02f884ac61298473bb3271a))
|
||||
* plugin file deployment script ([780d87d](https://github.com/unraid/api/commit/780d87d6589a5469f47ac3fdfd50610ecfc394c8))
|
||||
* prevent corrupt case model in state.php ([#874](https://github.com/unraid/api/issues/874)) ([4ad31df](https://github.com/unraid/api/commit/4ad31dfea9192146dbd2c90bc64a913c696ab0b7))
|
||||
* prevent local dev from throwing ssl error ([051f647](https://github.com/unraid/api/commit/051f6474becf3b25b242cdc6ceee67247b79f8ba))
|
||||
* rc.flashbackup needs to check both signed in and connected ([#882](https://github.com/unraid/api/issues/882)) ([ac8068c](https://github.com/unraid/api/commit/ac8068c9b084622d46fe2c9cb320b793c9ea8c52))
|
||||
* update os cancel refresh on update page ([213c16b](https://github.com/unraid/api/commit/213c16ba3d5a84ebf4965f9d2f4024c66605a613))
|
||||
* **web:** discord url ([1a6f4c6](https://github.com/unraid/api/commit/1a6f4c6db4ef0e5eefac467ec6583b14cb3546c4))
|
||||
* **web:** lint unused rebootVersion ([e198ec9](https://github.com/unraid/api/commit/e198ec9d458e262c412c2dcb5a9d279238de1730))
|
||||
* **web:** registration component remove unused ref ([76f556b](https://github.com/unraid/api/commit/76f556bd64b95ba96af795c9edfa045ebdff4444))
|
||||
|
||||
## [3.6.0](https://github.com/unraid/api/compare/v3.5.3...v3.6.0) (2024-03-26)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* server config enum message w/ ineligible support ([#861](https://github.com/unraid/api/issues/861)) ([4d3a351](https://github.com/unraid/api/commit/4d3a3510777090788573f4cee83694a0dc6f8df5))
|
||||
|
||||
### [3.5.3](https://github.com/unraid/api/compare/v3.5.2...v3.5.3) (2024-03-25)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* regDevs usage to allow more flexibility for STARTER ([#860](https://github.com/unraid/api/issues/860)) ([92a9600](https://github.com/unraid/api/commit/92a9600f3a242c5f263f1672eab81054b9cf4fae))
|
||||
|
||||
### [3.5.2](https://github.com/unraid/api/compare/v3.5.1...v3.5.2) (2024-03-06)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **deps:** update dependency vue-i18n to v9.10.1 ([#813](https://github.com/unraid/api/issues/813)) ([69b599c](https://github.com/unraid/api/commit/69b599c5ed8d44864201a32b4d952427d454dc74))
|
||||
* **deps:** update dependency wretch to v2.8.0 ([#814](https://github.com/unraid/api/issues/814)) ([66900b4](https://github.com/unraid/api/commit/66900b495b82b923264897d38b1529a22b10aa1c))
|
||||
* update os check modal button conditionals ([282a836](https://github.com/unraid/api/commit/282a83625f417ccefe090b65cc6b73a084727a87))
|
||||
* update os check modal ineligible date format ([83083de](https://github.com/unraid/api/commit/83083de1e698f73a35635ae6047dcf49fd4b8114))
|
||||
|
||||
### [3.5.1](https://github.com/unraid/api/compare/v3.5.0...v3.5.1) (2024-02-29)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* build docker command updated to use dc.sh script ([0b40886](https://github.com/unraid/api/commit/0b40886e84f27a94dbf67ef4ca0cd8539ef3913e))
|
||||
* date format in UnraidCheck.php ([#852](https://github.com/unraid/api/issues/852)) ([6465f2d](https://github.com/unraid/api/commit/6465f2d7b2394090f35e29cdd680d98ce37f3728))
|
||||
* **deps:** update dependency @apollo/client to v3.9.5 ([#785](https://github.com/unraid/api/issues/785)) ([75b98bc](https://github.com/unraid/api/commit/75b98bc1cbca5b66ae72f52a0b6f5f58230a2473))
|
||||
* **deps:** update dependency @heroicons/vue to v2.1.1 ([#804](https://github.com/unraid/api/issues/804)) ([a0eb7ee](https://github.com/unraid/api/commit/a0eb7ee3ec459dbe1992a7f85bf194da30395a74))
|
||||
* **deps:** update dependency focus-trap to v7.5.4 ([#788](https://github.com/unraid/api/issues/788)) ([fe000e8](https://github.com/unraid/api/commit/fe000e83825e82cac558d3277664a440e59c0e4a))
|
||||
* **deps:** update dependency graphql-ws to v5.15.0 ([#790](https://github.com/unraid/api/issues/790)) ([4773b13](https://github.com/unraid/api/commit/4773b132167d740d4c996efe22e0f1b99576fb9b))
|
||||
* display dropdown for pro key no connect installed ([#848](https://github.com/unraid/api/issues/848)) ([b559604](https://github.com/unraid/api/commit/b55960429895b46627f1cd3ed1683ee527e62944))
|
||||
* dropdown reboot link text ([#849](https://github.com/unraid/api/issues/849)) ([a8ed5e5](https://github.com/unraid/api/commit/a8ed5e5628bc71fb783a03c3db92d21805243738))
|
||||
* os updates rc to stable ([bf1bd88](https://github.com/unraid/api/commit/bf1bd887d60ac085bf4aeae90f11be3b45ee1182))
|
||||
* state connect values without connect installed ([e47de6c](https://github.com/unraid/api/commit/e47de6c2c5db7a2a1a9b24099feb02023b3a7bbf))
|
||||
* state php breaking with double quotes in server description ([c6e92aa](https://github.com/unraid/api/commit/c6e92aa3157c9fe9e7b83580881ebcc1cbd03658))
|
||||
* state php special chars for html attributes ([#853](https://github.com/unraid/api/issues/853)) ([dd4139c](https://github.com/unraid/api/commit/dd4139cf1a7ae5c6f9b00111c33ae124bb17e630))
|
||||
* unraid-api missing start command + var defaults ([ceb4c58](https://github.com/unraid/api/commit/ceb4c587d20c7527f2b36a3278c310b0e657bfba))
|
||||
* unraid-api.php $param1 fallback ([909c79c](https://github.com/unraid/api/commit/909c79c8c82500aea1a0d4d00766f788103c5fe3))
|
||||
|
||||
## [3.5.0](https://github.com/unraid/api/compare/v3.4.0...v3.5.0) (2024-02-07)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* add manage account link to all versions of upc dropdown ([678e620](https://github.com/unraid/api/commit/678e620c1902a376b1866265711d5722b4119d8e))
|
||||
* add new staging url for connect website ([#841](https://github.com/unraid/api/issues/841)) ([4cfc07b](https://github.com/unraid/api/commit/4cfc07b6763dbb79b68cf01f7eaf7cf33370d4db))
|
||||
* also ship to cloudflare ([#844](https://github.com/unraid/api/issues/844)) ([41c4210](https://github.com/unraid/api/commit/41c42103685209592b272f81a877702da04d0915))
|
||||
* button add underline-hover-red style option ([f2fa5fa](https://github.com/unraid/api/commit/f2fa5fa49675ef461330be7b7eb3e3e4106983b0))
|
||||
* changelog modal ([2ddbacd](https://github.com/unraid/api/commit/2ddbacd137cc5748244c3d25ac91f82e64d77f99))
|
||||
* check update response modal ([39678f0](https://github.com/unraid/api/commit/39678f0bb0ddc5f87ea7f5ed80a0472100ea8b5d))
|
||||
* create WebguiCheckForUpdate endpoint ([41d546e](https://github.com/unraid/api/commit/41d546eea5fcf6593d7b5047274c074bb89c1802))
|
||||
* getOsReleaseBySha256 cached endpoint with keyfile header ([cd2413a](https://github.com/unraid/api/commit/cd2413abe8c5baab40e4e5974e08a5d18dce8e0d))
|
||||
* new check update buttons in dropdown ([ef5fcb9](https://github.com/unraid/api/commit/ef5fcb96a324143da864df803acaa0da1cd00eb7))
|
||||
* ship preview to different bucket ([#845](https://github.com/unraid/api/issues/845)) ([8e5d247](https://github.com/unraid/api/commit/8e5d247bca83d9e50977c9b16b212841ac9f70ad))
|
||||
* ship production to different bucket ([#846](https://github.com/unraid/api/issues/846)) ([63c0875](https://github.com/unraid/api/commit/63c08758c76425e007b1779bb2f77b75bc45896e))
|
||||
* unraidcheck callable from webgui with altUrl & json output ([ba8a67e](https://github.com/unraid/api/commit/ba8a67edfa043f442b11724227129f8d3f6cae0a))
|
||||
* update modals ([8ad7d8b](https://github.com/unraid/api/commit/8ad7d8be9437e0caa0409da8f7322050919fbbaa))
|
||||
* update os ignore release ([1955eb2](https://github.com/unraid/api/commit/1955eb23a3cdc30f0a67bc5950a047f83a860d99))
|
||||
* update os notifications enabled usage + link to enable & more options to account app ([5c82aff](https://github.com/unraid/api/commit/5c82aff80dc7e6d8f4b23e52af29abc2b8576424))
|
||||
* updateOs check response determines if update auth is required ([a9816d9](https://github.com/unraid/api/commit/a9816d9ad48ff80d87b5aeb236ff60c4979ad298))
|
||||
* updateOs store call local server-side endpoint & add modal support ([be48447](https://github.com/unraid/api/commit/be48447f943828af281095c5a092ac686e729030))
|
||||
* upgrade a ton of dependencies ([#842](https://github.com/unraid/api/issues/842)) ([94c1746](https://github.com/unraid/api/commit/94c174620c2347a3cf3d100404635f99a5b47287))
|
||||
* WebguiCheckForUpdate using server-side check ([590deb1](https://github.com/unraid/api/commit/590deb130c301d4004fecdc211270583806b5593))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* backport _var() PHP function to older versions of Unraid ([f53150e](https://github.com/unraid/api/commit/f53150e1fa33b3f45b66ad0dc5eaabc470564d45))
|
||||
* changlog relative links and external links ([a789e20](https://github.com/unraid/api/commit/a789e204ce7b966e6c935923626538ac344aeefe))
|
||||
* check update response modal expired key button styles ([92993e3](https://github.com/unraid/api/commit/92993e3e0b6240c83a6a64efedd8ddb3be3f9ef7))
|
||||
* **deps:** update dependency ws to v8.16.0 ([#815](https://github.com/unraid/api/issues/815)) ([212020e](https://github.com/unraid/api/commit/212020e78d4de0576137058a3374837b4a43e02d))
|
||||
* extraLinks when no updates available ([853a991](https://github.com/unraid/api/commit/853a9911e3fd7eec9bbc88468de78f87b448d477))
|
||||
* ignore release localStorage ([62c45ec](https://github.com/unraid/api/commit/62c45ec9d7c68498bbcfe933a5b63e4759c7129c))
|
||||
* lint ([83235f9](https://github.com/unraid/api/commit/83235f9db726f4582b9d353a66f2f5e8925b8e34))
|
||||
* lint unused value ([2c7e53b](https://github.com/unraid/api/commit/2c7e53bf67d1f214201624b39786bfb7de6aa520))
|
||||
* marked-base-url install ([416ba71](https://github.com/unraid/api/commit/416ba716aa750a094e8cd521a79f6deebcd37864))
|
||||
* missing translations ([faf17e4](https://github.com/unraid/api/commit/faf17e41e81c11443bc062d8ce35a33d9ae9ebbc))
|
||||
* regTm format after key install without page refresh ([f3ddb31](https://github.com/unraid/api/commit/f3ddb31f994de9192f7203698ecc5d7de673c6a3))
|
||||
* regTm format when already set ([5ad911f](https://github.com/unraid/api/commit/5ad911f8133daa60de53da738d41c6a59e2f02cc))
|
||||
* ServerUpdateOsResponse type ([78bdae8](https://github.com/unraid/api/commit/78bdae86c907142d3ee32d6715eaa8f5a974a1ed))
|
||||
* State Class usage in other files ([4ad7f53](https://github.com/unraid/api/commit/4ad7f53ec145b2e6d2895619523e90c1daa3f68f))
|
||||
* state data humanReadable switch fallthrus ([9144e39](https://github.com/unraid/api/commit/9144e39d39aa56af0ad897735d1a3545330920d0))
|
||||
* state php usage from cli ([46fd321](https://github.com/unraid/api/commit/46fd321707c14cd1f265ee806f673500d87132dd))
|
||||
* translations ([3fabd57](https://github.com/unraid/api/commit/3fabd5756674c06fa803729cf13d19c592d8d46a))
|
||||
* type issue with changlelog modal visibility ([e3c3f6b](https://github.com/unraid/api/commit/e3c3f6bf0f1882788291db17bd74865fefc3abf6))
|
||||
|
||||
## [3.4.0](https://github.com/unraid/api/compare/v3.3.0...v3.4.0) (2024-01-11)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* add logrotate to cron in nestjs ([#839](https://github.com/unraid/api/issues/839)) ([5c91524](https://github.com/unraid/api/commit/5c91524d849147c0ac7925f3a2f1cce67ffe75de))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* allow failure for log deletion ([eff3142](https://github.com/unraid/api/commit/eff31423927644be436a831126678719c2eb0621))
|
||||
* allowed origins check not working without spaces ([#838](https://github.com/unraid/api/issues/838)) ([b998b38](https://github.com/unraid/api/commit/b998b38355fab77ecc2f62bc64896766218db3d4))
|
||||
* excessive logging ([89cb254](https://github.com/unraid/api/commit/89cb2544ed0e0edd33b59f15d487487e22c0ae32))
|
||||
* run hourly ([0425794](https://github.com/unraid/api/commit/0425794356a01262222e7dff2645d3629e00d0f6))
|
||||
|
||||
## [3.3.0](https://github.com/unraid/api/compare/v3.2.3...v3.3.0) (2024-01-09)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* add button to add current origin to extra origins setting ([8c15163](https://github.com/unraid/api/commit/8c15163b3b072122bff1f8f25de62594b1e67992))
|
||||
* add environment to docker-compose ([2ee4683](https://github.com/unraid/api/commit/2ee46839095e3b8ee287cfe10f29ae9a39dcff68))
|
||||
* add support for expiration in var.ini ([#833](https://github.com/unraid/api/issues/833)) ([0474c2e](https://github.com/unraid/api/commit/0474c2e14fa462d2e1ec6d9a7f974660385d073e))
|
||||
* always show DRA even if disabled ([ab708c0](https://github.com/unraid/api/commit/ab708c0df634e21bf81595412d7de0be3ff7c392))
|
||||
* change sort order of Update/Downgrade ([#754](https://github.com/unraid/api/issues/754)) ([be96b3a](https://github.com/unraid/api/commit/be96b3aac709682a6517fa6e84beb586b9d8bf5c))
|
||||
* check for OS updates via PHP ([#752](https://github.com/unraid/api/issues/752)) ([4496615](https://github.com/unraid/api/commit/44966157b80a51dfe01d927c2af2d010c04becc5))
|
||||
* close log on exit ([d6ede86](https://github.com/unraid/api/commit/d6ede86eca6301342cdf35bf1f9365896b5e5009))
|
||||
* disable account & key actions when unraid-api CORS error ([1d15406](https://github.com/unraid/api/commit/1d1540646a264038ae96f4063c31a40cd048d2f9))
|
||||
* extraOrigins public, remove origin listener ([91f96ba](https://github.com/unraid/api/commit/91f96ba818773d6e71dde1ff52a4c8ec21ba6b5d))
|
||||
* fix codegen ([d0bf5bb](https://github.com/unraid/api/commit/d0bf5bb8197b11f7a250ca5392890184a1dbeff7))
|
||||
* fix exit hook and cleanup docker scripts ([#758](https://github.com/unraid/api/issues/758)) ([a9ff73e](https://github.com/unraid/api/commit/a9ff73e0a04c67e9ec9d5551cf0b1f124be6f381))
|
||||
* fix logging format on start and stop ([c6720c3](https://github.com/unraid/api/commit/c6720c331df055480d2d65b37290f4978fe429da))
|
||||
* improve check for OS updates via PHP ([cde12b2](https://github.com/unraid/api/commit/cde12b247f9bba97644750cd95a2b0db320ca1d9))
|
||||
* local start command ([99b6007](https://github.com/unraid/api/commit/99b6007ba30353084a8bea54cc0e782fcc1bfea4))
|
||||
* log config recreation reason ([f36c72f](https://github.com/unraid/api/commit/f36c72f5ad44b7e41d1726fa181dc2b9f594c72c))
|
||||
* nestjs initial query implementation ([#748](https://github.com/unraid/api/issues/748)) ([075d7f2](https://github.com/unraid/api/commit/075d7f25785bf686779b7fee1d5ea39f09ff3ea8))
|
||||
* new key types in API ([e42f9dc](https://github.com/unraid/api/commit/e42f9dc95be03e8389aac443f2147c07a316d48d))
|
||||
* npm scripts to prevent webgui builds with wrong urls ([279966a](https://github.com/unraid/api/commit/279966afa3c218fbe85bafe91ee40fff2eb59ef2))
|
||||
* patch DefaultPageLayout for web component ([629fec6](https://github.com/unraid/api/commit/629fec64f911131e4ab3810c99028b484ce18b83))
|
||||
* **plg:** WIP extra origins support ([85acaae](https://github.com/unraid/api/commit/85acaaee02dad98eeef8a8c4a09b463e84d593b4))
|
||||
* regTy swapped ([564b25c](https://github.com/unraid/api/commit/564b25cf5ce0a62d40f8d63d44c81e9c8560e0be))
|
||||
* run codegen and update build script ([07512ad](https://github.com/unraid/api/commit/07512adc13ee0d819db45ff6c5c5f58a0ba31141))
|
||||
* server store isOsVersionStable ([b5ee4d4](https://github.com/unraid/api/commit/b5ee4d4ee632a7528e6f5df079cab0cb5ea656eb))
|
||||
* stretch downgrade component buttons ([fa4f63e](https://github.com/unraid/api/commit/fa4f63e8bfca525ccfedb16f19d395bf11a68561))
|
||||
* swap to fragement usage on webcomponent ([42733ab](https://github.com/unraid/api/commit/42733abf6e443516ff715569333422ce80d3b1d2))
|
||||
* **web:** caseModel ([4174d0b](https://github.com/unraid/api/commit/4174d0bf2cac99af5db48e5642e0037d7425c952))
|
||||
* **web:** create script to move build to webgui repo ([92df453](https://github.com/unraid/api/commit/92df453255fed45210d9a192c68bb27d3b0ee981))
|
||||
* **web:** downgrade os web component ([45496ab](https://github.com/unraid/api/commit/45496ab7685d4bbfe591be46489260bac9b03474))
|
||||
* **web:** finalize api cors error & settings field ([e1d9e16](https://github.com/unraid/api/commit/e1d9e16b8e80e0940a0078131ea629559e3238ec))
|
||||
* **web:** guidValidation if new keyfile auto install ([0abb196](https://github.com/unraid/api/commit/0abb196d2c57ead4dca2adb2981ab79cdd1647c4))
|
||||
* **web:** localStorage craftUrl for dev ([e646187](https://github.com/unraid/api/commit/e646187b04548c010cf26c7ae38a82ced6270394))
|
||||
* **web:** refactor generic updateOS with date comparison ([91a753c](https://github.com/unraid/api/commit/91a753cd7018b89d53e9cd2d7c429ce53e291336))
|
||||
* **web:** registration component ui / ux ([717d873](https://github.com/unraid/api/commit/717d8733bd4b8c87b6ae6f1cd66717056c5df876))
|
||||
* **web:** registration replace eligibility docs btn ([b69285f](https://github.com/unraid/api/commit/b69285ff8ca5b896082b5f0e1aeba70f9a2c5129))
|
||||
* **web:** registration too many devices messaging ([1c0b5a3](https://github.com/unraid/api/commit/1c0b5a317aadf6173405770878e6038d4d8b448f))
|
||||
* **web:** start prep for new key type support ([5c5035a](https://github.com/unraid/api/commit/5c5035a5446516999729ddc56d1077ee512f14d3))
|
||||
* **web:** update os create flash backup button ([50ba61c](https://github.com/unraid/api/commit/50ba61cf80b7df2d121962cf4ec4b10952e8eecb))
|
||||
* **web:** WIP key expiration ([24618fe](https://github.com/unraid/api/commit/24618fe09db2109c2eb57ab1655ab0fb7d79fc90))
|
||||
* **web:** WIP registration page UI UX ([559e5b8](https://github.com/unraid/api/commit/559e5b8698d5df80ca57f530a2bf2cb6f01e30c7))
|
||||
* **web:** WIP registration page web component ([bd772a9](https://github.com/unraid/api/commit/bd772a9c97d49b57a0b5a0e6a367c9a4e3732086))
|
||||
* **web:** WIP updateOs callback ([2ad55ed](https://github.com/unraid/api/commit/2ad55ed019155e46d8627ea5c1b82cd5e4351127))
|
||||
* WIP first pass at UpdateOs page replacement component ([3a5d871](https://github.com/unraid/api/commit/3a5d871f1fd054720c3693705484072ff567ff28))
|
||||
* WIP UpdateOs page component ([8e4c36d](https://github.com/unraid/api/commit/8e4c36d38ce4e70307f5d14c953d5103c8b7e8e4))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* 6.10 view release notes js ([254d894](https://github.com/unraid/api/commit/254d894f39e512d1b4a0472180cb27090de256a0))
|
||||
* add missing translation keys ([03b506c](https://github.com/unraid/api/commit/03b506cd4e68f23a85bbfd54205322a6a4f93e5b))
|
||||
* add serverName / description to dashboard payload ([9677aff](https://github.com/unraid/api/commit/9677aff1cd0942f36a2845f3f105601c494efd9e))
|
||||
* allow null for the local entry in the myservers cfg ([01157c8](https://github.com/unraid/api/commit/01157c86ea3838ca675d65528a882cf25d0019a6))
|
||||
* azure and gray theme custom colors ([92e552c](https://github.com/unraid/api/commit/92e552c9c7f7804902f18eb2d71f8483671fe048))
|
||||
* codegen on web run ([e2e67c2](https://github.com/unraid/api/commit/e2e67c21067a138d963f5f10760b84cf6a533542))
|
||||
* combinedKnownOrigins in state.php for UPC ([b550eea](https://github.com/unraid/api/commit/b550eeae7077cbdbd6d004506bdc96d04c04bc4c))
|
||||
* Connect settings myservers config parse ([1c1483a](https://github.com/unraid/api/commit/1c1483a5cc506deab9d858dabbb8388c8b1d1ec1))
|
||||
* dateTime system settings ([56ccbff](https://github.com/unraid/api/commit/56ccbff61fb61ab67277100c525b80adf95e9b72))
|
||||
* **deps:** update dependency graphql to v16.8.1 ([bff1b19](https://github.com/unraid/api/commit/bff1b19706bee1e3103e3a0a1d2fceb3154f9bba))
|
||||
* **deps:** update graphql-tools monorepo (major) ([#693](https://github.com/unraid/api/issues/693)) ([3447eb0](https://github.com/unraid/api/commit/3447eb047a1dcd575b88a96bbcef9946aca366a1))
|
||||
* **deps:** update nest monorepo ([#816](https://github.com/unraid/api/issues/816)) ([4af3699](https://github.com/unraid/api/commit/4af36991b8b376f816ed51fd503a66e99675a3e7))
|
||||
* downgrade remove erroneous file_get_contents ([df9c918](https://github.com/unraid/api/commit/df9c91867cf3f7cf6b424a386d7e68bd510ec20f))
|
||||
* exit with process.exit not process.exitcode ([dcb6def](https://github.com/unraid/api/commit/dcb6def1cf3365dca819feed101160c8ad0125dc))
|
||||
* graphQL CORS error detection ([e5ea67f](https://github.com/unraid/api/commit/e5ea67fe5224fd5aaf06e1e63e7efc01974a10ac))
|
||||
* header version thirdPartyDriversDownloading pill ([c2ff31c](https://github.com/unraid/api/commit/c2ff31c672bc30683062c6cefbd5e744a7a2a676))
|
||||
* lint unused param var prefixed ([8d103a9](https://github.com/unraid/api/commit/8d103a9ca89139d7b4f513318a67bcc64c0daa0c))
|
||||
* local container startup commands cleaned up ([6c0ccb2](https://github.com/unraid/api/commit/6c0ccb2b24f98282be4db2e0b2e6362f4a187def))
|
||||
* logrotate not working due to invalid ownership of unraid-api folder ([ec0581a](https://github.com/unraid/api/commit/ec0581abf58a217f698d52d5337f2b312e5a645b))
|
||||
* missing translation ([81a9380](https://github.com/unraid/api/commit/81a93802993e7d95fb587cbfe3b598136a89348b))
|
||||
* optional check on api.version to allow fallback to save value ([0ac4455](https://github.com/unraid/api/commit/0ac4455f78407eca7aa1d6ee360830067a1c5c3e))
|
||||
* patch ShowChanges.php in 6.10 ([92d09c2](https://github.com/unraid/api/commit/92d09c2846c1bf64276e140c4cf4635e8bbfa94b))
|
||||
* plg installer header version replacement ([7d0de2c](https://github.com/unraid/api/commit/7d0de2c8b3dc3c2d3c204e7846cf65d6df07545f))
|
||||
* plg remove reboot-details path ([d54d90e](https://github.com/unraid/api/commit/d54d90ec04c67ee532cbcb77c4c5890545899e5a))
|
||||
* **plg:** Downgrade & Update page file locations ([3fbb6b7](https://github.com/unraid/api/commit/3fbb6b70c1152d0691f3d74298908338e19cda53))
|
||||
* **plg:** third party reboot detection ([f0ee640](https://github.com/unraid/api/commit/f0ee640767e446a829fd2e60033560786e5f63b0))
|
||||
* plugin install should suppress output from `unraid-api stop` ([#757](https://github.com/unraid/api/issues/757)) ([3da5d95](https://github.com/unraid/api/commit/3da5d9573b499c84c25e33b26a2014e79bef40f7))
|
||||
* rearrange exit hook to try to fix closing ([843d3f4](https://github.com/unraid/api/commit/843d3f41162c5dbcfd7803912b1879d7a182231a))
|
||||
* refreshServerState check regExp ([7fca971](https://github.com/unraid/api/commit/7fca971cab40b6e5493e7e21baf85e3d6ba66b90))
|
||||
* remove var_dump Connect settings ([9425f8b](https://github.com/unraid/api/commit/9425f8b133d44ac759d09158eadd13c81e7796fb))
|
||||
* renew callback messaging in modal ([e98d065](https://github.com/unraid/api/commit/e98d0654237b111cf912eb5014dbcc5da0e92ca3))
|
||||
* replaceRenew response cache use & purge ([ca85199](https://github.com/unraid/api/commit/ca851991ecb09720d70135d302aa93ad10a96d3a))
|
||||
* set sha in test step as well. ([8af3367](https://github.com/unraid/api/commit/8af3367226f9a3bc51db65ffe5dd53d6c5aa0017))
|
||||
* state php version checking ([494f5e9](https://github.com/unraid/api/commit/494f5e9935bc207b81098e84a0fe3e259939cf39))
|
||||
* stop using username to determine reg status ([c5a6cd7](https://github.com/unraid/api/commit/c5a6cd7bf930d8bc94ccae45f5363c12fd1fccfc))
|
||||
* ThirdPartyDriver messaging on Update page ([f23ad76](https://github.com/unraid/api/commit/f23ad762c04c3da918429a376146fe096a5030d5))
|
||||
* try to set environment in docker build ([caece63](https://github.com/unraid/api/commit/caece63e7f180f94a7ee6b962c905296c6b987bb))
|
||||
* uninstall reboot-details include ([3849462](https://github.com/unraid/api/commit/3849462f572659a43157a49511075f2d8cd5dd4c))
|
||||
* unraid-api server state refresh after key extension use regExp ([490595f](https://github.com/unraid/api/commit/490595f9b420054e6c2fe40d868b902b262718af))
|
||||
* updateOs auth group usage ([52b1ad9](https://github.com/unraid/api/commit/52b1ad9a7d3c9cdc989dd729d7828b0678349c27))
|
||||
* updateOs type check ([ba230e2](https://github.com/unraid/api/commit/ba230e2643399fbfa1612059f235ccdf61f7f486))
|
||||
* web component translations class ([6c81f6f](https://github.com/unraid/api/commit/6c81f6f70dcbe4f055a0041863fe275d6e01d6b9))
|
||||
* **web:** azure & gray theme header font colors ([8a5c7c9](https://github.com/unraid/api/commit/8a5c7c9304a063b26d7ff2df5c174aa9f1c0f53c))
|
||||
* **web:** card wrapper error border styles ([c71f420](https://github.com/unraid/api/commit/c71f420a4c9f7325127e3f38157dbc6255b3e139))
|
||||
* **web:** connect graph error handling ([c239937](https://github.com/unraid/api/commit/c239937c407cfea0defde1994809a5c0a196cca2))
|
||||
* **web:** default time format include am/pm ([31694cd](https://github.com/unraid/api/commit/31694cd7141e2ec0b0c3b4e4480d34d19c80adae))
|
||||
* **web:** downgrade status pill for no downgrade available ([9d9ebb1](https://github.com/unraid/api/commit/9d9ebb1c6efd486a90dcd78ba63766e24be26d55))
|
||||
* **web:** downgrade-not-available when downgrade initiated ([d060359](https://github.com/unraid/api/commit/d0603592596a3173889e9d06d57cfaa602eb80bb))
|
||||
* **web:** installPlugin composable for os updates ([9fb024a](https://github.com/unraid/api/commit/9fb024a68d65905e5351cfa71ca64cdffa0fa74c))
|
||||
* **web:** lint fixes ([224d637](https://github.com/unraid/api/commit/224d63773d505b8d65c9455fb94260ae617d9fe5))
|
||||
* **web:** localStorage craftUrl for dev ([2e108da](https://github.com/unraid/api/commit/2e108da0db7de01d03ee3b0657a614355a61b208))
|
||||
* **web:** missing translation ([74a8f27](https://github.com/unraid/api/commit/74a8f27643d7ba9c9d5dcd6a43b189a936dae648))
|
||||
* **web:** missing translation for update ([cb46a94](https://github.com/unraid/api/commit/cb46a94c7238bf381fbfc48109b1dd648d2e4949))
|
||||
* **web:** missing translations ([8ea733b](https://github.com/unraid/api/commit/8ea733b295a5f3bd922e867f544e5538873a5088))
|
||||
* **web:** missing translations ([d2eed92](https://github.com/unraid/api/commit/d2eed9291de9297aa0d556f06b9b8f5f09734250))
|
||||
* **web:** no plugin, don't show restart api button ([e628a8b](https://github.com/unraid/api/commit/e628a8b64fab4d1a5ce84af62abde3cd4c53ba96))
|
||||
* **web:** preview and test releases usage ([4b8cfb4](https://github.com/unraid/api/commit/4b8cfb464e8296ce20d6ff3870949d739a86ca1b))
|
||||
* **web:** reboot required disable update check link ([f029652](https://github.com/unraid/api/commit/f0296528bae52227ecbe281786ddf4d3a0cc940f))
|
||||
* **web:** reg component conditional keyActions ([730dff2](https://github.com/unraid/api/commit/730dff2e6344f7ee076e1c67d82ef0783a5931b2))
|
||||
* **web:** Registration key actions ([f7b1016](https://github.com/unraid/api/commit/f7b1016980c3f576b007a1d01184bf35f0eef311))
|
||||
* **web:** regTy on account payload ([64b0b5e](https://github.com/unraid/api/commit/64b0b5eb5767d41012f6bcb9536030ec39e45af9))
|
||||
* **web:** regUpdatesExpired use .isAfter ([5d67adf](https://github.com/unraid/api/commit/5d67adf4625a108e3374eb72714cdc1747b2a9c5))
|
||||
* **web:** replace check request error handling ([c1491fe](https://github.com/unraid/api/commit/c1491fecdc327d78f8de7c0f04fda481fb47cb56))
|
||||
* **web:** replaceCheck type ([1bd9729](https://github.com/unraid/api/commit/1bd9729b0197b49ca460912bbc56cd3b206d00dc))
|
||||
* **web:** replaceCheck type ([8cc6020](https://github.com/unraid/api/commit/8cc602019a2c8a718b59590d166644a1cb4d16cc))
|
||||
* **web:** state $_SESSION usage ([412392d](https://github.com/unraid/api/commit/412392dc1c5e612199e76ee7e1cae03705957e3d))
|
||||
* **web:** state php warnings ([1460cab](https://github.com/unraid/api/commit/1460cabe6b041f9f9fb89ca474a7d7e872d31c39))
|
||||
* **web:** translation ([cc85a49](https://github.com/unraid/api/commit/cc85a4903178999dbb80da50aa3b02ff38012172))
|
||||
* **web:** type errors ([e6c57eb](https://github.com/unraid/api/commit/e6c57eb910a1c1f948a3104c4e7fc04ac8b2d327))
|
||||
* **web:** upc dropdown updates external icon ([13936bb](https://github.com/unraid/api/commit/13936bb157f9097a19c7498fce252f3f86526ccb))
|
||||
* **web:** update CallbackButton import ([eabfeca](https://github.com/unraid/api/commit/eabfeca618d3bf682a331c6d9e1f17b5facdcdca))
|
||||
* **web:** Update OS auto redirect loop with account ([9b56fc3](https://github.com/unraid/api/commit/9b56fc3883f51942de9b1c8d1d1f30595fee7fa5))
|
||||
* **web:** updateOs lint ([bd9e9d5](https://github.com/unraid/api/commit/bd9e9d55cc7bba432f65d78feee83526dbfff059))
|
||||
* **web:** use dateTime format from server ([7090f38](https://github.com/unraid/api/commit/7090f38a9ab8b2d1dfce4095f4e2669d4d78a3e1))
|
||||
|
||||
### [3.2.3](https://github.com/unraid/api/compare/v3.2.2...v3.2.3) (2023-09-08)
|
||||
|
||||
|
||||
@@ -3168,4 +3621,4 @@ All notable changes to this project will be documented in this file. See [standa
|
||||
|
||||
# Changelog
|
||||
|
||||
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
|
||||
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
|
||||
|
||||
@@ -1,39 +1,31 @@
|
||||
###########################################################
|
||||
# Development/Build Image
|
||||
###########################################################
|
||||
FROM node:18.17.1-alpine As development
|
||||
FROM node:20-bookworm-slim AS development
|
||||
|
||||
# Install build tools and dependencies
|
||||
RUN apk add --no-cache \
|
||||
RUN apt-get update -y && apt-get install -y \
|
||||
bash \
|
||||
# Real PS Command (needed for some dependencies)
|
||||
procps \
|
||||
alpine-sdk \
|
||||
python3 \
|
||||
libvirt-dev \
|
||||
jq \
|
||||
zstd
|
||||
|
||||
RUN mkdir /var/log/unraid-api/
|
||||
zstd \
|
||||
git \
|
||||
build-essential
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Set app env
|
||||
ENV NODE_ENV=development
|
||||
|
||||
# Setup cache for pkg
|
||||
ENV PKG_CACHE_PATH /app/.pkg-cache
|
||||
RUN mkdir -p ${PKG_CACHE_PATH}
|
||||
|
||||
COPY tsconfig.json tsup.config.ts .eslintrc.cjs .npmrc .env.production .env.staging ./
|
||||
COPY tsconfig.json .eslintrc.ts .npmrc .env.production .env.staging ./
|
||||
|
||||
COPY package.json package-lock.json ./
|
||||
|
||||
# Install pkg
|
||||
RUN npm i -g pkg zx
|
||||
|
||||
# Install deps
|
||||
RUN npm ci
|
||||
RUN npm i
|
||||
|
||||
EXPOSE 4000
|
||||
|
||||
@@ -43,6 +35,8 @@ EXPOSE 4000
|
||||
|
||||
FROM development AS builder
|
||||
|
||||
ENV NODE_ENV=production
|
||||
|
||||
COPY . .
|
||||
|
||||
CMD ["npm", "run", "build-pkg"]
|
||||
CMD ["npm", "run", "build-and-pack"]
|
||||
62
api/README.md
Normal file
62
api/README.md
Normal file
@@ -0,0 +1,62 @@
|
||||
# @unraid/api
|
||||
|
||||
## Installation
|
||||
|
||||
Install the production plugin via the apps tab (search for "Unraid Connect")
|
||||
|
||||
Manual install can be done with the following routes:
|
||||
[production](https://stable.dl.unraid.net/unraid-api/dynamix.unraid.net.plg)
|
||||
[staging](https://preview.dl.unraid.net/unraid-api/dynamix.unraid.net.staging.plg)
|
||||
|
||||
## CLI
|
||||
|
||||
If you're on a unraid v6.9.2 or later machine this should be available by running `unraid-api` in any directory.
|
||||
|
||||
```bash
|
||||
root@Devon:~# unraid-api
|
||||
|
||||
Unraid API
|
||||
|
||||
Thanks for using the official Unraid API
|
||||
|
||||
Usage:
|
||||
|
||||
$ unraid-api command <options>
|
||||
|
||||
Commands:
|
||||
|
||||
start/stop/restart/version/status/report/switch-env
|
||||
|
||||
Options:
|
||||
|
||||
-h, --help Prints this usage guide.
|
||||
-d, --debug Enabled debug mode.
|
||||
-p, --port string Set the graphql port.
|
||||
--environment production/staging/development Set the working environment.
|
||||
--log-level ALL/TRACE/DEBUG/INFO/WARN/ERROR/FATAL/MARK/OFF Set the log level.
|
||||
|
||||
Copyright © 2024 Lime Technology, Inc.
|
||||
|
||||
```
|
||||
|
||||
## Report
|
||||
To view the current status of the unraid-api and its connection to mothership, run:
|
||||
```
|
||||
unraid-api report
|
||||
```
|
||||
|
||||
To view verbose data (anonymized), run:
|
||||
```
|
||||
unraid-api report -v
|
||||
```
|
||||
|
||||
To view non-anonymized verbose data, run:
|
||||
```
|
||||
unraid-api report -vv
|
||||
```
|
||||
|
||||
## Secrets
|
||||
If you found this file you're likely a developer. If you'd like to know more about the API and when it's available please join [our discord](https://discord.unraid.net/).
|
||||
|
||||
## License
|
||||
Copyright Lime Technology Inc. All rights reserved.
|
||||
105
api/codegen.ts
Normal file
105
api/codegen.ts
Normal file
@@ -0,0 +1,105 @@
|
||||
import type { CodegenConfig } from '@graphql-codegen/cli';
|
||||
|
||||
const config: CodegenConfig = {
|
||||
overwrite: true,
|
||||
emitLegacyCommonJSImports: false,
|
||||
verbose: true,
|
||||
config: {
|
||||
namingConvention: {
|
||||
typeNames: './fix-array-type.cjs',
|
||||
enumValues: 'change-case#upperCase',
|
||||
useTypeImports: true,
|
||||
},
|
||||
scalars: {
|
||||
DateTime: 'string',
|
||||
Long: 'number',
|
||||
JSON: '{ [key: string]: any }',
|
||||
URL: 'URL',
|
||||
Port: 'number',
|
||||
UUID: 'string',
|
||||
},
|
||||
},
|
||||
generates: {
|
||||
'src/graphql/generated/client/': {
|
||||
documents: './src/graphql/mothership/*.ts',
|
||||
schema: {
|
||||
[process.env.MOTHERSHIP_GRAPHQL_LINK as string]: {
|
||||
headers: {
|
||||
origin: 'https://forums.unraid.net',
|
||||
},
|
||||
},
|
||||
},
|
||||
preset: 'client',
|
||||
presetConfig: {
|
||||
gqlTagName: 'graphql',
|
||||
},
|
||||
config: {
|
||||
useTypeImports: true,
|
||||
withObjectType: true,
|
||||
},
|
||||
plugins: [
|
||||
{ add: { content: '/* eslint-disable */' } },
|
||||
],
|
||||
},
|
||||
// Generate Types for the API Server
|
||||
'src/graphql/generated/api/types.ts': {
|
||||
schema: [
|
||||
'./src/graphql/types.ts',
|
||||
'./src/graphql/schema/types/**/*.graphql',
|
||||
],
|
||||
plugins: [
|
||||
'typescript',
|
||||
'typescript-resolvers',
|
||||
{ add: { content: '/* eslint-disable */' } },
|
||||
],
|
||||
config: {
|
||||
contextType: '@app/graphql/schema/utils#Context',
|
||||
useIndexSignature: true,
|
||||
},
|
||||
},
|
||||
// Generate Operations for any built-in API Server Operations (e.g., report.ts)
|
||||
'src/graphql/generated/api/operations.ts': {
|
||||
documents: './src/graphql/client/api/*.ts',
|
||||
schema: [
|
||||
'./src/graphql/types.ts',
|
||||
'./src/graphql/schema/types/**/*.graphql',
|
||||
],
|
||||
preset: 'import-types',
|
||||
presetConfig: {
|
||||
typesPath: '@app/graphql/generated/api/types',
|
||||
},
|
||||
plugins: [
|
||||
'typescript-validation-schema',
|
||||
'typescript-operations',
|
||||
'typed-document-node',
|
||||
{ add: { content: '/* eslint-disable */' } },
|
||||
],
|
||||
config: {
|
||||
importFrom: '@app/graphql/generated/api/types',
|
||||
strictScalars: false,
|
||||
schema: 'zod',
|
||||
withObjectType: true,
|
||||
},
|
||||
},
|
||||
'src/graphql/generated/client/validators.ts': {
|
||||
schema: {
|
||||
[process.env.MOTHERSHIP_GRAPHQL_LINK as string]: {
|
||||
headers: {
|
||||
origin: 'https://forums.unraid.net',
|
||||
},
|
||||
},
|
||||
},
|
||||
plugins: [
|
||||
'typescript-validation-schema',
|
||||
{ add: { content: '/* eslint-disable */' } },
|
||||
],
|
||||
config: {
|
||||
importFrom: '@app/graphql/generated/client/graphql',
|
||||
strictScalars: false,
|
||||
schema: 'zod',
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export default config;
|
||||
@@ -1,77 +0,0 @@
|
||||
overwrite: true
|
||||
emitLegacyCommonJSImports: false
|
||||
verbose: true
|
||||
require:
|
||||
- ts-node/register
|
||||
config:
|
||||
namingConvention:
|
||||
typeNames: './fix-array-type.cjs'
|
||||
enumValues: 'change-case#upperCase'
|
||||
useTypeImports: true
|
||||
scalars:
|
||||
DateTime: string
|
||||
Long: number
|
||||
JSON: "{ [key: string]: any }"
|
||||
URL: URL
|
||||
Port: number
|
||||
UUID: string
|
||||
|
||||
generates:
|
||||
src/graphql/generated/client/:
|
||||
documents: './src/graphql/mothership/*.ts'
|
||||
schema:
|
||||
'${MOTHERSHIP_GRAPHQL_LINK}':
|
||||
headers:
|
||||
origin: 'https://forums.unraid.net'
|
||||
preset: client
|
||||
presetConfig:
|
||||
gqlTagName: graphql
|
||||
config:
|
||||
useTypeImports: true
|
||||
withObjectType: true
|
||||
plugins:
|
||||
- add: { content: '/* eslint-disable */' }
|
||||
|
||||
# Generate Types for the API Server
|
||||
src/graphql/generated/api/types.ts:
|
||||
schema:
|
||||
- './src/graphql/types.ts'
|
||||
- './src/graphql/schema/types/**/*.graphql'
|
||||
plugins:
|
||||
- typescript
|
||||
- typescript-resolvers
|
||||
- add: { content: '/* eslint-disable */' }
|
||||
config:
|
||||
contextType: '@app/graphql/schema/utils#Context'
|
||||
useIndexSignature: true
|
||||
# Generate Operations for any built in API Server Operations (ie report.ts)
|
||||
src/graphql/generated/api/operations.ts:
|
||||
documents: './src/graphql/client/api/*.ts'
|
||||
schema:
|
||||
- './src/graphql/types.ts'
|
||||
- './src/graphql/schema/types/**/*.graphql'
|
||||
preset: import-types
|
||||
presetConfig:
|
||||
typesPath: '@app/graphql/generated/api/types'
|
||||
plugins:
|
||||
- typescript-validation-schema
|
||||
- typescript-operations
|
||||
- typed-document-node
|
||||
- add: { content: '/* eslint-disable */' }
|
||||
config:
|
||||
importFrom: '@app/graphql/generated/api/types'
|
||||
strictScalars: false
|
||||
schema: 'zod'
|
||||
withObjectType: true
|
||||
src/graphql/generated/client/validators.ts:
|
||||
schema:
|
||||
'${MOTHERSHIP_GRAPHQL_LINK}':
|
||||
headers:
|
||||
origin: 'https://forums.unraid.net'
|
||||
plugins:
|
||||
- typescript-validation-schema
|
||||
- add: { content: '/* eslint-disable */'}
|
||||
config:
|
||||
importFrom: '@app/graphql/generated/client/graphql'
|
||||
strictScalars: false
|
||||
schema: 'zod'
|
||||
@@ -1,5 +1,6 @@
|
||||
[api]
|
||||
version="3.1.1+8efc0992"
|
||||
version="3.11.0"
|
||||
extraOrigins="https://google.com,https://test.com"
|
||||
[local]
|
||||
[notifier]
|
||||
apikey="unnotify_30994bfaccf839c65bae75f7fa12dd5ee16e69389f754c3b98ed7d5"
|
||||
@@ -8,6 +9,7 @@ wanaccess="yes"
|
||||
wanport="8443"
|
||||
upnpEnabled="no"
|
||||
apikey="_______________________BIG_API_KEY_HERE_________________________"
|
||||
localApiKey="_______________________LOCAL_API_KEY_HERE_________________________"
|
||||
email="test@example.com"
|
||||
username="zspearmint"
|
||||
avatar="https://via.placeholder.com/200"
|
||||
@@ -15,5 +17,6 @@ regWizTime="1611175408732_0951-1653-3509-FBA155FA23C0"
|
||||
idtoken=""
|
||||
accesstoken=""
|
||||
refreshtoken=""
|
||||
dynamicRemoteAccessType="DISABLED"
|
||||
[upc]
|
||||
apikey="unupc_fab6ff6ffe51040595c6d9ffb63a353ba16cc2ad7d93f813a2e80a5810"
|
||||
|
||||
80
api/dev/dynamix/default.cfg
Normal file
80
api/dev/dynamix/default.cfg
Normal file
@@ -0,0 +1,80 @@
|
||||
[confirm]
|
||||
down="1"
|
||||
stop="1"
|
||||
[display]
|
||||
width=""
|
||||
font=""
|
||||
tty="15"
|
||||
date="%c"
|
||||
time="%R"
|
||||
number=".,"
|
||||
unit="C"
|
||||
scale="-1"
|
||||
resize="0"
|
||||
wwn="0"
|
||||
total="1"
|
||||
banner=""
|
||||
header=""
|
||||
background=""
|
||||
tabs="1"
|
||||
users="Tasks:3"
|
||||
usage="0"
|
||||
text="1"
|
||||
warning="70"
|
||||
critical="90"
|
||||
hot="45"
|
||||
max="55"
|
||||
hotssd="60"
|
||||
maxssd="70"
|
||||
power=""
|
||||
theme="white"
|
||||
locale=""
|
||||
raw=""
|
||||
rtl=""
|
||||
headermetacolor=""
|
||||
headerdescription="yes"
|
||||
showBannerGradient="yes"
|
||||
[parity]
|
||||
mode="0"
|
||||
hour="0 0"
|
||||
dotm="1"
|
||||
month="1"
|
||||
day="0"
|
||||
cron=""
|
||||
write="NOCORRECT"
|
||||
[notify]
|
||||
display="0"
|
||||
life="5"
|
||||
date="d-m-Y"
|
||||
time="H:i"
|
||||
position="top-right"
|
||||
path="/tmp/notifications"
|
||||
system="*/1 * * * *"
|
||||
entity="1"
|
||||
normal="1"
|
||||
warning="1"
|
||||
alert="1"
|
||||
unraid="1"
|
||||
plugin="1"
|
||||
docker_notify="1"
|
||||
language_notify="1"
|
||||
report="1"
|
||||
unraidos=""
|
||||
version=""
|
||||
docker_update=""
|
||||
language_update=""
|
||||
status=""
|
||||
[ssmtp]
|
||||
root=""
|
||||
RcptTo=""
|
||||
SetEmailPriority="True"
|
||||
Subject="Unraid Status: "
|
||||
server="smtp.gmail.com"
|
||||
port="465"
|
||||
UseTLS="YES"
|
||||
UseSTARTTLS="NO"
|
||||
UseTLSCert="NO"
|
||||
TLSCert=""
|
||||
AuthMethod="login"
|
||||
AuthUser=""
|
||||
AuthPass=""
|
||||
@@ -1,5 +1,6 @@
|
||||
[display]
|
||||
date="%c"
|
||||
time="%I:%M %p"
|
||||
number=".,"
|
||||
scale="-1"
|
||||
tabs="1"
|
||||
|
||||
8
api/dev/keys/10f356da-1e9e-43b8-9028-a26a645539a6.json
Normal file
8
api/dev/keys/10f356da-1e9e-43b8-9028-a26a645539a6.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"id": "10f356da-1e9e-43b8-9028-a26a645539a6",
|
||||
"key": "73717ca0-8c15-40b9-bcca-8d85656d1438",
|
||||
"name": "Test API Key",
|
||||
"description": "Testing API key creation",
|
||||
"roles": ["guest", "connect"],
|
||||
"createdAt": "2024-10-29T19:59:12.569Z"
|
||||
}
|
||||
10
api/dev/keys/d166bf8b-3615-444a-8932-c460b2132ba3.json
Normal file
10
api/dev/keys/d166bf8b-3615-444a-8932-c460b2132ba3.json
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"createdAt": "2024-12-20T15:05:55.336Z",
|
||||
"description": "API key for Connect user",
|
||||
"id": "d166bf8b-3615-444a-8932-c460b2132ba3",
|
||||
"key": "_______________________LOCAL_API_KEY_HERE_________________________",
|
||||
"name": "Connect",
|
||||
"roles": [
|
||||
"connect"
|
||||
]
|
||||
}
|
||||
@@ -3,3 +3,4 @@ event=Unraid Parity check
|
||||
subject=Notice [UNRAID] - Parity check finished (0 errors)
|
||||
description=Canceled
|
||||
importance=warning
|
||||
link=/
|
||||
|
||||
1
api/dev/sessions/sess_mock-user-session
Normal file
1
api/dev/sessions/sess_mock-user-session
Normal file
@@ -0,0 +1 @@
|
||||
unraid_login|i:1736523078;unraid_user|s:4:"root";locale|s:0:"";buildDate|s:8:"20241202";
|
||||
@@ -1,5 +1,6 @@
|
||||
[api]
|
||||
version="3.1.1+8efc0992"
|
||||
version="3.11.0"
|
||||
extraOrigins="https://google.com,https://test.com"
|
||||
[local]
|
||||
[notifier]
|
||||
apikey="unnotify_30994bfaccf839c65bae75f7fa12dd5ee16e69389f754c3b98ed7d5"
|
||||
@@ -8,6 +9,7 @@ wanaccess="yes"
|
||||
wanport="8443"
|
||||
upnpEnabled="no"
|
||||
apikey="_______________________BIG_API_KEY_HERE_________________________"
|
||||
localApiKey="_______________________LOCAL_API_KEY_HERE_________________________"
|
||||
email="test@example.com"
|
||||
username="zspearmint"
|
||||
avatar="https://via.placeholder.com/200"
|
||||
@@ -15,7 +17,8 @@ regWizTime="1611175408732_0951-1653-3509-FBA155FA23C0"
|
||||
idtoken=""
|
||||
accesstoken=""
|
||||
refreshtoken=""
|
||||
allowedOrigins="/var/run/unraid-notifications.sock, /var/run/unraid-php.sock, /var/run/unraid-cli.sock, http://localhost:8080, https://localhost:4443, https://tower.local:4443, https://192.168.1.150:4443, https://tower:4443, https://192-168-1-150.thisisfourtyrandomcharacters012345678900.myunraid.net:4443, https://85-121-123-122.thisisfourtyrandomcharacters012345678900.myunraid.net:8443, https://10-252-0-1.hash.myunraid.net:4443, https://10-252-1-1.hash.myunraid.net:4443, https://10-253-3-1.hash.myunraid.net:4443, https://10-253-4-1.hash.myunraid.net:4443, https://10-253-5-1.hash.myunraid.net:4443, https://connect.myunraid.net, https://staging.connect.myunraid.net, https://dev-my.myunraid.net:4000, https://studio.apollographql.com"
|
||||
allowedOrigins="/var/run/unraid-notifications.sock, /var/run/unraid-php.sock, /var/run/unraid-cli.sock, http://localhost:8080, https://localhost:4443, https://tower.local:4443, https://192.168.1.150:4443, https://tower:4443, https://192-168-1-150.thisisfourtyrandomcharacters012345678900.myunraid.net:4443, https://85-121-123-122.thisisfourtyrandomcharacters012345678900.myunraid.net:8443, https://10-252-0-1.hash.myunraid.net:4443, https://10-252-1-1.hash.myunraid.net:4443, https://10-253-3-1.hash.myunraid.net:4443, https://10-253-4-1.hash.myunraid.net:4443, https://10-253-5-1.hash.myunraid.net:4443, https://10-100-0-1.hash.myunraid.net:4443, https://10-100-0-2.hash.myunraid.net:4443, https://10-123-1-2.hash.myunraid.net:4443, https://221-123-121-112.hash.myunraid.net:4443, https://google.com, https://test.com, https://connect.myunraid.net, https://connect-staging.myunraid.net, https://dev-my.myunraid.net:4000, https://studio.apollographql.com"
|
||||
dynamicRemoteAccessType="DISABLED"
|
||||
[upc]
|
||||
apikey="unupc_fab6ff6ffe51040595c6d9ffb63a353ba16cc2ad7d93f813a2e80a5810"
|
||||
[connectionStatus]
|
||||
|
||||
26
api/dev/states/nginx.ini
Normal file
26
api/dev/states/nginx.ini
Normal file
@@ -0,0 +1,26 @@
|
||||
NGINX_LANIP="192.168.1.150"
|
||||
NGINX_LANIP6=""
|
||||
NGINX_LANNAME="Tower"
|
||||
NGINX_LANMDNS="Tower.local"
|
||||
NGINX_CERTPATH="/boot/config/ssl/certs/certificate_bundle.pem"
|
||||
NGINX_USESSL="yes"
|
||||
NGINX_PORT="8080"
|
||||
NGINX_PORTSSL="4443"
|
||||
NGINX_DEFAULTURL="https://Tower.local:4443"
|
||||
NGINX_CERTNAME="*.thisisfourtyrandomcharacters012345678900.myunraid.net"
|
||||
NGINX_LANFQDN="192-168-1-150.thisisfourtyrandomcharacters012345678900.myunraid.net"
|
||||
NGINX_LANFQDN6=""
|
||||
NGINX_WANACCESS=""
|
||||
NGINX_WANIP=""
|
||||
NGINX_WANIP6=""
|
||||
NGINX_WANFQDN="85-121-123-122.thisisfourtyrandomcharacters012345678900.myunraid.net"
|
||||
NGINX_WANFQDN6=""
|
||||
NGINX_WG0FQDN="10-252-0-1.hash.myunraid.net"
|
||||
NGINX_WG1FQDN="10-252-1-1.hash.myunraid.net"
|
||||
NGINX_WG3FQDN="10-253-3-1.hash.myunraid.net"
|
||||
NGINX_WG4FQDN="10-253-4-1.hash.myunraid.net"
|
||||
NGINX_WG55FQDN="10-253-5-1.hash.myunraid.net"
|
||||
NGINX_TAILSCALEFQDN="10-100-0-1.hash.myunraid.net"
|
||||
NGINX_TAILSCALE0FQDN="10-100-0-2.hash.myunraid.net"
|
||||
NGINX_CUSTOMFQDN="10-123-1-2.hash.myunraid.net"
|
||||
NGINX_CUSTOMFQDN6="221-123-121-112.hash.myunraid.net"
|
||||
143
api/dev/states/var.ini
Normal file
143
api/dev/states/var.ini
Normal file
@@ -0,0 +1,143 @@
|
||||
version="6.11.2"
|
||||
MAX_ARRAYSZ="30"
|
||||
MAX_CACHESZ="30"
|
||||
NAME="Tower"
|
||||
timeZone="Australia/Adelaide"
|
||||
COMMENT="Dev Server"
|
||||
SECURITY="user"
|
||||
WORKGROUP="WORKGROUP"
|
||||
DOMAIN=""
|
||||
DOMAIN_SHORT=""
|
||||
hideDotFiles="no"
|
||||
localMaster="yes"
|
||||
enableFruit="no"
|
||||
USE_NETBIOS="yes"
|
||||
USE_WSD="no"
|
||||
WSD_OPT=""
|
||||
USE_NTP="yes"
|
||||
NTP_SERVER1="time1.google.com"
|
||||
NTP_SERVER2="time2.google.com"
|
||||
NTP_SERVER3="time3.google.com"
|
||||
NTP_SERVER4="time4.google.com"
|
||||
DOMAIN_LOGIN="Administrator"
|
||||
SYS_MODEL="Dell R710"
|
||||
SYS_ARRAY_SLOTS="24"
|
||||
SYS_FLASH_SLOTS="1"
|
||||
USE_SSL="auto"
|
||||
PORT="80"
|
||||
PORTSSL="443"
|
||||
LOCAL_TLD="local"
|
||||
BIND_MGT="no"
|
||||
USE_TELNET="yes"
|
||||
PORTTELNET="23"
|
||||
USE_SSH="yes"
|
||||
PORTSSH="22"
|
||||
USE_UPNP="yes"
|
||||
START_PAGE="Main"
|
||||
startArray="no"
|
||||
spindownDelay="0"
|
||||
spinupGroups="no"
|
||||
defaultFsType="xfs"
|
||||
shutdownTimeout="90"
|
||||
luksKeyfile="/tmp/unraid/keyfile"
|
||||
poll_attributes="1800"
|
||||
poll_attributes_default="1800"
|
||||
poll_attributes_status="default"
|
||||
queueDepth="auto"
|
||||
nr_requests="Auto"
|
||||
nr_requests_default="Auto"
|
||||
nr_requests_status="default"
|
||||
md_scheduler="auto"
|
||||
md_scheduler_default="auto"
|
||||
md_scheduler_status="default"
|
||||
md_num_stripes="1280"
|
||||
md_num_stripes_default="1280"
|
||||
md_num_stripes_status="default"
|
||||
md_queue_limit="80"
|
||||
md_queue_limit_default="80"
|
||||
md_queue_limit_status="default"
|
||||
md_sync_limit="5"
|
||||
md_sync_limit_default="5"
|
||||
md_sync_limit_status="default"
|
||||
md_write_method="auto"
|
||||
md_write_method_default="auto"
|
||||
md_write_method_status="default"
|
||||
shareDisk="yes"
|
||||
shareUser="e"
|
||||
shareUserInclude=""
|
||||
shareUserExclude=""
|
||||
shareSMBEnabled="yes"
|
||||
shareNFSEnabled="no"
|
||||
shareInitialOwner="Administrator"
|
||||
shareInitialGroup="Domain Users"
|
||||
shareCacheEnabled="yes"
|
||||
shareCacheFloor="2000000"
|
||||
shareMoverSchedule="40 3 * * *"
|
||||
shareMoverLogging="no"
|
||||
fuse_remember="330"
|
||||
fuse_remember_default="330"
|
||||
fuse_remember_status="default"
|
||||
fuse_directio="auto"
|
||||
fuse_directio_default="auto"
|
||||
fuse_directio_status="default"
|
||||
fuse_useino="yes"
|
||||
shareAvahiEnabled="yes"
|
||||
shareAvahiSMBName="%h"
|
||||
shareAvahiSMBModel="Xserve"
|
||||
shfs_logging="1"
|
||||
safeMode="no"
|
||||
startMode="Normal"
|
||||
configValid="yes"
|
||||
joinStatus="Not joined"
|
||||
deviceCount="4"
|
||||
flashGUID="0000-0000-0000-000000000000"
|
||||
flashProduct="DataTraveler_3.0"
|
||||
flashVendor="KINGSTON"
|
||||
regCheck=""
|
||||
regFILE="/app/dev/Unraid.net/Pro.key"
|
||||
regGUID="13FE-4200-C300-58C372A52B19"
|
||||
regTy="Pro"
|
||||
regTo="Eli Bosley"
|
||||
regTm="1833409182"
|
||||
regTm2="0"
|
||||
regExp=""
|
||||
regGen="0"
|
||||
sbName="/boot/config/super.dat"
|
||||
sbVersion="2.9.13"
|
||||
sbUpdated="1596079143"
|
||||
sbEvents="173"
|
||||
sbState="1"
|
||||
sbClean="yes"
|
||||
sbSynced="1586819259"
|
||||
sbSyncErrs="0"
|
||||
sbSynced2="1586822456"
|
||||
sbSyncExit="0"
|
||||
sbNumDisks="5"
|
||||
mdColor="green-blink"
|
||||
mdNumDisks="4"
|
||||
mdNumDisabled="1"
|
||||
mdNumInvalid="1"
|
||||
mdNumMissing="0"
|
||||
mdNumNew="0"
|
||||
mdNumErased="0"
|
||||
mdResync="0"
|
||||
mdResyncCorr="0"
|
||||
mdResyncPos="0"
|
||||
mdResyncDb="0"
|
||||
mdResyncDt="0"
|
||||
mdResyncAction="check P"
|
||||
mdResyncSize="438960096"
|
||||
mdState="STOPPED"
|
||||
mdVersion="2.9.14"
|
||||
fsState="Stopped"
|
||||
fsProgress="Autostart disabled"
|
||||
fsCopyPrcnt="0"
|
||||
fsNumMounted="0"
|
||||
fsNumUnmountable="0"
|
||||
fsUnmountableMask=""
|
||||
shareCount="0"
|
||||
shareSMBCount="1"
|
||||
shareNFSCount="0"
|
||||
shareMoverActive="no"
|
||||
reservedNames="parity,parity2,parity3,diskP,diskQ,diskR,disk,disks,flash,boot,user,user0,disk0,disk1,disk2,disk3,disk4,disk5,disk6,disk7,disk8,disk9,disk10,disk11,disk12,disk13,disk14,disk15,disk16,disk17,disk18,disk19,disk20,disk21,disk22,disk23,disk24,disk25,disk26,disk27,disk28,disk29,disk30,disk31"
|
||||
csrf_token="0000000000000000"
|
||||
@@ -4,11 +4,9 @@ x-volumes: &volumes
|
||||
volumes:
|
||||
- ./dev:/app/dev
|
||||
- ./src:/app/src
|
||||
- ./patches:/app/patches
|
||||
- ./package.json:/app/package.json
|
||||
- ./package-lock.json:/app/package-lock.json
|
||||
- ./tsconfig.json:/app/tsconfig.json
|
||||
- ./tsup.config.ts:/app/tsup.config.ts
|
||||
- ./vite.config.ts:/app/vite.config.ts
|
||||
- ./dist/:/app/dist/
|
||||
- ./deploy/:/app/deploy/
|
||||
@@ -19,19 +17,15 @@ x-volumes: &volumes
|
||||
- ./.env.staging:/app/.env.staging
|
||||
- ./.env.test:/app/.env.test
|
||||
- ./.env.development:/app/.env.development
|
||||
- ./.pkg-cache:/app/.pkg-cache
|
||||
- ./codegen.yml:/app/codegen.yml
|
||||
- ./codegen.ts:/app/codegen.ts
|
||||
- ./fix-array-type.cjs:/app/fix-array-type.cjs
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
- ./unraid-api.js:/app/unraid-api.js
|
||||
- ./ecosystem.config.json:/app/ecosystem.config.json
|
||||
|
||||
|
||||
networks:
|
||||
mothership_default:
|
||||
services:
|
||||
|
||||
dev:
|
||||
networks:
|
||||
- mothership_default
|
||||
image: unraid-api:development
|
||||
ports:
|
||||
- "3001:3001"
|
||||
@@ -45,12 +39,33 @@ services:
|
||||
entrypoint: /bin/bash
|
||||
environment:
|
||||
- IS_DOCKER=true
|
||||
- GIT_SHA=${GIT_SHA:?err}
|
||||
- IS_TAGGED=${IS_TAGGED}
|
||||
profiles:
|
||||
- builder
|
||||
|
||||
local:
|
||||
image: unraid-api:development
|
||||
ports:
|
||||
- "3001:3001"
|
||||
build:
|
||||
context: .
|
||||
target: development
|
||||
dockerfile: Dockerfile
|
||||
<<: *volumes
|
||||
command: npm run start:dev
|
||||
environment:
|
||||
- IS_DOCKER=true
|
||||
- GIT_SHA=${GIT_SHA:?err}
|
||||
- IS_TAGGED=${IS_TAGGED}
|
||||
profiles:
|
||||
- builder
|
||||
|
||||
builder:
|
||||
image: unraid-api:builder
|
||||
environment:
|
||||
- GIT_SHA=${GIT_SHA:?err}
|
||||
- IS_TAGGED=${IS_TAGGED}
|
||||
build:
|
||||
context: .
|
||||
target: builder
|
||||
|
||||
116
api/docs/development.md
Normal file
116
api/docs/development.md
Normal file
@@ -0,0 +1,116 @@
|
||||
# Development
|
||||
|
||||
## Installation
|
||||
|
||||
Manual install can be done with the following routes:
|
||||
[production](https://stable.dl.unraid.net/unraid-api/dynamix.unraid.net.plg)
|
||||
[staging](https://preview.dl.unraid.net/unraid-api/dynamix.unraid.net.staging.plg)
|
||||
|
||||
## Connecting to the API
|
||||
|
||||
### HTTP
|
||||
|
||||
This can be accessed by default via `http://tower.local/graphql`.
|
||||
|
||||
See <https://graphql.org/learn/serving-over-http/#http-methods-headers-and-body>
|
||||
|
||||
## Building in Docker
|
||||
|
||||
To get a development environment for testing start by running this docker command:
|
||||
|
||||
`npm run build:docker`
|
||||
`npm run start:ddev`
|
||||
|
||||
which will give you an interactive shell inside of the newly build linux container.
|
||||
|
||||
To automatically build the plugin run the command below:
|
||||
|
||||
`npm run build:docker`
|
||||
|
||||
The builder command will build the plugin into deploy/release, and the interactive plugin lets you build the plugin or install node modules how you like.
|
||||
|
||||
## Logs
|
||||
|
||||
Logging can be configured via environment variables.
|
||||
|
||||
Log levels can be set when the api starts via `LOG_LEVEL=all/trace/debug/info/warn/error/fatal/mark/off`.
|
||||
|
||||
Additional detail for the log entry can be added with `LOG_CONTEXT=true` (warning, generates a lot of data).
|
||||
|
||||
By default, logs will be sent to syslog. Or you can set `LOG_TRANSPORT=file` to have logs saved in `/var/log/unraid-api/stdout.log`. Or enable debug mode to view logs inline.
|
||||
|
||||
Examples:
|
||||
|
||||
- `unraid-api start`
|
||||
- `LOG_LEVEL=debug unraid-api start --debug`
|
||||
- `LOG_LEVEL=trace LOG_CONTEXT=true LOG_TRANSPORT=file unraid-api start`
|
||||
|
||||
## Viewing data sent to mothership
|
||||
|
||||
If the environment variable `LOG_MOTHERSHIP_MESSAGES=true` exists, any data the unraid-api sends to mothership will be saved in clear text here: `/var/log/unraid-api/relay-messages.log`
|
||||
|
||||
Examples:
|
||||
|
||||
- `LOG_MOTHERSHIP_MESSAGES=true unraid-api start`
|
||||
- `LOG_MOTHERSHIP_MESSAGES=true LOG_LEVEL=debug unraid-api start --debug`
|
||||
|
||||
## Debug Logging
|
||||
|
||||
To view debug logs, change the log level when starting the API. Then type unraid-api logs to trail the logs.
|
||||
Examples:
|
||||
|
||||
- `LOG_LEVEL=debug unraid-api start`
|
||||
- `unraid-api logs`
|
||||
|
||||
## Switching between staging and production environments
|
||||
|
||||
1. Stop the api: `unraid-api stop`
|
||||
2. Switch environments: `unraid-api switch-env`
|
||||
3. Start the api: `unraid-api start`
|
||||
4. Confirm the environment: `unraid-api report`
|
||||
|
||||
## Playground
|
||||
|
||||
The playground can be access via `http://tower.local/graphql` while in debug mode.
|
||||
To get your API key open a terminal on your server and run `cat /boot/config/plugins/dynamix.my.servers/myservers.cfg | grep apikey=\"unraid | cut -d '"' -f2`. Add that API key in the "HTTP headers" panel of the playground.
|
||||
|
||||
```json
|
||||
{
|
||||
"x-api-key": "__REPLACE_ME_WITH_API_KEY__"
|
||||
}
|
||||
```
|
||||
|
||||
Next add the query you want to run and hit the play icon.
|
||||
|
||||
```gql
|
||||
query welcome {
|
||||
welcome {
|
||||
message
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
You should get something like this back.
|
||||
|
||||
```json
|
||||
{
|
||||
"data": {
|
||||
"welcome": {
|
||||
"message": "Welcome root to this Unraid 6.10.0 server"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Click the "Schema" and "Docs" button on the right side of the playground to learn more.
|
||||
|
||||
## Create a new release
|
||||
|
||||
To create a new version run `npm run release` and then run **ONLY** the `git push` section of the commands it returns.
|
||||
To create a new prerelease run `npm run release -- --prerelease alpha`.
|
||||
|
||||
Pushing to this repo will cause an automatic "rolling" release to be built which can be accessed via the page for the associated Github action run.
|
||||
|
||||
## Using a custom version (e.g. testing a new release)
|
||||
|
||||
Find the Pull Request you'd like to install, and a link will be present as a comment to install a PR-specific version.
|
||||
21
api/ecosystem.config.json
Normal file
21
api/ecosystem.config.json
Normal file
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"apps": [
|
||||
{
|
||||
"name": "unraid-api",
|
||||
"script": "./dist/main.js",
|
||||
"cwd": "/usr/local/unraid-api",
|
||||
"log": "/var/log/unraid-api/unraid-api.log",
|
||||
"exec_mode": "fork",
|
||||
"wait_ready": true,
|
||||
"listen_timeout": 30000,
|
||||
"max_restarts": 10,
|
||||
"min_uptime": 10000,
|
||||
"ignore_watch": [
|
||||
"node_modules",
|
||||
"src",
|
||||
".env.*",
|
||||
"myservers.cfg"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
21
api/justfile
Normal file
21
api/justfile
Normal file
@@ -0,0 +1,21 @@
|
||||
set fallback
|
||||
|
||||
default:
|
||||
@just --list --justfile {{justfile()}} --list-heading $'\nAPI project recipes:\n'
|
||||
@just list-commands
|
||||
|
||||
setup:
|
||||
npm install
|
||||
npm run container:build
|
||||
|
||||
# builds js files that can run on an unraid server
|
||||
@build:
|
||||
npm run build
|
||||
|
||||
# deploys to an unraid server
|
||||
@deploy:
|
||||
./scripts/deploy-dev.sh
|
||||
|
||||
# build & deploy
|
||||
bd: build deploy
|
||||
|
||||
52829
api/package-lock.json
generated
52829
api/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
387
api/package.json
387
api/package.json
@@ -1,202 +1,193 @@
|
||||
{
|
||||
"name": "@unraid/api",
|
||||
"version": "3.2.3",
|
||||
"main": "dist/index.js",
|
||||
"bin": "dist/unraid-api.cjs",
|
||||
"type": "module",
|
||||
"repository": "git@github.com:unraid/api.git",
|
||||
"author": "Alexis Tyler <xo@wvvw.me> (https://wvvw.me/)",
|
||||
"license": "UNLICENSED",
|
||||
"engines": {
|
||||
"node": ">=16.5.0"
|
||||
},
|
||||
"pkg": {
|
||||
"assets": [
|
||||
"dist/index.cjs",
|
||||
"node_modules/@vmngr/libvirt/build/Release",
|
||||
"node_modules/ts-invariant/",
|
||||
"src/**/*.graphql"
|
||||
"name": "@unraid/api",
|
||||
"version": "3.11.0",
|
||||
"main": "src/cli/index.ts",
|
||||
"type": "module",
|
||||
"repository": "git@github.com:unraid/api.git",
|
||||
"author": "Lime Technology, Inc. <unraid.net>",
|
||||
"license": "UNLICENSED",
|
||||
"scripts": {
|
||||
"start": "node dist/main.js",
|
||||
"build:docker": "./scripts/dc.sh run --rm builder",
|
||||
"build": "vite build --mode=production",
|
||||
"postbuild": "chmod +x dist/main.js && chmod +x dist/cli.js",
|
||||
"build-and-pack": "./scripts/build.mjs",
|
||||
"codegen": "MOTHERSHIP_GRAPHQL_LINK='https://staging.mothership.unraid.net/ws' graphql-codegen --config codegen.ts -r dotenv/config './.env.staging'",
|
||||
"codegen:watch": "DOTENV_CONFIG_PATH='./.env.staging' graphql-codegen --config codegen.ts --watch -r dotenv/config",
|
||||
"codegen:local": "NODE_TLS_REJECT_UNAUTHORIZED=0 MOTHERSHIP_GRAPHQL_LINK='https://mothership.localhost/ws' graphql-codegen --config codegen.ts --watch",
|
||||
"tsc": "tsc --noEmit",
|
||||
"lint": "eslint --flag unstable_ts_config --config .eslintrc.ts src/",
|
||||
"lint:fix": "eslint --flag unstable_ts_config --fix --config .eslintrc.ts src/",
|
||||
"test:watch": "vitest --pool=forks",
|
||||
"test": "vitest run --pool=forks",
|
||||
"coverage": "vitest run --pool=forks --coverage",
|
||||
"release": "standard-version",
|
||||
"dev": "vite",
|
||||
"container:build": "./scripts/dc.sh build dev",
|
||||
"container:start": "./scripts/dc.sh run --rm --service-ports dev",
|
||||
"container:test": "./scripts/dc.sh run --rm builder npm run test",
|
||||
"container:enter": "./scripts/dc.sh exec dev /bin/bash"
|
||||
},
|
||||
"files": [
|
||||
".env.staging",
|
||||
".env.production",
|
||||
"ecosystem.config.json",
|
||||
"README.md",
|
||||
"src",
|
||||
"node_modules/"
|
||||
],
|
||||
"targets": [
|
||||
"node18-linux-x64"
|
||||
],
|
||||
"outputPath": "dist"
|
||||
},
|
||||
"scripts": {
|
||||
"compile": "tsup --config ./tsup.config.ts",
|
||||
"bundle": "pkg . --public",
|
||||
"build": "npm run compile && npm run bundle",
|
||||
"build:docker": "docker-compose run --rm builder",
|
||||
"build-pkg": "./scripts/build.mjs",
|
||||
"codegen": "MOTHERSHIP_GRAPHQL_LINK='https://staging.mothership.unraid.net/ws' graphql-codegen --config codegen.yml -r dotenv/config './.env.staging'",
|
||||
"codegen:watch": "DOTENV_CONFIG_PATH='./.env.staging' graphql-codegen-esm --config codegen.yml --watch -r dotenv/config",
|
||||
"codegen:local": "MOTHERSHIP_GRAPHQL_LINK='http://localhost:3000/graphql' graphql-codegen-esm --config codegen.yml --watch",
|
||||
"tsc": "tsc --noEmit",
|
||||
"lint": "DEBUG=eslint:cli-engine eslint . --config .eslintrc.cjs",
|
||||
"lint:fix": "DEBUG=eslint:cli-engine eslint . --fix --config .eslintrc.cjs",
|
||||
"test:watch": "vitest --segfault-retry=3 --no-threads",
|
||||
"test": "vitest run --segfault-retry=3 --no-threads",
|
||||
"coverage": "vitest run --segfault-retry=3 --coverage",
|
||||
"patch:subscriptions-transport-ws": "node ./.scripts/patches/subscriptions-transport-ws.cjs",
|
||||
"release": "standard-version",
|
||||
"typesync": "typesync",
|
||||
"install:unraid": "./scripts/install-in-unraid.sh",
|
||||
"start:plugin": "LOG_MOTHERSHIP_MESSAGES=true LOG_TYPE=pretty LOG_LEVEL=trace unraid-api start --debug",
|
||||
"start:plugin-verbose": "LOG_CONTEXT=true LOG_MOTHERSHIP_MESSAGES=true LOG_TYPE=pretty LOG_LEVEL=trace unraid-api start --debug",
|
||||
"start:dev": "LOG_MOTHERSHIP_MESSAGES=true LOG_TYPE=pretty NODE_ENV=development LOG_LEVEL=trace NODE_ENV=development tsup --config ./tsup.config.ts --watch --onSuccess 'DOTENV_CONFIG_PATH=./.env.development node -r dotenv/config dist/unraid-api.cjs start --debug'",
|
||||
"stop:dev": "LOG_MOTHERSHIP_MESSAGES=true LOG_TYPE=pretty NODE_ENV=development LOG_LEVEL=trace NODE_ENV=development tsup --config ./tsup.config.ts --watch --onSuccess 'DOTENV_CONFIG_PATH=./.env.development node -r dotenv/config dist/unraid-api.cjs stop --debug'",
|
||||
"start:report": "LOG_MOTHERSHIP_MESSAGES=true LOG_TYPE=pretty NODE_ENV=development LOG_LEVEL=trace NODE_ENV=development LOG_CONTEXT=true tsup --config ./tsup.config.ts --watch --onSuccess 'DOTENV_CONFIG_PATH=./.env.development node -r dotenv/config dist/unraid-api.cjs report --debug'",
|
||||
"start:docker": "docker compose run --rm builder-interactive",
|
||||
"docker:dev": "docker-compose run --rm --service-ports dev",
|
||||
"docker:test": "docker-compose run --rm builder npm run test"
|
||||
},
|
||||
"files": [
|
||||
".env.staging",
|
||||
".env.production",
|
||||
"dist",
|
||||
"unraid-api"
|
||||
],
|
||||
"dependencies": {
|
||||
"@apollo/client": "^3.7.12",
|
||||
"@apollo/server": "^4.6.0",
|
||||
"@graphql-codegen/client-preset": "^3.0.0",
|
||||
"@graphql-tools/load-files": "^6.6.1",
|
||||
"@graphql-tools/merge": "^8.4.0",
|
||||
"@graphql-tools/schema": "^9.0.17",
|
||||
"@graphql-tools/utils": "^9.2.1",
|
||||
"@reduxjs/toolkit": "^1.9.5",
|
||||
"@reflet/cron": "^1.3.1",
|
||||
"@runonflux/nat-upnp": "^1.0.2",
|
||||
"accesscontrol": "^2.2.1",
|
||||
"am": "github:unraid/am",
|
||||
"async-exit-hook": "^2.0.1",
|
||||
"btoa": "^1.2.1",
|
||||
"bycontract": "^2.0.11",
|
||||
"bytes": "^3.1.2",
|
||||
"cacheable-lookup": "^6.1.0",
|
||||
"catch-exit": "^1.2.2",
|
||||
"chalk": "^4.1.2",
|
||||
"chokidar": "^3.5.3",
|
||||
"cli-table": "^0.3.11",
|
||||
"command-exists": "^1.2.9",
|
||||
"convert": "^4.10.0",
|
||||
"cors": "^2.8.5",
|
||||
"cross-fetch": "^4.0.0",
|
||||
"docker-event-emitter": "^0.3.0",
|
||||
"dockerode": "^3.3.5",
|
||||
"dotenv": "^16.0.3",
|
||||
"express": "^4.18.2",
|
||||
"find-process": "^1.4.7",
|
||||
"graphql": "^16.6.0",
|
||||
"graphql-fields": "^2.0.3",
|
||||
"graphql-scalars": "^1.21.3",
|
||||
"graphql-subscriptions": "^2.0.0",
|
||||
"graphql-tag": "^2.12.6",
|
||||
"graphql-type-json": "^0.3.2",
|
||||
"graphql-type-uuid": "^0.2.0",
|
||||
"graphql-ws": "^5.12.1",
|
||||
"htpasswd-js": "^1.0.2",
|
||||
"ini": "^4.1.0",
|
||||
"ip": "^1.1.8",
|
||||
"jose": "^4.14.2",
|
||||
"lodash": "^4.17.21",
|
||||
"multi-ini": "^2.2.0",
|
||||
"mustache": "^4.2.0",
|
||||
"nanobus": "^4.5.0",
|
||||
"node-cache": "^5.1.2",
|
||||
"node-window-polyfill": "^1.0.2",
|
||||
"openid-client": "^5.4.0",
|
||||
"p-iteration": "^1.1.8",
|
||||
"p-retry": "^4.6.2",
|
||||
"pidusage": "^3.0.2",
|
||||
"reflect-metadata": "^0.1.13",
|
||||
"request": "^2.88.2",
|
||||
"semver": "^7.4.0",
|
||||
"stoppable": "^1.1.0",
|
||||
"subscriptions-transport-ws": "^0.11.0",
|
||||
"systeminformation": "^5.21.2",
|
||||
"ts-command-line-args": "^2.5.0",
|
||||
"uuid": "^9.0.0",
|
||||
"ws": "^8.13.0",
|
||||
"wtfnode": "^0.9.1",
|
||||
"xhr2": "^0.2.1",
|
||||
"zod": "^3.22.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/runtime": "^7.21.0",
|
||||
"@graphql-codegen/add": "^4.0.1",
|
||||
"@graphql-codegen/cli": "^3.3.0",
|
||||
"@graphql-codegen/fragment-matcher": "^4.0.1",
|
||||
"@graphql-codegen/import-types-preset": "^2.2.6",
|
||||
"@graphql-codegen/typed-document-node": "^4.0.0",
|
||||
"@graphql-codegen/typescript": "^3.0.3",
|
||||
"@graphql-codegen/typescript-operations": "^3.0.3",
|
||||
"@graphql-codegen/typescript-resolvers": "3.2.1",
|
||||
"@graphql-typed-document-node/core": "^3.2.0",
|
||||
"@swc/core": "^1.3.81",
|
||||
"@types/async-exit-hook": "^2.0.0",
|
||||
"@types/btoa": "^1.2.3",
|
||||
"@types/bytes": "^3.1.1",
|
||||
"@types/cli-table": "^0.3.1",
|
||||
"@types/command-exists": "^1.2.0",
|
||||
"@types/dockerode": "^3.3.16",
|
||||
"@types/express": "^4.17.17",
|
||||
"@types/graphql-fields": "^1.3.5",
|
||||
"@types/graphql-type-uuid": "^0.2.3",
|
||||
"@types/ini": "^1.3.31",
|
||||
"@types/lodash": "^4.14.192",
|
||||
"@types/mustache": "^4.2.2",
|
||||
"@types/node": "^18.17.12",
|
||||
"@types/pidusage": "^2.0.2",
|
||||
"@types/pify": "^5.0.1",
|
||||
"@types/semver": "^7.3.13",
|
||||
"@types/sendmail": "^1.4.4",
|
||||
"@types/stoppable": "^1.1.1",
|
||||
"@types/uuid": "^9.0.1",
|
||||
"@types/ws": "^8.5.4",
|
||||
"@types/wtfnode": "^0.7.0",
|
||||
"@typescript-eslint/eslint-plugin": "^5.58.0",
|
||||
"@typescript-eslint/parser": "^5.58.0",
|
||||
"@unraid/eslint-config": "github:unraid/eslint-config",
|
||||
"@vitest/coverage-v8": "^0.34.1",
|
||||
"@vitest/ui": "^0.34.0",
|
||||
"camelcase-keys": "^8.0.2",
|
||||
"cz-conventional-changelog": "3.3.0",
|
||||
"eslint": "^8.38.0",
|
||||
"eslint-import-resolver-typescript": "^3.6.0",
|
||||
"eslint-plugin-import": "^2.27.5",
|
||||
"eslint-plugin-prettier": "^5.0.0",
|
||||
"eslint-plugin-unicorn": "^48.0.1",
|
||||
"eslint-plugin-unused-imports": "^2.0.0",
|
||||
"execa": "^7.1.1",
|
||||
"filter-obj": "^5.1.0",
|
||||
"got": "^13.0.0",
|
||||
"graphql-codegen-typescript-validation-schema": "^0.11.0",
|
||||
"ip-regex": "^5.0.0",
|
||||
"json-difference": "^1.9.1",
|
||||
"log4js": "^6.9.1",
|
||||
"map-obj": "^5.0.2",
|
||||
"p-props": "^5.0.0",
|
||||
"path-exists": "^5.0.0",
|
||||
"path-type": "^5.0.0",
|
||||
"pkg": "^5.8.1",
|
||||
"pretty-bytes": "^6.1.0",
|
||||
"pretty-ms": "^8.0.0",
|
||||
"serialize-error": "^11.0.2",
|
||||
"standard-version": "^9.5.0",
|
||||
"tsup": "^7.0.0",
|
||||
"typescript": "^4.9.4",
|
||||
"typesync": "^0.11.0",
|
||||
"vite-tsconfig-paths": "^4.2.0",
|
||||
"vitest": "^0.34.0",
|
||||
"zx": "^7.2.1"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@vmngr/libvirt": "github:unraid/libvirt"
|
||||
},
|
||||
"config": {
|
||||
"commitizen": {
|
||||
"path": "./node_modules/cz-conventional-changelog"
|
||||
"bin": {
|
||||
"unraid-api": "dist/cli.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"@apollo/client": "^3.11.8",
|
||||
"@apollo/server": "^4.11.2",
|
||||
"@as-integrations/fastify": "^2.1.1",
|
||||
"@fastify/cookie": "^9.4.0",
|
||||
"@graphql-codegen/client-preset": "^4.5.0",
|
||||
"@graphql-tools/load-files": "^7.0.0",
|
||||
"@graphql-tools/merge": "^9.0.8",
|
||||
"@graphql-tools/schema": "^10.0.7",
|
||||
"@graphql-tools/utils": "^10.5.5",
|
||||
"@nestjs/apollo": "^12.2.1",
|
||||
"@nestjs/core": "^10.4.7",
|
||||
"@nestjs/graphql": "^12.2.1",
|
||||
"@nestjs/passport": "^10.0.3",
|
||||
"@nestjs/platform-fastify": "^10.4.7",
|
||||
"@nestjs/schedule": "^4.1.1",
|
||||
"@nestjs/throttler": "^6.2.1",
|
||||
"@reduxjs/toolkit": "^2.3.0",
|
||||
"@reflet/cron": "^1.3.1",
|
||||
"@runonflux/nat-upnp": "^1.0.2",
|
||||
"accesscontrol": "^2.2.1",
|
||||
"btoa": "^1.2.1",
|
||||
"bycontract": "^2.0.11",
|
||||
"bytes": "^3.1.2",
|
||||
"cacheable-lookup": "^7.0.0",
|
||||
"camelcase-keys": "^9.1.3",
|
||||
"casbin": "^5.32.0",
|
||||
"catch-exit": "^1.2.2",
|
||||
"chokidar": "^4.0.1",
|
||||
"cli-table": "^0.3.11",
|
||||
"command-exists": "^1.2.9",
|
||||
"convert": "^5.5.1",
|
||||
"cross-fetch": "^4.0.0",
|
||||
"docker-event-emitter": "^0.3.0",
|
||||
"dockerode": "^3.3.5",
|
||||
"dotenv": "^16.4.5",
|
||||
"execa": "^9.5.1",
|
||||
"exit-hook": "^4.0.0",
|
||||
"express": "^4.21.1",
|
||||
"filenamify": "^6.0.0",
|
||||
"fs-extra": "^11.2.0",
|
||||
"global-agent": "^3.0.0",
|
||||
"got": "^14.4.4",
|
||||
"graphql": "^16.9.0",
|
||||
"graphql-fields": "^2.0.3",
|
||||
"graphql-scalars": "^1.23.0",
|
||||
"graphql-subscriptions": "^2.0.0",
|
||||
"graphql-tag": "^2.12.6",
|
||||
"graphql-type-json": "^0.3.2",
|
||||
"graphql-type-uuid": "^0.2.0",
|
||||
"graphql-ws": "^5.16.0",
|
||||
"ini": "^4.1.2",
|
||||
"ip": "^2.0.1",
|
||||
"ip-regex": "^5.0.0",
|
||||
"jose": "^5.9.6",
|
||||
"lodash-es": "^4.17.21",
|
||||
"multi-ini": "^2.3.2",
|
||||
"mustache": "^4.2.0",
|
||||
"nest-access-control": "^3.1.0",
|
||||
"nest-authz": "^2.11.0",
|
||||
"nestjs-pino": "^4.1.0",
|
||||
"node-cache": "^5.1.2",
|
||||
"node-window-polyfill": "^1.0.2",
|
||||
"openid-client": "^6.1.3",
|
||||
"p-retry": "^6.2.0",
|
||||
"passport-custom": "^1.1.1",
|
||||
"passport-http-header-strategy": "^1.1.0",
|
||||
"path-type": "^6.0.0",
|
||||
"pidusage": "^3.0.2",
|
||||
"pino": "^9.5.0",
|
||||
"pino-http": "^10.3.0",
|
||||
"pino-pretty": "^11.3.0",
|
||||
"pm2": "^5.4.2",
|
||||
"reflect-metadata": "^0.1.14",
|
||||
"request": "^2.88.2",
|
||||
"semver": "^7.6.3",
|
||||
"stoppable": "^1.1.0",
|
||||
"strftime": "^0.10.3",
|
||||
"systeminformation": "^5.23.5",
|
||||
"ts-command-line-args": "^2.5.1",
|
||||
"uuid": "^11.0.2",
|
||||
"ws": "^8.18.0",
|
||||
"xhr2": "^0.2.1",
|
||||
"zod": "^3.23.8"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@graphql-codegen/add": "^5.0.3",
|
||||
"@graphql-codegen/cli": "^5.0.3",
|
||||
"@graphql-codegen/fragment-matcher": "^5.0.2",
|
||||
"@graphql-codegen/import-types-preset": "^3.0.0",
|
||||
"@graphql-codegen/typed-document-node": "^5.0.11",
|
||||
"@graphql-codegen/typescript": "^4.1.1",
|
||||
"@graphql-codegen/typescript-operations": "^4.3.1",
|
||||
"@graphql-codegen/typescript-resolvers": "4.4.1",
|
||||
"@graphql-typed-document-node/core": "^3.2.0",
|
||||
"@ianvs/prettier-plugin-sort-imports": "^4.4.0",
|
||||
"@nestjs/testing": "^10.4.7",
|
||||
"@originjs/vite-plugin-commonjs": "^1.0.3",
|
||||
"@rollup/plugin-node-resolve": "^15.3.0",
|
||||
"@swc/core": "^1.10.1",
|
||||
"@types/async-exit-hook": "^2.0.2",
|
||||
"@types/btoa": "^1.2.5",
|
||||
"@types/bytes": "^3.1.4",
|
||||
"@types/cli-table": "^0.3.4",
|
||||
"@types/command-exists": "^1.2.3",
|
||||
"@types/cors": "^2.8.17",
|
||||
"@types/dockerode": "^3.3.31",
|
||||
"@types/express": "^5.0.0",
|
||||
"@types/graphql-fields": "^1.3.9",
|
||||
"@types/graphql-type-uuid": "^0.2.6",
|
||||
"@types/ini": "^4.1.1",
|
||||
"@types/ip": "^1.1.3",
|
||||
"@types/lodash": "^4.17.13",
|
||||
"@types/mustache": "^4.2.5",
|
||||
"@types/node": "^22.9.0",
|
||||
"@types/pidusage": "^2.0.5",
|
||||
"@types/pify": "^5.0.4",
|
||||
"@types/semver": "^7.5.8",
|
||||
"@types/sendmail": "^1.4.7",
|
||||
"@types/stoppable": "^1.1.3",
|
||||
"@types/strftime": "^0.9.8",
|
||||
"@types/uuid": "^10.0.0",
|
||||
"@types/ws": "^8.5.13",
|
||||
"@types/wtfnode": "^0.7.3",
|
||||
"@vitest/coverage-v8": "^2.1.8",
|
||||
"@vitest/ui": "^2.1.4",
|
||||
"cz-conventional-changelog": "3.3.0",
|
||||
"eslint": "^9.14.0",
|
||||
"graphql-codegen-typescript-validation-schema": "^0.16.0",
|
||||
"jiti": "^2.4.0",
|
||||
"nodemon": "^3.1.7",
|
||||
"rollup-plugin-node-externals": "^7.1.3",
|
||||
"standard-version": "^9.5.0",
|
||||
"typescript": "^5.6.3",
|
||||
"typescript-eslint": "^8.13.0",
|
||||
"unplugin-swc": "^1.5.1",
|
||||
"vite": "^5.4.10",
|
||||
"vite-plugin-node": "^4.0.0",
|
||||
"vite-plugin-static-copy": "^2.0.0",
|
||||
"vite-tsconfig-paths": "^5.1.0",
|
||||
"vitest": "^2.1.8",
|
||||
"zx": "^8.2.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@vmngr/libvirt": "github:unraid/libvirt"
|
||||
},
|
||||
"overrides": {
|
||||
"eslint": {
|
||||
"jiti": "2"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,71 +2,85 @@
|
||||
import { exit } from 'process';
|
||||
import { cd, $ } from 'zx';
|
||||
|
||||
import getTags from './get-tags.mjs'
|
||||
import { getDeploymentVersion } from './get-deployment-version.mjs';
|
||||
|
||||
try {
|
||||
// Enable colours in output
|
||||
process.env.FORCE_COLOR = '1';
|
||||
// Enable colours in output
|
||||
process.env.FORCE_COLOR = '1';
|
||||
|
||||
// Ensure we have the correct working directory
|
||||
process.env.WORKDIR = process.env.WORKDIR ?? process.env.PWD;
|
||||
cd(process.env.WORKDIR);
|
||||
// Ensure we have the correct working directory
|
||||
process.env.WORKDIR ??= process.env.PWD;
|
||||
cd(process.env.WORKDIR);
|
||||
|
||||
// Clean up last deploy
|
||||
await $`rm -rf ./deploy/release`;
|
||||
await $`rm -rf ./deploy/pre-pack`;
|
||||
await $`mkdir -p ./deploy/release/`;
|
||||
await $`mkdir -p ./deploy/pre-pack/`;
|
||||
// Create deployment directories - ignore if they already exist
|
||||
await $`mkdir -p ./deploy/release`;
|
||||
await $`mkdir -p ./deploy/pre-pack`;
|
||||
|
||||
// Ensure all deps are installed
|
||||
await $`npm i`;
|
||||
await $`rm -rf ./deploy/release/*`;
|
||||
await $`rm -rf ./deploy/pre-pack/*`;
|
||||
|
||||
// Build Generated Types
|
||||
await $`npm run codegen`;
|
||||
// Build binary
|
||||
await $`npm run build`;
|
||||
// Build Generated Types
|
||||
await $`npm run codegen`;
|
||||
|
||||
// Copy binary + extra files to deployment directory
|
||||
await $`cp ./dist/api ./deploy/pre-pack/unraid-api`;
|
||||
await $`cp ./.env.production ./deploy/pre-pack/.env.production`;
|
||||
await $`cp ./.env.staging ./deploy/pre-pack/.env.staging`;
|
||||
await $`npm run build`;
|
||||
// Copy app files to plugin directory
|
||||
await $`cp -r ./src/ ./deploy/pre-pack/src/`;
|
||||
await $`cp -r ./dist/ ./deploy/pre-pack/dist/`;
|
||||
|
||||
// Get package details
|
||||
const { name, version } = await import('../package.json', {
|
||||
assert: { type: 'json' },
|
||||
}).then(pkg => pkg.default);
|
||||
// Copy environment to deployment directory
|
||||
const files = [
|
||||
'.env.production',
|
||||
'.env.staging',
|
||||
'tsconfig.json',
|
||||
'codegen.ts',
|
||||
'ecosystem.config.json',
|
||||
'vite.config.ts',
|
||||
]
|
||||
|
||||
const tags = getTags(process.env);
|
||||
|
||||
// Decide whether to use full version or just tag
|
||||
const isTaggedRelease = tags.isTagged;
|
||||
const gitShaShort = tags.shortSha;
|
||||
|
||||
const deploymentVersion = isTaggedRelease ? version : `${version}+${gitShaShort}`;
|
||||
for (const file of files) {
|
||||
await $`cp ./${file} ./deploy/pre-pack/${file}`;
|
||||
}
|
||||
|
||||
// Create deployment package.json
|
||||
await $`echo ${JSON.stringify({ name, version: deploymentVersion })} > ./deploy/pre-pack/package.json`;
|
||||
// Get package details
|
||||
const { name, version, ...rest } = await import('../package.json', {
|
||||
assert: { type: 'json' },
|
||||
}).then((pkg) => pkg.default);
|
||||
|
||||
// # Create final tgz
|
||||
await $`cp ./README.md ./deploy/pre-pack/`;
|
||||
cd('./deploy/pre-pack');
|
||||
await $`npm pack`;
|
||||
const deploymentVersion = getDeploymentVersion(process.env, version);
|
||||
|
||||
// Move unraid-api.tgz to release directory
|
||||
await $`mv unraid-api-${deploymentVersion}.tgz ../release`;
|
||||
// Create deployment package.json
|
||||
await $`echo ${JSON.stringify({
|
||||
...rest,
|
||||
name,
|
||||
version: deploymentVersion,
|
||||
})} > ./deploy/pre-pack/package.json`;
|
||||
|
||||
// Set API_VERSION output based on this command
|
||||
await $`echo "::set-output name=API_VERSION::${deploymentVersion}"`;
|
||||
// # Create final tgz
|
||||
await $`cp ./README.md ./deploy/pre-pack/`;
|
||||
|
||||
await $`cp -r ./node_modules ./deploy/pre-pack/node_modules`;
|
||||
// Install production dependencies
|
||||
cd('./deploy/pre-pack');
|
||||
|
||||
await $`npm prune --omit=dev`;
|
||||
await $`npm install --omit=dev`;
|
||||
await $`npm install github:unraid/libvirt`;
|
||||
|
||||
// Now we'll pack everything in the pre-pack directory
|
||||
await $`tar -czf ../unraid-api-${deploymentVersion}.tgz .`;
|
||||
|
||||
// Move unraid-api.tgz to release directory
|
||||
await $`mv ../unraid-api-${deploymentVersion}.tgz ../release`;
|
||||
} catch (error) {
|
||||
// Error with a command
|
||||
if (Object.keys(error).includes('stderr')) {
|
||||
console.log(`Failed building package. Exit code: ${error.exitCode}`);
|
||||
console.log(`Error: ${error.stderr}`);
|
||||
} else {
|
||||
// Normal js error
|
||||
console.log('Failed building package.');
|
||||
console.log(`Error: ${error.message}`);
|
||||
}
|
||||
// Error with a command
|
||||
if (Object.keys(error).includes('stderr')) {
|
||||
console.log(`Failed building package. Exit code: ${error.exitCode}`);
|
||||
console.log(`Error: ${error.stderr}`);
|
||||
} else {
|
||||
// Normal js error
|
||||
console.log('Failed building package.');
|
||||
console.log(`Error: ${error.message}`);
|
||||
}
|
||||
|
||||
exit(error.exitCode);
|
||||
exit(error.exitCode);
|
||||
}
|
||||
|
||||
33
api/scripts/create-session.sh
Executable file
33
api/scripts/create-session.sh
Executable file
@@ -0,0 +1,33 @@
|
||||
# This script creates a mock session on a server.
|
||||
# During local dev/testing, you should run it in the api container,
|
||||
# so the nest.js api can authenticate cookies against it.
|
||||
#
|
||||
# You should also set a cookie named 'unraid_...' whose value matches
|
||||
# the name of the session you created (where name is sess_<name>).
|
||||
# By default, this is my-session
|
||||
|
||||
sessions_dir=/var/lib/php
|
||||
default_session_name=mock-user-session
|
||||
|
||||
if [ "$1" = "--help" ]; then
|
||||
echo "This script creates a mock session on a server."
|
||||
echo ""
|
||||
echo "Usage: $0 [options]"
|
||||
echo ""
|
||||
echo "Options:"
|
||||
echo " [name] Name of the session to create (default: mock-user-session)"
|
||||
echo " --help Display this help message and exit"
|
||||
echo ""
|
||||
echo "Example: $0 a-session-name"
|
||||
echo ""
|
||||
echo "Current list of sessions:"
|
||||
ls $sessions_dir
|
||||
exit 0
|
||||
fi
|
||||
|
||||
session_name="${1:-$default_session_name}"
|
||||
|
||||
mkdir -p $sessions_dir
|
||||
touch "$sessions_dir/sess_$session_name"
|
||||
|
||||
ls $sessions_dir
|
||||
4
api/scripts/dc.sh
Executable file
4
api/scripts/dc.sh
Executable file
@@ -0,0 +1,4 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Pass all entered params after the docker compose call
|
||||
COMPOSE_PROJECT_NAME="connect" GIT_SHA=$(git rev-parse --short HEAD) IS_TAGGED=$(git describe --tags --abbrev=0 --exact-match || echo '') docker compose -f docker-compose.yml "$@"
|
||||
70
api/scripts/deploy-dev.sh
Executable file
70
api/scripts/deploy-dev.sh
Executable file
@@ -0,0 +1,70 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Path to store the last used server name
|
||||
state_file="$HOME/.deploy_state"
|
||||
|
||||
# Read the last used server name from the state file
|
||||
if [[ -f "$state_file" ]]; then
|
||||
last_server_name=$(cat "$state_file")
|
||||
else
|
||||
last_server_name=""
|
||||
fi
|
||||
|
||||
# Read the server name from the command-line argument or use the last used server name as the default
|
||||
server_name="${1:-$last_server_name}"
|
||||
|
||||
# Check if the server name is provided
|
||||
if [[ -z "$server_name" ]]; then
|
||||
echo "Please provide the SSH server name."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Save the current server name to the state file
|
||||
echo "$server_name" > "$state_file"
|
||||
|
||||
# Source directory path
|
||||
source_directory="./dist"
|
||||
|
||||
if [ ! -d "$source_directory" ]; then
|
||||
echo "The dist directory does not exist. Attempting build..."
|
||||
npm run build
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "Build failed!"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# Replace the value inside the rsync command with the user's input
|
||||
rsync_command="rsync -avz -e ssh $source_directory root@${server_name}:/usr/local/unraid-api"
|
||||
|
||||
echo "Executing the following command:"
|
||||
echo "$rsync_command"
|
||||
|
||||
# Execute the rsync command and capture the exit code
|
||||
eval "$rsync_command"
|
||||
exit_code=$?
|
||||
|
||||
# Run unraid-api restart on remote host
|
||||
dev=${DEV:-true}
|
||||
|
||||
if [ "$dev" = true ]; then
|
||||
ssh root@"${server_name}" "INTROSPECTION=true unraid-api restart"
|
||||
else
|
||||
ssh root@"${server_name}" "unraid-api restart"
|
||||
fi
|
||||
|
||||
# Play built-in sound based on the operating system
|
||||
if [[ "$OSTYPE" == "darwin"* ]]; then
|
||||
# macOS
|
||||
afplay /System/Library/Sounds/Glass.aiff
|
||||
elif [[ "$OSTYPE" == "linux-gnu" ]]; then
|
||||
# Linux
|
||||
paplay /usr/share/sounds/freedesktop/stereo/complete.oga
|
||||
elif [[ "$OSTYPE" == "msys" || "$OSTYPE" == "win32" ]]; then
|
||||
# Windows
|
||||
powershell.exe -c "(New-Object Media.SoundPlayer 'C:\Windows\Media\Windows Default.wav').PlaySync()"
|
||||
fi
|
||||
|
||||
# Exit with the rsync command's exit code
|
||||
exit $exit_code
|
||||
|
||||
29
api/scripts/get-deployment-version.mjs
Normal file
29
api/scripts/get-deployment-version.mjs
Normal file
@@ -0,0 +1,29 @@
|
||||
import { execSync } from 'child_process';
|
||||
|
||||
const runCommand = (command) => {
|
||||
try {
|
||||
return execSync(command, { stdio: 'pipe' }).toString().trim();
|
||||
} catch (error) {
|
||||
console.log('Failed to get value from tag command: ', command, error.message);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
export const getDeploymentVersion = (env = process.env, packageVersion) => {
|
||||
if (env.API_VERSION) {
|
||||
console.log(`Using env var for version: ${env.API_VERSION}`);
|
||||
return env.API_VERSION;
|
||||
} else if (env.GIT_SHA && env.IS_TAGGED) {
|
||||
console.log(`Using env vars for git tags: ${env.GIT_SHA} ${env.IS_TAGGED}`);
|
||||
return env.IS_TAGGED ? packageVersion : `${packageVersion}+${env.GIT_SHA}`;
|
||||
} else {
|
||||
const gitShortSHA = runCommand('git rev-parse --short HEAD');
|
||||
const isCommitTagged = runCommand('git describe --tags --abbrev=0 --exact-match') !== undefined;
|
||||
console.log('gitShortSHA', gitShortSHA, 'isCommitTagged', isCommitTagged);
|
||||
if (!gitShortSHA) {
|
||||
console.error('Failed to get git short SHA');
|
||||
process.exit(1);
|
||||
}
|
||||
return isCommitTagged ? packageVersion : `${packageVersion}+${gitShortSHA}`;
|
||||
}
|
||||
};
|
||||
@@ -1,33 +0,0 @@
|
||||
import { execSync } from 'child_process';
|
||||
|
||||
const runCommand = (command) => {
|
||||
try {
|
||||
return execSync(command, { stdio: 'pipe' }).toString().trim();
|
||||
} catch(error) {
|
||||
console.log('Failed to get value from tag command: ', command, error.message);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
const getTags = (env = process.env) => {
|
||||
|
||||
if (env.GIT_SHA) {
|
||||
return {
|
||||
shortSha: env.GIT_SHA,
|
||||
isTagged: Boolean(env.IS_TAGGED)
|
||||
}
|
||||
} else {
|
||||
const gitShortSHA = runCommand('git rev-parse --short HEAD');
|
||||
const isCommitTagged = runCommand('git describe --tags --abbrev=0 --exact-match') !== undefined;
|
||||
console.log('gitShortSHA', gitShortSHA, 'isCommitTagged', isCommitTagged);
|
||||
if (!gitShortSHA) {
|
||||
throw new Error('Failing build due to missing SHA');
|
||||
}
|
||||
return {
|
||||
shortSha: gitShortSHA,
|
||||
isTagged: isCommitTagged
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default getTags;
|
||||
@@ -1,31 +0,0 @@
|
||||
import { beforeEach, expect, test, vi } from 'vitest';
|
||||
|
||||
// Preloading imports for faster tests
|
||||
import '@app/cli/commands/restart';
|
||||
import '@app/cli/commands/start';
|
||||
import '@app/cli/commands/stop';
|
||||
|
||||
beforeEach(() => {
|
||||
vi.resetAllMocks();
|
||||
});
|
||||
|
||||
test('calls stop and then start', async () => {
|
||||
vi.mock('@app/cli/commands/start');
|
||||
vi.mock('@app/cli/commands/stop');
|
||||
// Call restart
|
||||
const { restart } = await import('@app/cli/commands/restart');
|
||||
const { start } = await import('@app/cli/commands/start');
|
||||
const { stop } = await import('@app/cli/commands/stop');
|
||||
await restart();
|
||||
|
||||
// Check stop was called
|
||||
expect(vi.mocked(stop).mock.calls.length).toBe(1);
|
||||
|
||||
// Check start was called
|
||||
expect(vi.mocked(start).mock.calls.length).toBe(1);
|
||||
|
||||
// Check stop was called first
|
||||
expect(vi.mocked(stop).mock.invocationCallOrder[0]).toBeLessThan(
|
||||
vi.mocked(start).mock.invocationCallOrder[0]
|
||||
);
|
||||
});
|
||||
48
api/src/__test__/common/allowed-origins.test.ts
Normal file
48
api/src/__test__/common/allowed-origins.test.ts
Normal file
@@ -0,0 +1,48 @@
|
||||
import 'reflect-metadata';
|
||||
import { expect, test } from 'vitest';
|
||||
|
||||
// Preloading imports for faster tests
|
||||
import '@app/common/allowed-origins';
|
||||
import '@app/store/modules/emhttp';
|
||||
import '@app/store';
|
||||
|
||||
test('Returns allowed origins', async () => {
|
||||
const { store } = await import('@app/store');
|
||||
const { loadStateFiles } = await import('@app/store/modules/emhttp');
|
||||
const { getAllowedOrigins } = await import('@app/common/allowed-origins');
|
||||
const { loadConfigFile } = await import('@app/store/modules/config');
|
||||
|
||||
// Load state files into store
|
||||
await store.dispatch(loadStateFiles());
|
||||
await store.dispatch(loadConfigFile());
|
||||
|
||||
// Get allowed origins
|
||||
expect(getAllowedOrigins()).toMatchInlineSnapshot(`
|
||||
[
|
||||
"/var/run/unraid-notifications.sock",
|
||||
"/var/run/unraid-php.sock",
|
||||
"/var/run/unraid-cli.sock",
|
||||
"http://localhost:8080",
|
||||
"https://localhost:4443",
|
||||
"https://tower.local:4443",
|
||||
"https://192.168.1.150:4443",
|
||||
"https://tower:4443",
|
||||
"https://192-168-1-150.thisisfourtyrandomcharacters012345678900.myunraid.net:4443",
|
||||
"https://85-121-123-122.thisisfourtyrandomcharacters012345678900.myunraid.net:8443",
|
||||
"https://10-252-0-1.hash.myunraid.net:4443",
|
||||
"https://10-252-1-1.hash.myunraid.net:4443",
|
||||
"https://10-253-3-1.hash.myunraid.net:4443",
|
||||
"https://10-253-4-1.hash.myunraid.net:4443",
|
||||
"https://10-253-5-1.hash.myunraid.net:4443",
|
||||
"https://10-100-0-1.hash.myunraid.net:4443",
|
||||
"https://10-100-0-2.hash.myunraid.net:4443",
|
||||
"https://10-123-1-2.hash.myunraid.net:4443",
|
||||
"https://221-123-121-112.hash.myunraid.net:4443",
|
||||
"https://google.com",
|
||||
"https://test.com",
|
||||
"https://connect.myunraid.net",
|
||||
"https://connect-staging.myunraid.net",
|
||||
"https://dev-my.myunraid.net:4000",
|
||||
]
|
||||
`);
|
||||
});
|
||||
@@ -1,120 +0,0 @@
|
||||
import { expect, test, vi } from 'vitest';
|
||||
import { store } from '@app/store';
|
||||
|
||||
import { loadStateFiles } from '@app/store/modules/emhttp';
|
||||
|
||||
vi.mock('@vmngr/libvirt', () => ({
|
||||
ConnectListAllDomainsFlags: {
|
||||
ACTIVE: 0,
|
||||
INACTIVE: 1,
|
||||
},
|
||||
}));
|
||||
|
||||
vi.mock('@app/core/log', () => ({
|
||||
logger: {
|
||||
info: vi.fn(),
|
||||
error: vi.fn(),
|
||||
debug: vi.fn(),
|
||||
trace: vi.fn(),
|
||||
addContext: vi.fn(),
|
||||
removeContext: vi.fn(),
|
||||
},
|
||||
dashboardLogger: {
|
||||
info: vi.fn(),
|
||||
error: vi.fn((...input) => console.log(input)),
|
||||
debug: vi.fn(),
|
||||
trace: vi.fn(),
|
||||
addContext: vi.fn(),
|
||||
removeContext: vi.fn(),
|
||||
},
|
||||
emhttpLogger: {
|
||||
info: vi.fn(),
|
||||
error: vi.fn(),
|
||||
debug: vi.fn(),
|
||||
trace: vi.fn(),
|
||||
addContext: vi.fn(),
|
||||
removeContext: vi.fn(),
|
||||
},
|
||||
}));
|
||||
|
||||
vi.mock('@app/common/two-factor', () => ({
|
||||
checkTwoFactorEnabled: vi.fn(() => ({
|
||||
isRemoteEnabled: false,
|
||||
isLocalEnabled: false,
|
||||
})),
|
||||
}));
|
||||
|
||||
vi.mock('@app/common/dashboard/boot-timestamp', () => ({
|
||||
bootTimestamp: new Date('2022-06-10T04:35:58.276Z'),
|
||||
}));
|
||||
|
||||
test('Returns generated data', async () => {
|
||||
await store.dispatch(loadStateFiles()).unwrap();
|
||||
|
||||
const { generateData } = await import('@app/common/dashboard/generate-data');
|
||||
const result = await generateData();
|
||||
|
||||
expect(result).toMatchInlineSnapshot(`
|
||||
{
|
||||
"apps": {
|
||||
"installed": 0,
|
||||
"started": 0,
|
||||
},
|
||||
"array": {
|
||||
"capacity": {
|
||||
"bytes": {
|
||||
"free": 19495825571000,
|
||||
"total": 41994745901000,
|
||||
"used": 22498920330000,
|
||||
},
|
||||
},
|
||||
"state": "STOPPED",
|
||||
},
|
||||
"config": {
|
||||
"valid": true,
|
||||
},
|
||||
"display": {
|
||||
"case": {
|
||||
"base64": "",
|
||||
"error": "",
|
||||
"icon": "case-model.png",
|
||||
"url": "",
|
||||
},
|
||||
},
|
||||
"os": {
|
||||
"hostname": "Tower",
|
||||
"uptime": "2022-06-10T04:35:58.276Z",
|
||||
},
|
||||
"services": [
|
||||
{
|
||||
"name": "unraid-api",
|
||||
"online": true,
|
||||
"uptime": {
|
||||
"timestamp": "2022-06-10T04:35:58.276Z",
|
||||
},
|
||||
"version": "THIS_WILL_BE_REPLACED_WHEN_BUILT",
|
||||
},
|
||||
{
|
||||
"name": "dynamic-remote-access",
|
||||
"online": false,
|
||||
"uptime": {
|
||||
"timestamp": "2022-06-10T04:35:58.276Z",
|
||||
},
|
||||
"version": "DISABLED",
|
||||
},
|
||||
],
|
||||
"vars": {
|
||||
"flashGuid": "0000-0000-0000-000000000000",
|
||||
"regState": "PRO",
|
||||
"regTy": "PRO",
|
||||
},
|
||||
"versions": {
|
||||
"unraid": "6.11.2",
|
||||
},
|
||||
"vms": {
|
||||
"installed": 0,
|
||||
"started": 0,
|
||||
},
|
||||
}
|
||||
`);
|
||||
}, 10_000);
|
||||
@@ -1,360 +0,0 @@
|
||||
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
||||
|
||||
exports[`Returns default permissions 1`] = `
|
||||
{
|
||||
"admin": {
|
||||
"extends": "user",
|
||||
"permissions": [
|
||||
{
|
||||
"action": "read:any",
|
||||
"attributes": "*",
|
||||
"resource": "apikey",
|
||||
},
|
||||
{
|
||||
"action": "read:any",
|
||||
"attributes": "*",
|
||||
"resource": "array",
|
||||
},
|
||||
{
|
||||
"action": "read:any",
|
||||
"attributes": "*",
|
||||
"resource": "cpu",
|
||||
},
|
||||
{
|
||||
"action": "read:any",
|
||||
"attributes": "*",
|
||||
"resource": "crash-reporting-enabled",
|
||||
},
|
||||
{
|
||||
"action": "read:any",
|
||||
"attributes": "*",
|
||||
"resource": "device",
|
||||
},
|
||||
{
|
||||
"action": "read:any",
|
||||
"attributes": "*",
|
||||
"resource": "device/unassigned",
|
||||
},
|
||||
{
|
||||
"action": "read:any",
|
||||
"attributes": "*",
|
||||
"resource": "disk",
|
||||
},
|
||||
{
|
||||
"action": "read:any",
|
||||
"attributes": "*",
|
||||
"resource": "disk/settings",
|
||||
},
|
||||
{
|
||||
"action": "read:any",
|
||||
"attributes": "*",
|
||||
"resource": "display",
|
||||
},
|
||||
{
|
||||
"action": "read:any",
|
||||
"attributes": "*",
|
||||
"resource": "docker/container",
|
||||
},
|
||||
{
|
||||
"action": "read:any",
|
||||
"attributes": "*",
|
||||
"resource": "docker/network",
|
||||
},
|
||||
{
|
||||
"action": "read:any",
|
||||
"attributes": "*",
|
||||
"resource": "flash",
|
||||
},
|
||||
{
|
||||
"action": "read:any",
|
||||
"attributes": "*",
|
||||
"resource": "info",
|
||||
},
|
||||
{
|
||||
"action": "read:any",
|
||||
"attributes": "*",
|
||||
"resource": "license-key",
|
||||
},
|
||||
{
|
||||
"action": "read:any",
|
||||
"attributes": "*",
|
||||
"resource": "machine-id",
|
||||
},
|
||||
{
|
||||
"action": "read:any",
|
||||
"attributes": "*",
|
||||
"resource": "memory",
|
||||
},
|
||||
{
|
||||
"action": "read:any",
|
||||
"attributes": "*",
|
||||
"resource": "notifications",
|
||||
},
|
||||
{
|
||||
"action": "read:any",
|
||||
"attributes": "*",
|
||||
"resource": "online",
|
||||
},
|
||||
{
|
||||
"action": "read:any",
|
||||
"attributes": "*",
|
||||
"resource": "os",
|
||||
},
|
||||
{
|
||||
"action": "read:any",
|
||||
"attributes": "*",
|
||||
"resource": "owner",
|
||||
},
|
||||
{
|
||||
"action": "read:any",
|
||||
"attributes": "*",
|
||||
"resource": "parity-history",
|
||||
},
|
||||
{
|
||||
"action": "read:any",
|
||||
"attributes": "*",
|
||||
"resource": "permission",
|
||||
},
|
||||
{
|
||||
"action": "read:any",
|
||||
"attributes": "*",
|
||||
"resource": "registration",
|
||||
},
|
||||
{
|
||||
"action": "read:any",
|
||||
"attributes": "*",
|
||||
"resource": "servers",
|
||||
},
|
||||
{
|
||||
"action": "read:any",
|
||||
"attributes": "*",
|
||||
"resource": "service",
|
||||
},
|
||||
{
|
||||
"action": "read:any",
|
||||
"attributes": "*",
|
||||
"resource": "service/emhttpd",
|
||||
},
|
||||
{
|
||||
"action": "read:any",
|
||||
"attributes": "*",
|
||||
"resource": "service/unraid-api",
|
||||
},
|
||||
{
|
||||
"action": "read:any",
|
||||
"attributes": "*",
|
||||
"resource": "services",
|
||||
},
|
||||
{
|
||||
"action": "read:any",
|
||||
"attributes": "*",
|
||||
"resource": "share",
|
||||
},
|
||||
{
|
||||
"action": "read:any",
|
||||
"attributes": "*",
|
||||
"resource": "software-versions",
|
||||
},
|
||||
{
|
||||
"action": "read:any",
|
||||
"attributes": "*",
|
||||
"resource": "unraid-version",
|
||||
},
|
||||
{
|
||||
"action": "read:any",
|
||||
"attributes": "*",
|
||||
"resource": "uptime",
|
||||
},
|
||||
{
|
||||
"action": "read:any",
|
||||
"attributes": "*",
|
||||
"resource": "user",
|
||||
},
|
||||
{
|
||||
"action": "read:any",
|
||||
"attributes": "*",
|
||||
"resource": "vars",
|
||||
},
|
||||
{
|
||||
"action": "read:any",
|
||||
"attributes": "*",
|
||||
"resource": "vms",
|
||||
},
|
||||
{
|
||||
"action": "read:any",
|
||||
"attributes": "*",
|
||||
"resource": "vms/domain",
|
||||
},
|
||||
{
|
||||
"action": "read:any",
|
||||
"attributes": "*",
|
||||
"resource": "vms/network",
|
||||
},
|
||||
],
|
||||
},
|
||||
"guest": {
|
||||
"permissions": [
|
||||
{
|
||||
"action": "read:any",
|
||||
"attributes": "*",
|
||||
"resource": "me",
|
||||
},
|
||||
{
|
||||
"action": "read:any",
|
||||
"attributes": "*",
|
||||
"resource": "welcome",
|
||||
},
|
||||
],
|
||||
},
|
||||
"my_servers": {
|
||||
"extends": "guest",
|
||||
"permissions": [
|
||||
{
|
||||
"action": "read:any",
|
||||
"attributes": "*",
|
||||
"resource": "dashboard",
|
||||
},
|
||||
{
|
||||
"action": "read:own",
|
||||
"attributes": "*",
|
||||
"resource": "two-factor",
|
||||
},
|
||||
{
|
||||
"action": "read:any",
|
||||
"attributes": "*",
|
||||
"resource": "array",
|
||||
},
|
||||
{
|
||||
"action": "read:any",
|
||||
"attributes": "*",
|
||||
"resource": "docker/container",
|
||||
},
|
||||
{
|
||||
"action": "read:any",
|
||||
"attributes": "*",
|
||||
"resource": "docker/network",
|
||||
},
|
||||
{
|
||||
"action": "read:any",
|
||||
"attributes": "*",
|
||||
"resource": "notifications",
|
||||
},
|
||||
{
|
||||
"action": "read:any",
|
||||
"attributes": "*",
|
||||
"resource": "vms/domain",
|
||||
},
|
||||
{
|
||||
"action": "read:any",
|
||||
"attributes": "*",
|
||||
"resource": "unraid-version",
|
||||
},
|
||||
],
|
||||
},
|
||||
"notifier": {
|
||||
"extends": "guest",
|
||||
"permissions": [
|
||||
{
|
||||
"action": "create:own",
|
||||
"attributes": "*",
|
||||
"resource": "notifications",
|
||||
},
|
||||
],
|
||||
},
|
||||
"upc": {
|
||||
"extends": "guest",
|
||||
"permissions": [
|
||||
{
|
||||
"action": "read:own",
|
||||
"attributes": "*",
|
||||
"resource": "apikey",
|
||||
},
|
||||
{
|
||||
"action": "read:own",
|
||||
"attributes": "*",
|
||||
"resource": "cloud",
|
||||
},
|
||||
{
|
||||
"action": "read:any",
|
||||
"attributes": "*",
|
||||
"resource": "config",
|
||||
},
|
||||
{
|
||||
"action": "read:any",
|
||||
"attributes": "*",
|
||||
"resource": "crash-reporting-enabled",
|
||||
},
|
||||
{
|
||||
"action": "read:any",
|
||||
"attributes": "*",
|
||||
"resource": "disk",
|
||||
},
|
||||
{
|
||||
"action": "read:any",
|
||||
"attributes": "*",
|
||||
"resource": "display",
|
||||
},
|
||||
{
|
||||
"action": "read:any",
|
||||
"attributes": "*",
|
||||
"resource": "flash",
|
||||
},
|
||||
{
|
||||
"action": "read:any",
|
||||
"attributes": "*",
|
||||
"resource": "os",
|
||||
},
|
||||
{
|
||||
"action": "read:any",
|
||||
"attributes": "*",
|
||||
"resource": "owner",
|
||||
},
|
||||
{
|
||||
"action": "read:any",
|
||||
"attributes": "*",
|
||||
"resource": "permission",
|
||||
},
|
||||
{
|
||||
"action": "read:any",
|
||||
"attributes": "*",
|
||||
"resource": "registration",
|
||||
},
|
||||
{
|
||||
"action": "read:any",
|
||||
"attributes": "*",
|
||||
"resource": "servers",
|
||||
},
|
||||
{
|
||||
"action": "read:any",
|
||||
"attributes": "*",
|
||||
"resource": "vars",
|
||||
},
|
||||
{
|
||||
"action": "read:own",
|
||||
"attributes": "*",
|
||||
"resource": "connect",
|
||||
},
|
||||
{
|
||||
"action": "update:own",
|
||||
"attributes": "*",
|
||||
"resource": "connect",
|
||||
},
|
||||
],
|
||||
},
|
||||
"user": {
|
||||
"extends": "guest",
|
||||
"permissions": [
|
||||
{
|
||||
"action": "read:own",
|
||||
"attributes": "*",
|
||||
"resource": "apikey",
|
||||
},
|
||||
{
|
||||
"action": "read:any",
|
||||
"attributes": "*",
|
||||
"resource": "permission",
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
`;
|
||||
457
api/src/__test__/core/__snapshots__/permissions.test.ts.snap
Normal file
457
api/src/__test__/core/__snapshots__/permissions.test.ts.snap
Normal file
@@ -0,0 +1,457 @@
|
||||
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
||||
|
||||
exports[`Returns default permissions 1`] = `
|
||||
RolesBuilder {
|
||||
"_grants": {
|
||||
"admin": {
|
||||
"$extend": [
|
||||
"guest",
|
||||
],
|
||||
"apikey": {
|
||||
"read:any": [
|
||||
"*",
|
||||
],
|
||||
},
|
||||
"array": {
|
||||
"read:any": [
|
||||
"*",
|
||||
],
|
||||
},
|
||||
"cloud": {
|
||||
"read:own": [
|
||||
"*",
|
||||
],
|
||||
},
|
||||
"config": {
|
||||
"read:any": [
|
||||
"*",
|
||||
],
|
||||
"update:own": [
|
||||
"*",
|
||||
],
|
||||
},
|
||||
"connect": {
|
||||
"read:own": [
|
||||
"*",
|
||||
],
|
||||
"update:own": [
|
||||
"*",
|
||||
],
|
||||
},
|
||||
"cpu": {
|
||||
"read:any": [
|
||||
"*",
|
||||
],
|
||||
},
|
||||
"crash-reporting-enabled": {
|
||||
"read:any": [
|
||||
"*",
|
||||
],
|
||||
},
|
||||
"customizations": {
|
||||
"read:any": [
|
||||
"*",
|
||||
],
|
||||
},
|
||||
"device": {
|
||||
"read:any": [
|
||||
"*",
|
||||
],
|
||||
},
|
||||
"device/unassigned": {
|
||||
"read:any": [
|
||||
"*",
|
||||
],
|
||||
},
|
||||
"disk": {
|
||||
"read:any": [
|
||||
"*",
|
||||
],
|
||||
},
|
||||
"disk/settings": {
|
||||
"read:any": [
|
||||
"*",
|
||||
],
|
||||
},
|
||||
"display": {
|
||||
"read:any": [
|
||||
"*",
|
||||
],
|
||||
},
|
||||
"docker/container": {
|
||||
"read:any": [
|
||||
"*",
|
||||
],
|
||||
},
|
||||
"docker/network": {
|
||||
"read:any": [
|
||||
"*",
|
||||
],
|
||||
},
|
||||
"flash": {
|
||||
"read:any": [
|
||||
"*",
|
||||
],
|
||||
},
|
||||
"info": {
|
||||
"read:any": [
|
||||
"*",
|
||||
],
|
||||
},
|
||||
"license-key": {
|
||||
"read:any": [
|
||||
"*",
|
||||
],
|
||||
},
|
||||
"logs": {
|
||||
"read:any": [
|
||||
"*",
|
||||
],
|
||||
},
|
||||
"machine-id": {
|
||||
"read:any": [
|
||||
"*",
|
||||
],
|
||||
},
|
||||
"memory": {
|
||||
"read:any": [
|
||||
"*",
|
||||
],
|
||||
},
|
||||
"notifications": {
|
||||
"create:any": [
|
||||
"*",
|
||||
],
|
||||
"read:any": [
|
||||
"*",
|
||||
],
|
||||
},
|
||||
"online": {
|
||||
"read:any": [
|
||||
"*",
|
||||
],
|
||||
},
|
||||
"os": {
|
||||
"read:any": [
|
||||
"*",
|
||||
],
|
||||
},
|
||||
"owner": {
|
||||
"read:any": [
|
||||
"*",
|
||||
],
|
||||
},
|
||||
"parity-history": {
|
||||
"read:any": [
|
||||
"*",
|
||||
],
|
||||
},
|
||||
"permission": {
|
||||
"read:any": [
|
||||
"*",
|
||||
],
|
||||
},
|
||||
"registration": {
|
||||
"read:any": [
|
||||
"*",
|
||||
],
|
||||
},
|
||||
"servers": {
|
||||
"read:any": [
|
||||
"*",
|
||||
],
|
||||
},
|
||||
"service": {
|
||||
"read:any": [
|
||||
"*",
|
||||
],
|
||||
},
|
||||
"service/emhttpd": {
|
||||
"read:any": [
|
||||
"*",
|
||||
],
|
||||
},
|
||||
"service/unraid-api": {
|
||||
"read:any": [
|
||||
"*",
|
||||
],
|
||||
},
|
||||
"services": {
|
||||
"read:any": [
|
||||
"*",
|
||||
],
|
||||
},
|
||||
"share": {
|
||||
"read:any": [
|
||||
"*",
|
||||
],
|
||||
},
|
||||
"software-versions": {
|
||||
"read:any": [
|
||||
"*",
|
||||
],
|
||||
},
|
||||
"unraid-version": {
|
||||
"read:any": [
|
||||
"*",
|
||||
],
|
||||
},
|
||||
"uptime": {
|
||||
"read:any": [
|
||||
"*",
|
||||
],
|
||||
},
|
||||
"user": {
|
||||
"read:any": [
|
||||
"*",
|
||||
],
|
||||
},
|
||||
"vars": {
|
||||
"read:any": [
|
||||
"*",
|
||||
],
|
||||
},
|
||||
"vms": {
|
||||
"read:any": [
|
||||
"*",
|
||||
],
|
||||
},
|
||||
"vms/domain": {
|
||||
"read:any": [
|
||||
"*",
|
||||
],
|
||||
},
|
||||
"vms/network": {
|
||||
"read:any": [
|
||||
"*",
|
||||
],
|
||||
},
|
||||
},
|
||||
"guest": {
|
||||
"me": {
|
||||
"read:any": [
|
||||
"*",
|
||||
],
|
||||
},
|
||||
"welcome": {
|
||||
"read:any": [
|
||||
"*",
|
||||
],
|
||||
},
|
||||
},
|
||||
"my_servers": {
|
||||
"$extend": [
|
||||
"guest",
|
||||
],
|
||||
"array": {
|
||||
"read:any": [
|
||||
"*",
|
||||
],
|
||||
},
|
||||
"config": {
|
||||
"read:any": [
|
||||
"*",
|
||||
],
|
||||
},
|
||||
"connect": {
|
||||
"read:any": [
|
||||
"*",
|
||||
],
|
||||
},
|
||||
"connect/dynamic-remote-access": {
|
||||
"read:any": [
|
||||
"*",
|
||||
],
|
||||
"update:own": [
|
||||
"*",
|
||||
],
|
||||
},
|
||||
"customizations": {
|
||||
"read:any": [
|
||||
"*",
|
||||
],
|
||||
},
|
||||
"dashboard": {
|
||||
"read:any": [
|
||||
"*",
|
||||
],
|
||||
},
|
||||
"display": {
|
||||
"read:any": [
|
||||
"*",
|
||||
],
|
||||
},
|
||||
"docker": {
|
||||
"read:any": [
|
||||
"*",
|
||||
],
|
||||
},
|
||||
"docker/container": {
|
||||
"read:any": [
|
||||
"*",
|
||||
],
|
||||
},
|
||||
"info": {
|
||||
"read:any": [
|
||||
"*",
|
||||
],
|
||||
},
|
||||
"logs": {
|
||||
"read:any": [
|
||||
"*",
|
||||
],
|
||||
},
|
||||
"network": {
|
||||
"read:any": [
|
||||
"*",
|
||||
],
|
||||
},
|
||||
"notifications": {
|
||||
"read:any": [
|
||||
"*",
|
||||
],
|
||||
},
|
||||
"services": {
|
||||
"read:any": [
|
||||
"*",
|
||||
],
|
||||
},
|
||||
"unraid-version": {
|
||||
"read:any": [
|
||||
"*",
|
||||
],
|
||||
},
|
||||
"vars": {
|
||||
"read:any": [
|
||||
"*",
|
||||
],
|
||||
},
|
||||
"vms": {
|
||||
"read:any": [
|
||||
"*",
|
||||
],
|
||||
},
|
||||
"vms/domain": {
|
||||
"read:any": [
|
||||
"*",
|
||||
],
|
||||
},
|
||||
},
|
||||
"notifier": {
|
||||
"$extend": [
|
||||
"guest",
|
||||
],
|
||||
"notifications": {
|
||||
"create:own": [
|
||||
"*",
|
||||
],
|
||||
},
|
||||
},
|
||||
"upc": {
|
||||
"$extend": [
|
||||
"guest",
|
||||
],
|
||||
"apikey": {
|
||||
"read:own": [
|
||||
"*",
|
||||
],
|
||||
},
|
||||
"cloud": {
|
||||
"read:own": [
|
||||
"*",
|
||||
],
|
||||
},
|
||||
"config": {
|
||||
"read:any": [
|
||||
"*",
|
||||
],
|
||||
"update:own": [
|
||||
"*",
|
||||
],
|
||||
},
|
||||
"connect": {
|
||||
"read:own": [
|
||||
"*",
|
||||
],
|
||||
"update:own": [
|
||||
"*",
|
||||
],
|
||||
},
|
||||
"crash-reporting-enabled": {
|
||||
"read:any": [
|
||||
"*",
|
||||
],
|
||||
},
|
||||
"customizations": {
|
||||
"read:any": [
|
||||
"*",
|
||||
],
|
||||
},
|
||||
"disk": {
|
||||
"read:any": [
|
||||
"*",
|
||||
],
|
||||
},
|
||||
"display": {
|
||||
"read:any": [
|
||||
"*",
|
||||
],
|
||||
},
|
||||
"flash": {
|
||||
"read:any": [
|
||||
"*",
|
||||
],
|
||||
},
|
||||
"info": {
|
||||
"read:any": [
|
||||
"*",
|
||||
],
|
||||
},
|
||||
"logs": {
|
||||
"read:any": [
|
||||
"*",
|
||||
],
|
||||
},
|
||||
"notifications": {
|
||||
"read:any": [
|
||||
"*",
|
||||
],
|
||||
"update:any": [
|
||||
"*",
|
||||
],
|
||||
},
|
||||
"os": {
|
||||
"read:any": [
|
||||
"*",
|
||||
],
|
||||
},
|
||||
"owner": {
|
||||
"read:any": [
|
||||
"*",
|
||||
],
|
||||
},
|
||||
"permission": {
|
||||
"read:any": [
|
||||
"*",
|
||||
],
|
||||
},
|
||||
"registration": {
|
||||
"read:any": [
|
||||
"*",
|
||||
],
|
||||
},
|
||||
"servers": {
|
||||
"read:any": [
|
||||
"*",
|
||||
],
|
||||
},
|
||||
"vars": {
|
||||
"read:any": [
|
||||
"*",
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
"_isLocked": false,
|
||||
}
|
||||
`;
|
||||
@@ -10,12 +10,14 @@ test('Creates an array event', async () => {
|
||||
);
|
||||
const { store } = await import('@app/store');
|
||||
const { loadStateFiles } = await import('@app/store/modules/emhttp');
|
||||
|
||||
const { loadConfigFile } = await import('@app/store/modules/config');
|
||||
// Load state files into store
|
||||
await store.dispatch(loadStateFiles());
|
||||
|
||||
await store.dispatch(loadConfigFile());
|
||||
|
||||
const arrayEvent = getArrayData(store.getState);
|
||||
expect(arrayEvent).toMatchInlineSnapshot(`
|
||||
expect(arrayEvent).toMatchObject(
|
||||
{
|
||||
"boot": {
|
||||
"comment": "Unraid OS boot device",
|
||||
@@ -177,6 +179,7 @@ test('Creates an array event', async () => {
|
||||
"warning": null,
|
||||
},
|
||||
],
|
||||
"id": expect.any(String),
|
||||
"parities": [
|
||||
{
|
||||
"comment": null,
|
||||
@@ -205,5 +208,5 @@ test('Creates an array event', async () => {
|
||||
],
|
||||
"state": "STOPPED",
|
||||
}
|
||||
`);
|
||||
);
|
||||
});
|
||||
|
||||
8
api/src/__test__/core/permissions.test.ts
Normal file
8
api/src/__test__/core/permissions.test.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
import 'reflect-metadata';
|
||||
|
||||
import { expect, test } from 'vitest';
|
||||
import { setupPermissions } from '@app/core/permissions';
|
||||
|
||||
test('Returns default permissions', () => {
|
||||
expect(setupPermissions()).toMatchSnapshot();
|
||||
});
|
||||
169
api/src/__test__/core/utils/files/config-file-normalizer.test.ts
Normal file
169
api/src/__test__/core/utils/files/config-file-normalizer.test.ts
Normal file
@@ -0,0 +1,169 @@
|
||||
import 'reflect-metadata';
|
||||
|
||||
import { cloneDeep } from 'lodash-es';
|
||||
import { expect, test } from 'vitest';
|
||||
|
||||
import { getWriteableConfig } from '@app/core/utils/files/config-file-normalizer';
|
||||
import { initialState } from '@app/store/modules/config';
|
||||
|
||||
test('it creates a FLASH config with NO OPTIONAL values', () => {
|
||||
const basicConfig = initialState;
|
||||
const config = getWriteableConfig(basicConfig, 'flash');
|
||||
expect(config).toMatchInlineSnapshot(`
|
||||
{
|
||||
"api": {
|
||||
"extraOrigins": "",
|
||||
"version": "",
|
||||
},
|
||||
"local": {},
|
||||
"notifier": {
|
||||
"apikey": "",
|
||||
},
|
||||
"remote": {
|
||||
"accesstoken": "",
|
||||
"apikey": "",
|
||||
"avatar": "",
|
||||
"dynamicRemoteAccessType": "DISABLED",
|
||||
"email": "",
|
||||
"idtoken": "",
|
||||
"localApiKey": "",
|
||||
"refreshtoken": "",
|
||||
"regWizTime": "",
|
||||
"username": "",
|
||||
"wanaccess": "",
|
||||
"wanport": "",
|
||||
},
|
||||
"upc": {
|
||||
"apikey": "",
|
||||
},
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
test('it creates a MEMORY config with NO OPTIONAL values', () => {
|
||||
const basicConfig = initialState;
|
||||
const config = getWriteableConfig(basicConfig, 'memory');
|
||||
expect(config).toMatchInlineSnapshot(`
|
||||
{
|
||||
"api": {
|
||||
"extraOrigins": "",
|
||||
"version": "",
|
||||
},
|
||||
"connectionStatus": {
|
||||
"minigraph": "PRE_INIT",
|
||||
},
|
||||
"local": {},
|
||||
"notifier": {
|
||||
"apikey": "",
|
||||
},
|
||||
"remote": {
|
||||
"accesstoken": "",
|
||||
"allowedOrigins": "/var/run/unraid-notifications.sock, /var/run/unraid-php.sock, /var/run/unraid-cli.sock, https://connect.myunraid.net, https://connect-staging.myunraid.net, https://dev-my.myunraid.net:4000",
|
||||
"apikey": "",
|
||||
"avatar": "",
|
||||
"dynamicRemoteAccessType": "DISABLED",
|
||||
"email": "",
|
||||
"idtoken": "",
|
||||
"localApiKey": "",
|
||||
"refreshtoken": "",
|
||||
"regWizTime": "",
|
||||
"username": "",
|
||||
"wanaccess": "",
|
||||
"wanport": "",
|
||||
},
|
||||
"upc": {
|
||||
"apikey": "",
|
||||
},
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
test('it creates a FLASH config with OPTIONAL values', () => {
|
||||
const basicConfig = cloneDeep(initialState);
|
||||
// 2fa & t2fa should be ignored
|
||||
basicConfig.remote['2Fa'] = 'yes';
|
||||
basicConfig.local['2Fa'] = 'yes';
|
||||
basicConfig.local.showT2Fa = 'yes';
|
||||
|
||||
basicConfig.api.extraOrigins = 'myextra.origins';
|
||||
basicConfig.remote.upnpEnabled = 'yes';
|
||||
basicConfig.connectionStatus.upnpStatus = 'Turned On';
|
||||
const config = getWriteableConfig(basicConfig, 'flash');
|
||||
expect(config).toMatchInlineSnapshot(`
|
||||
{
|
||||
"api": {
|
||||
"extraOrigins": "myextra.origins",
|
||||
"version": "",
|
||||
},
|
||||
"local": {},
|
||||
"notifier": {
|
||||
"apikey": "",
|
||||
},
|
||||
"remote": {
|
||||
"accesstoken": "",
|
||||
"apikey": "",
|
||||
"avatar": "",
|
||||
"dynamicRemoteAccessType": "DISABLED",
|
||||
"email": "",
|
||||
"idtoken": "",
|
||||
"localApiKey": "",
|
||||
"refreshtoken": "",
|
||||
"regWizTime": "",
|
||||
"upnpEnabled": "yes",
|
||||
"username": "",
|
||||
"wanaccess": "",
|
||||
"wanport": "",
|
||||
},
|
||||
"upc": {
|
||||
"apikey": "",
|
||||
},
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
test('it creates a MEMORY config with OPTIONAL values', () => {
|
||||
const basicConfig = cloneDeep(initialState);
|
||||
// 2fa & t2fa should be ignored
|
||||
basicConfig.remote['2Fa'] = 'yes';
|
||||
basicConfig.local['2Fa'] = 'yes';
|
||||
basicConfig.local.showT2Fa = 'yes';
|
||||
basicConfig.api.extraOrigins = 'myextra.origins';
|
||||
basicConfig.remote.upnpEnabled = 'yes';
|
||||
basicConfig.connectionStatus.upnpStatus = 'Turned On';
|
||||
const config = getWriteableConfig(basicConfig, 'memory');
|
||||
expect(config).toMatchInlineSnapshot(`
|
||||
{
|
||||
"api": {
|
||||
"extraOrigins": "myextra.origins",
|
||||
"version": "",
|
||||
},
|
||||
"connectionStatus": {
|
||||
"minigraph": "PRE_INIT",
|
||||
"upnpStatus": "Turned On",
|
||||
},
|
||||
"local": {},
|
||||
"notifier": {
|
||||
"apikey": "",
|
||||
},
|
||||
"remote": {
|
||||
"accesstoken": "",
|
||||
"allowedOrigins": "/var/run/unraid-notifications.sock, /var/run/unraid-php.sock, /var/run/unraid-cli.sock, https://connect.myunraid.net, https://connect-staging.myunraid.net, https://dev-my.myunraid.net:4000",
|
||||
"apikey": "",
|
||||
"avatar": "",
|
||||
"dynamicRemoteAccessType": "DISABLED",
|
||||
"email": "",
|
||||
"idtoken": "",
|
||||
"localApiKey": "",
|
||||
"refreshtoken": "",
|
||||
"regWizTime": "",
|
||||
"upnpEnabled": "yes",
|
||||
"username": "",
|
||||
"wanaccess": "",
|
||||
"wanport": "",
|
||||
},
|
||||
"upc": {
|
||||
"apikey": "",
|
||||
},
|
||||
}
|
||||
`);
|
||||
});
|
||||
@@ -0,0 +1,69 @@
|
||||
import { test, expect } from 'vitest';
|
||||
import { parse } from 'ini';
|
||||
import { safelySerializeObjectToIni } from '@app/core/utils/files/safe-ini-serializer';
|
||||
import { Serializer } from 'multi-ini';
|
||||
|
||||
test('MultiIni breaks when serializing an object with a boolean inside', async () => {
|
||||
const objectToSerialize = {
|
||||
root: {
|
||||
anonMode: false,
|
||||
},
|
||||
};
|
||||
const serializer = new Serializer({ keep_quotes: false });
|
||||
expect(serializer.serialize(objectToSerialize)).toMatchInlineSnapshot(`
|
||||
"[root]
|
||||
anonMode=false
|
||||
"
|
||||
`);
|
||||
});
|
||||
|
||||
test('MultiIni can safely serialize an object with a boolean inside', async () => {
|
||||
const objectToSerialize = {
|
||||
root: {
|
||||
anonMode: false,
|
||||
},
|
||||
};
|
||||
expect(safelySerializeObjectToIni(objectToSerialize)).toMatchInlineSnapshot(`
|
||||
"[root]
|
||||
anonMode="false"
|
||||
"
|
||||
`);
|
||||
const result = safelySerializeObjectToIni(objectToSerialize);
|
||||
expect(parse(result)).toMatchInlineSnapshot(`
|
||||
{
|
||||
"root": {
|
||||
"anonMode": false,
|
||||
},
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
test.skip('Can serialize top-level fields', async () => {
|
||||
const objectToSerialize = {
|
||||
id: 'an-id',
|
||||
message: 'hello-world',
|
||||
number: 1,
|
||||
float: 1.1,
|
||||
flag: true,
|
||||
flag2: false,
|
||||
item: undefined,
|
||||
missing: null,
|
||||
empty: {},
|
||||
};
|
||||
|
||||
const expected = `
|
||||
"id=an-id
|
||||
message=hello-world
|
||||
number=1
|
||||
float=1.1
|
||||
flag="true"
|
||||
flag2="false"
|
||||
[empty]
|
||||
"
|
||||
`
|
||||
.split('\n')
|
||||
.map((line) => line.trim())
|
||||
.join('\n');
|
||||
|
||||
expect(safelySerializeObjectToIni(objectToSerialize)).toMatchInlineSnapshot(expected);
|
||||
});
|
||||
114
api/src/__test__/core/utils/misc/parse-config.test.ts
Normal file
114
api/src/__test__/core/utils/misc/parse-config.test.ts
Normal file
@@ -0,0 +1,114 @@
|
||||
import { test, expect } from 'vitest';
|
||||
import { parseConfig } from '@app/core/utils/misc/parse-config';
|
||||
import { Parser as MultiIniParser } from 'multi-ini';
|
||||
import { readFile, writeFile } from 'fs/promises';
|
||||
import { parse } from 'ini';
|
||||
import { safelySerializeObjectToIni } from '@app/core/utils/files/safe-ini-serializer';
|
||||
|
||||
const iniTestData = `["root"]
|
||||
idx="0"
|
||||
name="root"
|
||||
desc="Console and webGui login account"
|
||||
passwd="yes"
|
||||
["xo"]
|
||||
idx="1"
|
||||
name="xo"
|
||||
desc=""
|
||||
passwd="yes"
|
||||
["test_user"]
|
||||
idx="2"
|
||||
name="test_user"
|
||||
desc=""
|
||||
passwd="no"`;
|
||||
|
||||
test('it loads a config from a passed in ini file successfully', () => {
|
||||
const res = parseConfig<any>({
|
||||
file: iniTestData,
|
||||
type: 'ini',
|
||||
});
|
||||
expect(res).toMatchInlineSnapshot(`
|
||||
{
|
||||
"root": {
|
||||
"desc": "Console and webGui login account",
|
||||
"idx": "0",
|
||||
"name": "root",
|
||||
"passwd": "yes",
|
||||
},
|
||||
"testUser": {
|
||||
"desc": "",
|
||||
"idx": "2",
|
||||
"name": "test_user",
|
||||
"passwd": "no",
|
||||
},
|
||||
"xo": {
|
||||
"desc": "",
|
||||
"idx": "1",
|
||||
"name": "xo",
|
||||
"passwd": "yes",
|
||||
},
|
||||
}
|
||||
`);
|
||||
expect(res?.root.desc).toEqual('Console and webGui login account');
|
||||
});
|
||||
|
||||
test('it loads a config from disk properly', () => {
|
||||
const path = './dev/states/var.ini';
|
||||
const res = parseConfig<any>({ filePath: path, type: 'ini' });
|
||||
expect(res.DOMAIN_SHORT).toEqual(undefined);
|
||||
expect(res.domainShort).toEqual('');
|
||||
expect(res.shareCount).toEqual('0');
|
||||
});
|
||||
|
||||
test('Confirm Multi-Ini Parser Still Broken', () => {
|
||||
const parser = new MultiIniParser();
|
||||
const res = parser.parse(iniTestData);
|
||||
expect(res).toMatchInlineSnapshot('{}');
|
||||
});
|
||||
|
||||
test('Combine Ini and Multi-Ini to read and then write a file with quotes', async () => {
|
||||
const parsedFile = parse(iniTestData);
|
||||
expect(parsedFile).toMatchInlineSnapshot(`
|
||||
{
|
||||
"root": {
|
||||
"desc": "Console and webGui login account",
|
||||
"idx": "0",
|
||||
"name": "root",
|
||||
"passwd": "yes",
|
||||
},
|
||||
"test_user": {
|
||||
"desc": "",
|
||||
"idx": "2",
|
||||
"name": "test_user",
|
||||
"passwd": "no",
|
||||
},
|
||||
"xo": {
|
||||
"desc": "",
|
||||
"idx": "1",
|
||||
"name": "xo",
|
||||
"passwd": "yes",
|
||||
},
|
||||
}
|
||||
`);
|
||||
|
||||
const ini = safelySerializeObjectToIni(parsedFile);
|
||||
await writeFile('/tmp/test.ini', ini);
|
||||
const file = await readFile('/tmp/test.ini', 'utf-8');
|
||||
expect(file).toMatchInlineSnapshot(`
|
||||
"[root]
|
||||
idx="0"
|
||||
name="root"
|
||||
desc="Console and webGui login account"
|
||||
passwd="yes"
|
||||
[xo]
|
||||
idx="1"
|
||||
name="xo"
|
||||
desc=""
|
||||
passwd="yes"
|
||||
[test_user]
|
||||
idx="2"
|
||||
name="test_user"
|
||||
desc=""
|
||||
passwd="no"
|
||||
"
|
||||
`);
|
||||
});
|
||||
@@ -0,0 +1,10 @@
|
||||
import 'reflect-metadata';
|
||||
import { checkMothershipAuthentication } from "@app/graphql/resolvers/query/cloud/check-mothership-authentication";
|
||||
import { expect, test } from "vitest";
|
||||
import packageJson from '@app/../package.json'
|
||||
|
||||
test('It fails to authenticate with mothership with no credentials', async () => {
|
||||
await expect(checkMothershipAuthentication('BAD', 'BAD')).rejects.toThrowErrorMatchingInlineSnapshot(`[Error: Failed to connect to https://mothership.unraid.net/ws with a "426" HTTP error.]`);
|
||||
expect(packageJson.version).not.toBeNull();
|
||||
await expect(checkMothershipAuthentication(packageJson.version, 'BAD_API_KEY')).rejects.toThrowErrorMatchingInlineSnapshot(`[Error: Invalid credentials]`);
|
||||
}, 15_000)
|
||||
206
api/src/__test__/graphql/resolvers/subscription/network.test.ts
Normal file
206
api/src/__test__/graphql/resolvers/subscription/network.test.ts
Normal file
@@ -0,0 +1,206 @@
|
||||
import { expect, test } from 'vitest';
|
||||
import { type Nginx } from '../../../../core/types/states/nginx';
|
||||
import { getUrlForField, getUrlForServer, getServerIps, type NginxUrlFields } from '@app/graphql/resolvers/subscription/network';
|
||||
import { store } from '@app/store';
|
||||
import { loadStateFiles } from '@app/store/modules/emhttp';
|
||||
import { loadConfigFile } from '@app/store/modules/config';
|
||||
|
||||
test.each([
|
||||
[{ httpPort: 80, httpsPort: 443, url: 'my-default-url.com' }],
|
||||
[{ httpPort: 123, httpsPort: 443, url: 'my-default-url.com' }],
|
||||
[{ httpPort: 80, httpsPort: 12_345, url: 'my-default-url.com' }],
|
||||
[{ httpPort: 212, httpsPort: 3_233, url: 'my-default-url.com' }],
|
||||
[{ httpPort: 80, httpsPort: 443, url: 'https://BROKEN_URL' }],
|
||||
|
||||
])('getUrlForField', ({ httpPort, httpsPort, url }) => {
|
||||
const responseInsecure = getUrlForField({
|
||||
port: httpPort,
|
||||
url,
|
||||
});
|
||||
|
||||
const responseSecure = getUrlForField({
|
||||
portSsl: httpsPort,
|
||||
url,
|
||||
});
|
||||
if (httpPort === 80) {
|
||||
expect(responseInsecure.port).toBe('');
|
||||
} else {
|
||||
expect(responseInsecure.port).toBe(httpPort.toString());
|
||||
}
|
||||
|
||||
if (httpsPort === 443) {
|
||||
expect(responseSecure.port).toBe('');
|
||||
} else {
|
||||
expect(responseSecure.port).toBe(httpsPort.toString());
|
||||
}
|
||||
});
|
||||
|
||||
test('getUrlForServer - field exists, ssl disabled', () => {
|
||||
const result = getUrlForServer({ nginx: { lanIp: '192.168.1.1', sslEnabled: false, httpPort: 123, httpsPort: 445 } as const as Nginx,
|
||||
field: 'lanIp',
|
||||
});
|
||||
expect(result).toMatchInlineSnapshot('"http://192.168.1.1:123/"');
|
||||
});
|
||||
|
||||
test('getUrlForServer - field exists, ssl yes', () => {
|
||||
const result = getUrlForServer({
|
||||
nginx: { lanIp: '192.168.1.1', sslEnabled: true, sslMode: 'yes', httpPort: 123, httpsPort: 445 } as const as Nginx,
|
||||
field: 'lanIp',
|
||||
});
|
||||
expect(result).toMatchInlineSnapshot('"https://192.168.1.1:445/"');
|
||||
});
|
||||
|
||||
test('getUrlForServer - field exists, ssl yes, port empty', () => {
|
||||
const result = getUrlForServer(
|
||||
{ nginx: { lanIp: '192.168.1.1', sslEnabled: true, sslMode: 'yes', httpPort: 80, httpsPort: 443 } as const as Nginx,
|
||||
field: 'lanIp',
|
||||
});
|
||||
expect(result).toMatchInlineSnapshot('"https://192.168.1.1/"');
|
||||
});
|
||||
|
||||
test('getUrlForServer - field exists, ssl auto', async () => {
|
||||
const getResult = async () => getUrlForServer({
|
||||
nginx: { lanIp: '192.168.1.1', sslEnabled: true, sslMode: 'auto', httpPort: 123, httpsPort: 445 } as const as Nginx,
|
||||
field: 'lanIp',
|
||||
});
|
||||
await expect(getResult).rejects.toThrowErrorMatchingInlineSnapshot(`[Error: Cannot get IP Based URL for field: "lanIp" SSL mode auto]`);
|
||||
});
|
||||
|
||||
test('getUrlForServer - field does not exist, ssl disabled', async () => {
|
||||
const getResult = async () => getUrlForServer(
|
||||
{
|
||||
nginx: { lanIp: '192.168.1.1', sslEnabled: false, sslMode: 'no' } as const as Nginx,
|
||||
ports: {
|
||||
port: ':123', portSsl: ':445', defaultUrl: new URL('https://my-default-url.unraid.net'),
|
||||
},
|
||||
// @ts-expect-error Field doesn't exist
|
||||
field: 'idontexist',
|
||||
});
|
||||
await expect(getResult).rejects.toThrowErrorMatchingInlineSnapshot(`[Error: IP URL Resolver: Could not resolve any access URL for field: "idontexist", is FQDN?: false]`);
|
||||
});
|
||||
|
||||
test('getUrlForServer - FQDN - field exists, port non-empty', () => {
|
||||
const result = getUrlForServer({
|
||||
nginx: { lanFqdn: 'my-fqdn.unraid.net', httpsPort: 445 } as const as Nginx,
|
||||
field: 'lanFqdn',
|
||||
});
|
||||
expect(result).toMatchInlineSnapshot('"https://my-fqdn.unraid.net:445/"');
|
||||
});
|
||||
|
||||
test('getUrlForServer - FQDN - field exists, port empty', () => {
|
||||
const result = getUrlForServer({ nginx: { lanFqdn: 'my-fqdn.unraid.net', httpPort: 80, httpsPort: 443 } as const as Nginx,
|
||||
field: 'lanFqdn',
|
||||
});
|
||||
expect(result).toMatchInlineSnapshot('"https://my-fqdn.unraid.net/"');
|
||||
});
|
||||
|
||||
test.each([
|
||||
[{ nginx: { lanFqdn: 'my-fqdn.unraid.net', sslEnabled: false, sslMode: 'no', httpPort: 80, httpsPort: 443 } as const as Nginx, field: 'lanFqdn' as NginxUrlFields }],
|
||||
[{ nginx: { wanFqdn: 'my-fqdn.unraid.net', sslEnabled: true, sslMode: 'yes', httpPort: 80, httpsPort: 443 } as const as Nginx, field: 'wanFqdn' as NginxUrlFields }],
|
||||
[{ nginx: { wanFqdn6: 'my-fqdn.unraid.net', sslEnabled: true, sslMode: 'auto', httpPort: 80, httpsPort: 443 } as const as Nginx, field: 'wanFqdn6' as NginxUrlFields }],
|
||||
|
||||
])('getUrlForServer - FQDN', ({ nginx, field }) => {
|
||||
const result = getUrlForServer({ nginx, field });
|
||||
expect(result.toString()).toBe('https://my-fqdn.unraid.net/');
|
||||
});
|
||||
|
||||
test('getUrlForServer - field does not exist, ssl disabled', async () => {
|
||||
const getResult = async () => getUrlForServer({ nginx:
|
||||
{ lanFqdn: 'my-fqdn.unraid.net' } as const as Nginx,
|
||||
ports: { portSsl: '', port: '', defaultUrl: new URL('https://my-default-url.unraid.net') },
|
||||
// @ts-expect-error Field doesn't exist
|
||||
field: 'idontexist' });
|
||||
await expect(getResult).rejects.toThrowErrorMatchingInlineSnapshot(`[Error: IP URL Resolver: Could not resolve any access URL for field: "idontexist", is FQDN?: false]`);
|
||||
});
|
||||
|
||||
test('integration test, loading nginx ini and generating all URLs', async () => {
|
||||
await store.dispatch(loadStateFiles());
|
||||
await store.dispatch(loadConfigFile());
|
||||
|
||||
const urls = getServerIps();
|
||||
expect(urls.urls).toMatchInlineSnapshot(`
|
||||
[
|
||||
{
|
||||
"ipv4": "https://tower.local:4443/",
|
||||
"ipv6": "https://tower.local:4443/",
|
||||
"name": "Default",
|
||||
"type": "DEFAULT",
|
||||
},
|
||||
{
|
||||
"ipv4": "https://192.168.1.150:4443/",
|
||||
"name": "LAN IPv4",
|
||||
"type": "LAN",
|
||||
},
|
||||
{
|
||||
"ipv4": "https://tower:4443/",
|
||||
"name": "LAN Name",
|
||||
"type": "MDNS",
|
||||
},
|
||||
{
|
||||
"ipv4": "https://tower.local:4443/",
|
||||
"name": "LAN MDNS",
|
||||
"type": "MDNS",
|
||||
},
|
||||
{
|
||||
"ipv4": "https://192-168-1-150.thisisfourtyrandomcharacters012345678900.myunraid.net:4443/",
|
||||
"name": "FQDN LAN",
|
||||
"type": "LAN",
|
||||
},
|
||||
{
|
||||
"ipv4": "https://85-121-123-122.thisisfourtyrandomcharacters012345678900.myunraid.net:8443/",
|
||||
"name": "FQDN WAN",
|
||||
"type": "WAN",
|
||||
},
|
||||
{
|
||||
"ipv4": "https://10-252-0-1.hash.myunraid.net:4443/",
|
||||
"name": "FQDN WG 0",
|
||||
"type": "WIREGUARD",
|
||||
},
|
||||
{
|
||||
"ipv4": "https://10-252-1-1.hash.myunraid.net:4443/",
|
||||
"name": "FQDN WG 1",
|
||||
"type": "WIREGUARD",
|
||||
},
|
||||
{
|
||||
"ipv4": "https://10-253-3-1.hash.myunraid.net:4443/",
|
||||
"name": "FQDN WG 2",
|
||||
"type": "WIREGUARD",
|
||||
},
|
||||
{
|
||||
"ipv4": "https://10-253-4-1.hash.myunraid.net:4443/",
|
||||
"name": "FQDN WG 3",
|
||||
"type": "WIREGUARD",
|
||||
},
|
||||
{
|
||||
"ipv4": "https://10-253-5-1.hash.myunraid.net:4443/",
|
||||
"name": "FQDN WG 4",
|
||||
"type": "WIREGUARD",
|
||||
},
|
||||
{
|
||||
"ipv4": "https://10-100-0-1.hash.myunraid.net:4443/",
|
||||
"name": "FQDN TAILSCALE 0",
|
||||
"type": "WIREGUARD",
|
||||
},
|
||||
{
|
||||
"ipv4": "https://10-100-0-2.hash.myunraid.net:4443/",
|
||||
"name": "FQDN TAILSCALE 1",
|
||||
"type": "WIREGUARD",
|
||||
},
|
||||
{
|
||||
"ipv4": "https://10-123-1-2.hash.myunraid.net:4443/",
|
||||
"name": "FQDN CUSTOM 0",
|
||||
"type": "WIREGUARD",
|
||||
},
|
||||
{
|
||||
"ipv4": "https://221-123-121-112.hash.myunraid.net:4443/",
|
||||
"name": "FQDN CUSTOM 1",
|
||||
"type": "WIREGUARD",
|
||||
},
|
||||
]
|
||||
`);
|
||||
expect(urls.errors).toMatchInlineSnapshot(`
|
||||
[
|
||||
[Error: IP URL Resolver: Could not resolve any access URL for field: "lanIp6", is FQDN?: false],
|
||||
]
|
||||
`);
|
||||
});
|
||||
48
api/src/__test__/mothership/index.test.ts
Normal file
48
api/src/__test__/mothership/index.test.ts
Normal file
@@ -0,0 +1,48 @@
|
||||
import { beforeEach, expect, test, vi } from 'vitest';
|
||||
|
||||
// Preloading imports for faster tests
|
||||
import '@app/mothership/utils/convert-to-fuzzy-time';
|
||||
|
||||
vi.mock('fs', () => ({
|
||||
default: {
|
||||
readFileSync: vi.fn().mockReturnValue('my-file'),
|
||||
writeFileSync: vi.fn(),
|
||||
existsSync: vi.fn(),
|
||||
},
|
||||
readFileSync: vi.fn().mockReturnValue('my-file'),
|
||||
existsSync: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock('@graphql-tools/schema', () => ({
|
||||
makeExecutableSchema: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock('@app/core/log', () => ({
|
||||
default: { relayLogger: { trace: vi.fn() } },
|
||||
relayLogger: { trace: vi.fn() },
|
||||
logger: { trace: vi.fn() },
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
vi.resetModules();
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
const generateTestCases = () => {
|
||||
const cases: Array<{ min: number; max: number }> = [];
|
||||
for (let i = 0; i < 15; i += 1) {
|
||||
const min = Math.round(Math.random() * 100);
|
||||
const max = min + (Math.round(Math.random() * 20));
|
||||
cases.push({ min, max });
|
||||
}
|
||||
|
||||
return cases;
|
||||
};
|
||||
|
||||
test.each(generateTestCases())('Successfully converts to fuzzy time %o', async ({ min, max }) => {
|
||||
const { convertToFuzzyTime } = await import('@app/mothership/utils/convert-to-fuzzy-time');
|
||||
|
||||
const res = convertToFuzzyTime(min, max);
|
||||
expect(res).toBeGreaterThanOrEqual(min);
|
||||
expect(res).toBeLessThanOrEqual(max);
|
||||
});
|
||||
6
api/src/__test__/setup/child-process-easy-to-kill.js
Executable file
6
api/src/__test__/setup/child-process-easy-to-kill.js
Executable file
@@ -0,0 +1,6 @@
|
||||
/* eslint-disable */
|
||||
process.title = 'unraid-api';
|
||||
|
||||
setInterval(() => {
|
||||
console.log('I NEED TO DIE');
|
||||
}, 5_000);
|
||||
10
api/src/__test__/setup/child-process-hard-to-kill.js
Normal file
10
api/src/__test__/setup/child-process-hard-to-kill.js
Normal file
@@ -0,0 +1,10 @@
|
||||
/* eslint-disable */
|
||||
process.title = 'unraid-api';
|
||||
setInterval(() => {
|
||||
console.log('I NEED TO DIE (but i am very hard to kill)');
|
||||
}, 5_000);
|
||||
|
||||
process.on('SIGTERM', () => {
|
||||
// Do nothing
|
||||
console.log('you cant kill me haha');
|
||||
});
|
||||
6
api/src/__test__/setup/env-setup.ts
Normal file
6
api/src/__test__/setup/env-setup.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import { config } from 'dotenv';
|
||||
config({
|
||||
path: './.env.test',
|
||||
debug: false,
|
||||
encoding: 'utf-8',
|
||||
})
|
||||
@@ -6,7 +6,7 @@ exports[`loads notifications properly 1`] = `
|
||||
"description": "Canceled",
|
||||
"id": "/app/dev/notifications/unread/Unraid_Parity_check_1683971161.notify",
|
||||
"importance": "WARNING",
|
||||
"link": undefined,
|
||||
"link": "/",
|
||||
"subject": "Notice [UNRAID] - Parity check finished (0 errors)",
|
||||
"timestamp": "2023-05-13T09:46:01.000Z",
|
||||
"title": "Unraid Parity check",
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { test, expect } from 'vitest';
|
||||
import { expect, test } from 'vitest';
|
||||
|
||||
import { store } from '@app/store';
|
||||
|
||||
test('Before init returns default values for all fields', async () => {
|
||||
@@ -13,16 +14,12 @@ test('Before init returns default values for all fields', async () => {
|
||||
"minigraph": "PRE_INIT",
|
||||
"upnpStatus": "",
|
||||
},
|
||||
"local": {
|
||||
"2Fa": "",
|
||||
"showT2Fa": "",
|
||||
},
|
||||
"local": {},
|
||||
"nodeEnv": "test",
|
||||
"notifier": {
|
||||
"apikey": "",
|
||||
},
|
||||
"remote": {
|
||||
"2Fa": "",
|
||||
"accesstoken": "",
|
||||
"allowedOrigins": "",
|
||||
"apikey": "",
|
||||
@@ -30,6 +27,7 @@ test('Before init returns default values for all fields', async () => {
|
||||
"dynamicRemoteAccessType": "DISABLED",
|
||||
"email": "",
|
||||
"idtoken": "",
|
||||
"localApiKey": "",
|
||||
"refreshtoken": "",
|
||||
"regWizTime": "",
|
||||
"upnpEnabled": "",
|
||||
@@ -56,23 +54,19 @@ test('After init returns values from cfg file for all fields', async () => {
|
||||
expect(state).toMatchObject(
|
||||
expect.objectContaining({
|
||||
api: {
|
||||
extraOrigins: '',
|
||||
extraOrigins: expect.stringMatching('https://google.com,https://test.com'),
|
||||
version: expect.any(String),
|
||||
},
|
||||
connectionStatus: {
|
||||
minigraph: 'PRE_INIT',
|
||||
upnpStatus: '',
|
||||
},
|
||||
local: {
|
||||
'2Fa': '',
|
||||
showT2Fa: '',
|
||||
},
|
||||
local: {},
|
||||
nodeEnv: 'test',
|
||||
notifier: {
|
||||
apikey: 'unnotify_30994bfaccf839c65bae75f7fa12dd5ee16e69389f754c3b98ed7d5',
|
||||
},
|
||||
remote: {
|
||||
'2Fa': '',
|
||||
accesstoken: '',
|
||||
allowedOrigins: '',
|
||||
apikey: '_______________________BIG_API_KEY_HERE_________________________',
|
||||
@@ -80,6 +74,7 @@ test('After init returns values from cfg file for all fields', async () => {
|
||||
dynamicRemoteAccessType: 'DISABLED',
|
||||
email: 'test@example.com',
|
||||
idtoken: '',
|
||||
localApiKey: '_______________________LOCAL_API_KEY_HERE_________________________',
|
||||
refreshtoken: '',
|
||||
regWizTime: '1611175408732_0951-1653-3509-FBA155FA23C0',
|
||||
upnpEnabled: 'no',
|
||||
@@ -96,9 +91,7 @@ test('After init returns values from cfg file for all fields', async () => {
|
||||
});
|
||||
|
||||
test('updateUserConfig merges in changes to current state', async () => {
|
||||
const { loadConfigFile, updateUserConfig } = await import(
|
||||
'@app/store/modules/config'
|
||||
);
|
||||
const { loadConfigFile, updateUserConfig } = await import('@app/store/modules/config');
|
||||
|
||||
// Load cfg into store
|
||||
await store.dispatch(loadConfigFile());
|
||||
@@ -114,23 +107,19 @@ test('updateUserConfig merges in changes to current state', async () => {
|
||||
expect(state).toMatchObject(
|
||||
expect.objectContaining({
|
||||
api: {
|
||||
extraOrigins: '',
|
||||
extraOrigins: expect.stringMatching('https://google.com,https://test.com'),
|
||||
version: expect.any(String),
|
||||
},
|
||||
connectionStatus: {
|
||||
minigraph: 'PRE_INIT',
|
||||
upnpStatus: '',
|
||||
},
|
||||
local: {
|
||||
'2Fa': '',
|
||||
showT2Fa: '',
|
||||
},
|
||||
local: {},
|
||||
nodeEnv: 'test',
|
||||
notifier: {
|
||||
apikey: 'unnotify_30994bfaccf839c65bae75f7fa12dd5ee16e69389f754c3b98ed7d5',
|
||||
},
|
||||
remote: {
|
||||
'2Fa': '',
|
||||
accesstoken: '',
|
||||
allowedOrigins: '',
|
||||
apikey: '_______________________BIG_API_KEY_HERE_________________________',
|
||||
@@ -138,6 +127,7 @@ test('updateUserConfig merges in changes to current state', async () => {
|
||||
dynamicRemoteAccessType: 'DISABLED',
|
||||
email: 'test@example.com',
|
||||
idtoken: '',
|
||||
localApiKey: '_______________________LOCAL_API_KEY_HERE_________________________',
|
||||
refreshtoken: '',
|
||||
regWizTime: '1611175408732_0951-1653-3509-FBA155FA23C0',
|
||||
upnpEnabled: 'no',
|
||||
|
||||
@@ -104,10 +104,76 @@ test('After init returns values from cfg file for all fields', async () => {
|
||||
"certificateName": "*.thisisfourtyrandomcharacters012345678900.myunraid.net",
|
||||
"certificatePath": "/boot/config/ssl/certs/certificate_bundle.pem",
|
||||
"defaultUrl": "https://Tower.local:4443",
|
||||
"fqdnUrls": [
|
||||
{
|
||||
"fqdn": "192-168-1-150.thisisfourtyrandomcharacters012345678900.myunraid.net",
|
||||
"id": null,
|
||||
"interface": "LAN",
|
||||
"isIpv6": false,
|
||||
},
|
||||
{
|
||||
"fqdn": "85-121-123-122.thisisfourtyrandomcharacters012345678900.myunraid.net",
|
||||
"id": null,
|
||||
"interface": "WAN",
|
||||
"isIpv6": false,
|
||||
},
|
||||
{
|
||||
"fqdn": "10-252-0-1.hash.myunraid.net",
|
||||
"id": 0,
|
||||
"interface": "WG",
|
||||
"isIpv6": false,
|
||||
},
|
||||
{
|
||||
"fqdn": "10-252-1-1.hash.myunraid.net",
|
||||
"id": 1,
|
||||
"interface": "WG",
|
||||
"isIpv6": false,
|
||||
},
|
||||
{
|
||||
"fqdn": "10-253-3-1.hash.myunraid.net",
|
||||
"id": 2,
|
||||
"interface": "WG",
|
||||
"isIpv6": false,
|
||||
},
|
||||
{
|
||||
"fqdn": "10-253-4-1.hash.myunraid.net",
|
||||
"id": 3,
|
||||
"interface": "WG",
|
||||
"isIpv6": false,
|
||||
},
|
||||
{
|
||||
"fqdn": "10-253-5-1.hash.myunraid.net",
|
||||
"id": 4,
|
||||
"interface": "WG",
|
||||
"isIpv6": false,
|
||||
},
|
||||
{
|
||||
"fqdn": "10-100-0-1.hash.myunraid.net",
|
||||
"id": 0,
|
||||
"interface": "TAILSCALE",
|
||||
"isIpv6": false,
|
||||
},
|
||||
{
|
||||
"fqdn": "10-100-0-2.hash.myunraid.net",
|
||||
"id": 1,
|
||||
"interface": "TAILSCALE",
|
||||
"isIpv6": false,
|
||||
},
|
||||
{
|
||||
"fqdn": "10-123-1-2.hash.myunraid.net",
|
||||
"id": 0,
|
||||
"interface": "CUSTOM",
|
||||
"isIpv6": false,
|
||||
},
|
||||
{
|
||||
"fqdn": "221-123-121-112.hash.myunraid.net",
|
||||
"id": 1,
|
||||
"interface": "CUSTOM",
|
||||
"isIpv6": true,
|
||||
},
|
||||
],
|
||||
"httpPort": 8080,
|
||||
"httpsPort": 4443,
|
||||
"lanFqdn": "192-168-1-150.thisisfourtyrandomcharacters012345678900.myunraid.net",
|
||||
"lanFqdn6": "",
|
||||
"lanIp": "192.168.1.150",
|
||||
"lanIp6": "",
|
||||
"lanMdns": "Tower.local",
|
||||
@@ -115,31 +181,7 @@ test('After init returns values from cfg file for all fields', async () => {
|
||||
"sslEnabled": true,
|
||||
"sslMode": "yes",
|
||||
"wanAccessEnabled": false,
|
||||
"wanFqdn": "85-121-123-122.thisisfourtyrandomcharacters012345678900.myunraid.net",
|
||||
"wanFqdn6": "",
|
||||
"wanIp": "",
|
||||
"wgFqdns": [
|
||||
{
|
||||
"fqdn": "10-252-0-1.hash.myunraid.net",
|
||||
"id": 0,
|
||||
},
|
||||
{
|
||||
"fqdn": "10-252-1-1.hash.myunraid.net",
|
||||
"id": 1,
|
||||
},
|
||||
{
|
||||
"fqdn": "10-253-3-1.hash.myunraid.net",
|
||||
"id": 3,
|
||||
},
|
||||
{
|
||||
"fqdn": "10-253-4-1.hash.myunraid.net",
|
||||
"id": 4,
|
||||
},
|
||||
{
|
||||
"fqdn": "10-253-5-1.hash.myunraid.net",
|
||||
"id": 55,
|
||||
},
|
||||
],
|
||||
}
|
||||
`);
|
||||
expect(disks).toMatchInlineSnapshot(`
|
||||
@@ -984,6 +1026,7 @@ test('After init returns values from cfg file for all fields', async () => {
|
||||
"porttelnet": 23,
|
||||
"queueDepth": "auto",
|
||||
"regCheck": "Valid",
|
||||
"regExp": "",
|
||||
"regFile": "/app/dev/Unraid.net/Pro.key",
|
||||
"regGen": "0",
|
||||
"regGuid": "13FE-4200-C300-58C372A52B19",
|
||||
|
||||
@@ -2,8 +2,8 @@ import { expect, test } from 'vitest';
|
||||
import { store } from '@app/store';
|
||||
|
||||
test('Returns paths', async () => {
|
||||
const { paths } = store.getState();
|
||||
expect(Object.keys(paths)).toMatchInlineSnapshot(`
|
||||
const { paths } = store.getState();
|
||||
expect(Object.keys(paths)).toMatchInlineSnapshot(`
|
||||
[
|
||||
"core",
|
||||
"unraid-api-base",
|
||||
@@ -20,10 +20,13 @@ test('Returns paths', async () => {
|
||||
"myservers-config",
|
||||
"myservers-config-states",
|
||||
"myservers-env",
|
||||
"myservers-keepalive",
|
||||
"keyfile-base",
|
||||
"machine-id",
|
||||
"log-base",
|
||||
"var-run",
|
||||
"auth-sessions",
|
||||
"auth-keys",
|
||||
]
|
||||
`);
|
||||
});
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
||||
|
||||
exports[`Returns parsed state file 1`] = `
|
||||
{
|
||||
"certificateName": "*.thisisfourtyrandomcharacters012345678900.myunraid.net",
|
||||
"certificatePath": "/boot/config/ssl/certs/certificate_bundle.pem",
|
||||
"defaultUrl": "https://Tower.local:4443",
|
||||
"fqdnUrls": [
|
||||
{
|
||||
"fqdn": "192-168-1-150.thisisfourtyrandomcharacters012345678900.myunraid.net",
|
||||
"id": null,
|
||||
"interface": "LAN",
|
||||
"isIpv6": false,
|
||||
},
|
||||
{
|
||||
"fqdn": "85-121-123-122.thisisfourtyrandomcharacters012345678900.myunraid.net",
|
||||
"id": null,
|
||||
"interface": "WAN",
|
||||
"isIpv6": false,
|
||||
},
|
||||
{
|
||||
"fqdn": "10-252-0-1.hash.myunraid.net",
|
||||
"id": 0,
|
||||
"interface": "WG",
|
||||
"isIpv6": false,
|
||||
},
|
||||
{
|
||||
"fqdn": "10-252-1-1.hash.myunraid.net",
|
||||
"id": 1,
|
||||
"interface": "WG",
|
||||
"isIpv6": false,
|
||||
},
|
||||
{
|
||||
"fqdn": "10-253-3-1.hash.myunraid.net",
|
||||
"id": 2,
|
||||
"interface": "WG",
|
||||
"isIpv6": false,
|
||||
},
|
||||
{
|
||||
"fqdn": "10-253-4-1.hash.myunraid.net",
|
||||
"id": 3,
|
||||
"interface": "WG",
|
||||
"isIpv6": false,
|
||||
},
|
||||
{
|
||||
"fqdn": "10-253-5-1.hash.myunraid.net",
|
||||
"id": 4,
|
||||
"interface": "WG",
|
||||
"isIpv6": false,
|
||||
},
|
||||
{
|
||||
"fqdn": "10-100-0-1.hash.myunraid.net",
|
||||
"id": 0,
|
||||
"interface": "TAILSCALE",
|
||||
"isIpv6": false,
|
||||
},
|
||||
{
|
||||
"fqdn": "10-100-0-2.hash.myunraid.net",
|
||||
"id": 1,
|
||||
"interface": "TAILSCALE",
|
||||
"isIpv6": false,
|
||||
},
|
||||
{
|
||||
"fqdn": "10-123-1-2.hash.myunraid.net",
|
||||
"id": 0,
|
||||
"interface": "CUSTOM",
|
||||
"isIpv6": false,
|
||||
},
|
||||
{
|
||||
"fqdn": "221-123-121-112.hash.myunraid.net",
|
||||
"id": 1,
|
||||
"interface": "CUSTOM",
|
||||
"isIpv6": true,
|
||||
},
|
||||
],
|
||||
"httpPort": 8080,
|
||||
"httpsPort": 4443,
|
||||
"lanIp": "192.168.1.150",
|
||||
"lanIp6": "",
|
||||
"lanMdns": "Tower.local",
|
||||
"lanName": "Tower",
|
||||
"sslEnabled": true,
|
||||
"sslMode": "yes",
|
||||
"wanAccessEnabled": false,
|
||||
"wanIp": "",
|
||||
}
|
||||
`;
|
||||
16
api/src/__test__/store/state-parsers/nginx.test.ts
Normal file
16
api/src/__test__/store/state-parsers/nginx.test.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import { join } from 'path';
|
||||
import { expect, test } from 'vitest';
|
||||
import { store } from '@app/store';
|
||||
import type { NginxIni } from '@app/store/state-parsers/nginx';
|
||||
|
||||
test('Returns parsed state file', async () => {
|
||||
const { parse } = await import('@app/store/state-parsers/nginx');
|
||||
const { parseConfig } = await import('@app/core/utils/misc/parse-config');
|
||||
const { paths } = store.getState();
|
||||
const filePath = join(paths.states, 'nginx.ini');
|
||||
const stateFile = parseConfig<NginxIni>({
|
||||
filePath,
|
||||
type: 'ini',
|
||||
});
|
||||
expect(parse(stateFile)).toMatchSnapshot();
|
||||
});
|
||||
@@ -103,6 +103,7 @@ test('Returns parsed state file', async () => {
|
||||
"porttelnet": 23,
|
||||
"queueDepth": "auto",
|
||||
"regCheck": "Valid",
|
||||
"regExp": "",
|
||||
"regFile": "/app/dev/Unraid.net/Pro.key",
|
||||
"regGen": "0",
|
||||
"regGuid": "13FE-4200-C300-58C372A52B19",
|
||||
|
||||
71
api/src/__test__/utils.test.ts
Normal file
71
api/src/__test__/utils.test.ts
Normal file
@@ -0,0 +1,71 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
import { formatDatetime } from '@app/utils';
|
||||
|
||||
describe('formatDatetime', () => {
|
||||
const testDate = new Date('2024-02-14T12:34:56');
|
||||
|
||||
it('formats with default system time format and omits timezone', () => {
|
||||
const result = formatDatetime(testDate);
|
||||
// Default format is %c with timezone omitted
|
||||
expect(result).toMatch('Wed 14 Feb 2024 12:34:56 PM');
|
||||
});
|
||||
|
||||
it('includes timezone when omitTimezone is false', () => {
|
||||
const result = formatDatetime(testDate, { omitTimezone: false });
|
||||
// Should include timezone at the end
|
||||
expect(result).toMatch(/^Wed 14 Feb 2024 12:34:56 PM .+$/);
|
||||
});
|
||||
|
||||
it('formats with custom date and time formats', () => {
|
||||
const result = formatDatetime(testDate, {
|
||||
dateFormat: '%Y-%m-%d',
|
||||
timeFormat: '%H:%M',
|
||||
});
|
||||
expect(result).toBe('2024-02-14 12:34');
|
||||
});
|
||||
|
||||
it('formats with custom date format and default time format', () => {
|
||||
const result = formatDatetime(testDate, {
|
||||
dateFormat: '%d/%m/%Y',
|
||||
});
|
||||
expect(result).toBe('14/02/2024 12:34 PM');
|
||||
});
|
||||
|
||||
describe('Unraid-style date formats', () => {
|
||||
const dateFormats = [
|
||||
'%A, %Y %B %e', // Day, YYYY Month D
|
||||
'%A, %e %B %Y', // Day, D Month YYYY
|
||||
'%A, %B %e, %Y', // Day, Month D, YYYY
|
||||
'%A, %m/%d/%Y', // Day, MM/DD/YYYY
|
||||
'%A, %d-%m-%Y', // Day, DD-MM-YYYY
|
||||
'%A, %d.%m.%Y', // Day, DD.MM.YYYY
|
||||
'%A, %Y-%m-%d', // Day, YYYY-MM-DD
|
||||
];
|
||||
|
||||
const timeFormats = [
|
||||
'%I:%M %p', // 12 hours
|
||||
'%R', // 24 hours
|
||||
];
|
||||
|
||||
it.each(dateFormats)('formats date with %s', (dateFormat) => {
|
||||
const result = formatDatetime(testDate, { dateFormat });
|
||||
expect(result).toMatch(/^Wednesday.*2024.*12:34 PM$/);
|
||||
});
|
||||
|
||||
it.each(timeFormats)('formats time with %s', (timeFormat) => {
|
||||
// specify a non-system-time date format for this test
|
||||
const result = formatDatetime(testDate, { timeFormat, dateFormat: dateFormats[1] });
|
||||
const expectedTime = timeFormat === '%R' ? '12:34' : '12:34 PM';
|
||||
expect(result).toContain(expectedTime);
|
||||
});
|
||||
|
||||
it.each(dateFormats.flatMap((d) => timeFormats.map((t) => [d, t])))(
|
||||
'formats with date format %s and time format %s',
|
||||
(dateFormat, timeFormat) => {
|
||||
const result = formatDatetime(testDate, { dateFormat, timeFormat });
|
||||
expect(result).toMatch(/^Wednesday.*2024.*(?:12:34 PM|12:34)$/);
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -1,11 +1,16 @@
|
||||
import 'wtfnode';
|
||||
#!/usr/bin/env node
|
||||
import '@app/dotenv';
|
||||
|
||||
import { am } from 'am';
|
||||
import { main } from '@app/cli/index';
|
||||
import { internalLogger } from '@app/core/log';
|
||||
|
||||
void am(main, (error: unknown) => {
|
||||
internalLogger.fatal((error as Error).message);
|
||||
// Ensure process is exited
|
||||
process.exit(1);
|
||||
});
|
||||
try {
|
||||
await main();
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
internalLogger.error({
|
||||
message: 'Failed to start unraid-api',
|
||||
error,
|
||||
});
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
@@ -1,33 +1,24 @@
|
||||
import ipRegex from 'ip-regex';
|
||||
import readLine from 'readline';
|
||||
import { setEnv } from '@app/cli/set-env';
|
||||
import { getUnraidApiPid } from '@app/cli/get-unraid-api-pid';
|
||||
import { cliLogger } from '@app/core/log';
|
||||
import { getters, store } from '@app/store';
|
||||
import { stdout } from 'process';
|
||||
import { loadConfigFile } from '@app/store/modules/config';
|
||||
import { getApiApolloClient } from '../../graphql/client/api/get-api-client';
|
||||
import {
|
||||
getCloudDocument,
|
||||
getServersDocument,
|
||||
type getServersQuery,
|
||||
type getCloudQuery,
|
||||
} from '../../graphql/generated/api/operations';
|
||||
import {
|
||||
type ApolloQueryResult,
|
||||
type ApolloClient,
|
||||
type NormalizedCacheObject,
|
||||
} from '@apollo/client/core/core.cjs';
|
||||
import { MinigraphStatus } from '@app/graphql/generated/api/types';
|
||||
import readLine from 'readline';
|
||||
|
||||
import { ApolloClient, ApolloQueryResult, NormalizedCacheObject } from '@apollo/client/core/index.js';
|
||||
import ipRegex from 'ip-regex';
|
||||
|
||||
import { setEnv } from '@app/cli/set-env';
|
||||
import { cliLogger } from '@app/core/log';
|
||||
import { isUnraidApiRunning } from '@app/core/utils/pm2/unraid-api-running';
|
||||
import { API_VERSION } from '@app/environment';
|
||||
import { MinigraphStatus } from '@app/graphql/generated/api/types';
|
||||
import { getters, store } from '@app/store';
|
||||
import { loadConfigFile } from '@app/store/modules/config';
|
||||
import { loadStateFiles } from '@app/store/modules/emhttp';
|
||||
|
||||
type CloudQueryResult = NonNullable<
|
||||
ApolloQueryResult<getCloudQuery>['data']['cloud']
|
||||
>;
|
||||
type ServersQueryResultServer = NonNullable<
|
||||
ApolloQueryResult<getServersQuery>['data']['servers']
|
||||
>[0];
|
||||
import type { getCloudQuery, getServersQuery } from '../../graphql/generated/api/operations';
|
||||
import { getApiApolloClient } from '../../graphql/client/api/get-api-client';
|
||||
import { getCloudDocument, getServersDocument } from '../../graphql/generated/api/operations';
|
||||
|
||||
type CloudQueryResult = NonNullable<ApolloQueryResult<getCloudQuery>['data']['cloud']>;
|
||||
type ServersQueryResultServer = NonNullable<ApolloQueryResult<getServersQuery>['data']['servers']>[0];
|
||||
|
||||
type Verbosity = '' | '-v' | '-vv';
|
||||
|
||||
@@ -75,15 +66,10 @@ export const getCloudData = async (
|
||||
const cloud = await client.query({ query: getCloudDocument });
|
||||
return cloud.data.cloud ?? null;
|
||||
} catch (error: unknown) {
|
||||
cliLogger.addContext(
|
||||
'error-stack',
|
||||
error instanceof Error ? error.stack : error
|
||||
);
|
||||
cliLogger.trace(
|
||||
'Failed fetching cloud from local graphql with "%s"',
|
||||
error instanceof Error ? error.message : 'Unknown Error'
|
||||
);
|
||||
cliLogger.removeContext('error-stack');
|
||||
|
||||
return null;
|
||||
}
|
||||
@@ -122,12 +108,10 @@ export const getServersData = async ({
|
||||
);
|
||||
return foundServers;
|
||||
} catch (error: unknown) {
|
||||
cliLogger.addContext('error', error);
|
||||
cliLogger.trace(
|
||||
'Failed fetching servers from local graphql with "%s"',
|
||||
error instanceof Error ? error.message : 'Unknown Error'
|
||||
);
|
||||
cliLogger.removeContext('error');
|
||||
return {
|
||||
online: [],
|
||||
offline: [],
|
||||
@@ -139,8 +123,7 @@ export const getServersData = async ({
|
||||
const hashUrlRegex = () => /(.*)([a-z0-9]{40})(.*)/g;
|
||||
|
||||
export const anonymiseOrigins = (origins?: string[]): string[] => {
|
||||
const originsWithoutSocks =
|
||||
origins?.filter((url) => !url.endsWith('.sock')) ?? [];
|
||||
const originsWithoutSocks = origins?.filter((url) => !url.endsWith('.sock')) ?? [];
|
||||
return originsWithoutSocks
|
||||
.map((origin) =>
|
||||
origin
|
||||
@@ -149,29 +132,17 @@ export const anonymiseOrigins = (origins?: string[]): string[] => {
|
||||
// Replace ipv4 address using . separator with "IPV4ADDRESS"
|
||||
.replace(ipRegex(), 'IPV4ADDRESS')
|
||||
// Replace ipv4 address using - separator with "IPV4ADDRESS"
|
||||
.replace(
|
||||
new RegExp(ipRegex().toString().replace('\\.', '-')),
|
||||
'/IPV4ADDRESS'
|
||||
)
|
||||
.replace(new RegExp(ipRegex().toString().replace('\\.', '-')), '/IPV4ADDRESS')
|
||||
// Report WAN port
|
||||
.replace(
|
||||
`:${getters.config().remote.wanport || 443}`,
|
||||
':WANPORT'
|
||||
)
|
||||
.replace(`:${getters.config().remote.wanport || 443}`, ':WANPORT')
|
||||
)
|
||||
.filter(Boolean);
|
||||
};
|
||||
|
||||
const getAllowedOrigins = (
|
||||
cloud: CloudQueryResult | null,
|
||||
v: Verbosity
|
||||
): string[] | null => {
|
||||
const getAllowedOrigins = (cloud: CloudQueryResult | null, v: Verbosity): string[] | null => {
|
||||
switch (v) {
|
||||
case '-vv':
|
||||
return (
|
||||
cloud?.allowedOrigins.filter((url) => !url.endsWith('.sock')) ??
|
||||
[]
|
||||
);
|
||||
return cloud?.allowedOrigins.filter((url) => !url.endsWith('.sock')) ?? [];
|
||||
case '-v':
|
||||
return anonymiseOrigins(cloud?.allowedOrigins ?? []);
|
||||
default:
|
||||
@@ -179,37 +150,23 @@ const getAllowedOrigins = (
|
||||
}
|
||||
};
|
||||
|
||||
const getReadableCloudDetails = (
|
||||
reportObject: ReportObject,
|
||||
v: Verbosity
|
||||
): string => {
|
||||
const error = reportObject.cloud.error
|
||||
? `\n ERROR [${reportObject.cloud.error}]`
|
||||
: '';
|
||||
const status = reportObject.cloud.status
|
||||
? reportObject.cloud.status
|
||||
: 'disconnected';
|
||||
const ip =
|
||||
reportObject.cloud.ip && v !== ''
|
||||
? `\n IP: [${reportObject.cloud.ip}]`
|
||||
: '';
|
||||
const getReadableCloudDetails = (reportObject: ReportObject, v: Verbosity): string => {
|
||||
const error = reportObject.cloud.error ? `\n ERROR [${reportObject.cloud.error}]` : '';
|
||||
const status = reportObject.cloud.status ? reportObject.cloud.status : 'disconnected';
|
||||
const ip = reportObject.cloud.ip && v !== '' ? `\n IP: [${reportObject.cloud.ip}]` : '';
|
||||
return `
|
||||
STATUS: [${status}] ${ip} ${error}`;
|
||||
};
|
||||
|
||||
const getReadableMinigraphDetails = (reportObject: ReportObject): string => {
|
||||
const statusLine = `STATUS: [${reportObject.minigraph.status}]`;
|
||||
const errorLine = reportObject.minigraph.error
|
||||
? ` ERROR: [${reportObject.minigraph.error}]`
|
||||
: null;
|
||||
const errorLine = reportObject.minigraph.error ? ` ERROR: [${reportObject.minigraph.error}]` : null;
|
||||
const timeoutLine = reportObject.minigraph.timeout
|
||||
? ` TIMEOUT: [${(reportObject.minigraph.timeout || 1) / 1_000}s]`
|
||||
: null; // 1 in case of divide by zero
|
||||
|
||||
return `
|
||||
${statusLine}${errorLine ? `\n${errorLine}` : ''}${
|
||||
timeoutLine ? `\n${timeoutLine}` : ''
|
||||
}`;
|
||||
${statusLine}${errorLine ? `\n${errorLine}` : ''}${timeoutLine ? `\n${timeoutLine}` : ''}`;
|
||||
};
|
||||
|
||||
// Convert server to string output
|
||||
@@ -222,10 +179,7 @@ const serverToString = (v: Verbosity) => (server: ServersQueryResultServer) =>
|
||||
: ''
|
||||
}`;
|
||||
|
||||
const getReadableServerDetails = (
|
||||
reportObject: ReportObject,
|
||||
v: Verbosity
|
||||
): string => {
|
||||
const getReadableServerDetails = (reportObject: ReportObject, v: Verbosity): string => {
|
||||
if (!reportObject.servers) {
|
||||
return '';
|
||||
}
|
||||
@@ -243,9 +197,7 @@ const getReadableServerDetails = (
|
||||
return `
|
||||
SERVERS:
|
||||
ONLINE: ${reportObject.servers.online.map(serverToString(v)).join(',')}
|
||||
OFFLINE: ${reportObject.servers.offline
|
||||
.map(serverToString(v))
|
||||
.join(',')}${invalid}`;
|
||||
OFFLINE: ${reportObject.servers.offline.map(serverToString(v)).join(',')}${invalid}`;
|
||||
};
|
||||
|
||||
const getReadableAllowedOrigins = (reportObject: ReportObject): string => {
|
||||
@@ -270,7 +222,6 @@ const getVerbosity = (argv: string[]): Verbosity => {
|
||||
return '';
|
||||
};
|
||||
|
||||
// eslint-disable-next-line complexity
|
||||
export const report = async (...argv: string[]) => {
|
||||
// Check if the user has raw output enabled
|
||||
const rawOutput = argv.includes('--raw');
|
||||
@@ -302,7 +253,7 @@ export const report = async (...argv: string[]) => {
|
||||
const v = getVerbosity(argv);
|
||||
|
||||
// Find all processes called "unraid-api" which aren't this process
|
||||
const unraidApiPid = await getUnraidApiPid();
|
||||
const unraidApiRunning = await isUnraidApiRunning();
|
||||
|
||||
// Load my servers config file into store
|
||||
await store.dispatch(loadConfigFile());
|
||||
@@ -311,7 +262,7 @@ export const report = async (...argv: string[]) => {
|
||||
const { config, emhttp } = store.getState();
|
||||
if (!config.upc.apikey) throw new Error('Missing UPC API key');
|
||||
|
||||
const client = getApiApolloClient({ upcApiKey: config.upc.apikey });
|
||||
const client = getApiApolloClient({ localApiKey: config.remote.localApiKey || '' });
|
||||
// Fetch the cloud endpoint
|
||||
const cloud = await getCloudData(client);
|
||||
|
||||
@@ -328,43 +279,37 @@ export const report = async (...argv: string[]) => {
|
||||
const reportObject: ReportObject = {
|
||||
os: {
|
||||
serverName: emhttp.var.name,
|
||||
version: emhttp.var.version
|
||||
version: emhttp.var.version,
|
||||
},
|
||||
api: {
|
||||
version: API_VERSION,
|
||||
status: unraidApiPid ? 'running' : 'stopped',
|
||||
environment:
|
||||
process.env.ENVIRONMENT ??
|
||||
'THIS_WILL_BE_REPLACED_WHEN_BUILT',
|
||||
status: unraidApiRunning ? 'running' : 'stopped',
|
||||
environment: process.env.ENVIRONMENT ?? 'THIS_WILL_BE_REPLACED_WHEN_BUILT',
|
||||
nodeVersion: process.version,
|
||||
},
|
||||
apiKey: isApiKeyValid ? 'valid' : cloud?.apiKey.error ?? 'invalid',
|
||||
apiKey: isApiKeyValid ? 'valid' : (cloud?.apiKey.error ?? 'invalid'),
|
||||
...(servers ? { servers } : {}),
|
||||
myServers: {
|
||||
status: config?.remote?.username
|
||||
? 'authenticated'
|
||||
: 'signed out',
|
||||
status: config?.remote?.username ? 'authenticated' : 'signed out',
|
||||
...(config?.remote?.username
|
||||
? { myServersUsername: config?.remote?.username?.includes('@') ? 'REDACTED' : config?.remote.username }
|
||||
? {
|
||||
myServersUsername: config?.remote?.username?.includes('@')
|
||||
? 'REDACTED'
|
||||
: config?.remote.username,
|
||||
}
|
||||
: {}),
|
||||
},
|
||||
minigraph: {
|
||||
status: cloud?.minigraphql.status ?? MinigraphStatus.PRE_INIT,
|
||||
timeout: cloud?.minigraphql.timeout ?? null,
|
||||
error:
|
||||
cloud?.minigraphql.error ?? !cloud?.minigraphql.status
|
||||
? 'API Disconnected'
|
||||
: null,
|
||||
(cloud?.minigraphql.error ?? !cloud?.minigraphql.status) ? 'API Disconnected' : null,
|
||||
},
|
||||
cloud: {
|
||||
status: cloud?.cloud.status ?? 'error',
|
||||
...(cloud?.cloud.error ? { error: cloud.cloud.error } : {}),
|
||||
...(cloud?.cloud.status === 'ok'
|
||||
? { ip: cloud.cloud.ip ?? 'NO_IP' }
|
||||
: {}),
|
||||
...(getAllowedOrigins(cloud, v)
|
||||
? { allowedOrigins: getAllowedOrigins(cloud, v) }
|
||||
: {}),
|
||||
...(cloud?.cloud.status === 'ok' ? { ip: cloud.cloud.ip ?? 'NO_IP' } : {}),
|
||||
...(getAllowedOrigins(cloud, v) ? { allowedOrigins: getAllowedOrigins(cloud, v) } : {}),
|
||||
},
|
||||
};
|
||||
|
||||
@@ -377,8 +322,8 @@ export const report = async (...argv: string[]) => {
|
||||
|
||||
if (jsonReport) {
|
||||
stdout.write(JSON.stringify(reportObject) + '\n');
|
||||
stdoutLogger.close();
|
||||
return reportObject;
|
||||
stdoutLogger.close();
|
||||
return reportObject;
|
||||
} else {
|
||||
// Generate the actual report
|
||||
const report = `
|
||||
@@ -395,9 +340,7 @@ MY_SERVERS: ${reportObject.myServers.status}${
|
||||
: ''
|
||||
}
|
||||
CLOUD: ${getReadableCloudDetails(reportObject, v)}
|
||||
MINI-GRAPH: ${getReadableMinigraphDetails(
|
||||
reportObject
|
||||
)}${getReadableServerDetails(
|
||||
MINI-GRAPH: ${getReadableMinigraphDetails(reportObject)}${getReadableServerDetails(
|
||||
reportObject,
|
||||
v
|
||||
)}${getReadableAllowedOrigins(reportObject)}
|
||||
@@ -412,9 +355,7 @@ MINI-GRAPH: ${getReadableMinigraphDetails(
|
||||
console.log({ error });
|
||||
if (error instanceof Error) {
|
||||
cliLogger.trace(error);
|
||||
stdoutLogger.write(
|
||||
`\nFailed generating report with "${error.message}"\n`
|
||||
);
|
||||
stdoutLogger.write(`\nFailed generating report with "${error.message}"\n`);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
10
api/src/cli/commands/restart.ts
Normal file
10
api/src/cli/commands/restart.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import { start } from '@app/cli/commands/start';
|
||||
import { stop } from '@app/cli/commands/stop';
|
||||
|
||||
/**
|
||||
* Stop a running API process and then start it again.
|
||||
*/
|
||||
export const restart = async () => {
|
||||
await stop();
|
||||
await start();
|
||||
};
|
||||
@@ -1,87 +1,16 @@
|
||||
import { spawn } from 'child_process';
|
||||
import { addExitCallback } from 'catch-exit';
|
||||
import { PM2_PATH } from '@app/consts';
|
||||
import { cliLogger } from '@app/core/log';
|
||||
import { mainOptions } from '@app/cli/options';
|
||||
import { logToSyslog } from '@app/cli/log-to-syslog';
|
||||
import { getters } from '@app/store';
|
||||
import { getAllUnraidApiPids } from '@app/cli/get-unraid-api-pid';
|
||||
import { API_VERSION } from '@app/environment';
|
||||
|
||||
import { execSync } from 'child_process';
|
||||
import { join } from 'node:path';
|
||||
/**
|
||||
* Start a new API process.
|
||||
*/
|
||||
export const start = async () => {
|
||||
// Set process title
|
||||
process.title = 'unraid-api';
|
||||
const runningProcesses = await getAllUnraidApiPids();
|
||||
if (runningProcesses.length > 0) {
|
||||
cliLogger.info('unraid-api is Already Running!');
|
||||
cliLogger.info('Run "unraid-api restart" to stop all running processes and restart');
|
||||
process.exit(1);
|
||||
}
|
||||
cliLogger.info('Starting unraid-api with command', `${PM2_PATH} start ${join(import.meta.dirname, 'ecosystem.config.json')} --update-env`);
|
||||
|
||||
// Start API
|
||||
cliLogger.info('Starting unraid-api@v%s', API_VERSION);
|
||||
|
||||
// If we're in debug mode or we're NOT
|
||||
// in debug but ARE in the child process
|
||||
if (mainOptions.debug || process.env._DAEMONIZE_PROCESS) {
|
||||
// Log when the API exits
|
||||
addExitCallback((signal, exitCode, error) => {
|
||||
if (exitCode === 0 || exitCode === 130 || signal === 'SIGTERM') {
|
||||
logToSyslog('👋 Farewell. UNRAID API shutting down!');
|
||||
return;
|
||||
}
|
||||
|
||||
// Log when the API crashes
|
||||
if (signal === 'uncaughtException' && error) {
|
||||
logToSyslog(`⚠️ Caught exception: ${error.message}`);
|
||||
}
|
||||
|
||||
// Log when we crash
|
||||
if (exitCode) {
|
||||
logToSyslog(`⚠️ UNRAID API crashed with exit code ${exitCode}`);
|
||||
return;
|
||||
}
|
||||
|
||||
logToSyslog('🛑 UNRAID API crashed without an exit code?');
|
||||
});
|
||||
|
||||
logToSyslog('✔️ UNRAID API started successfully!');
|
||||
}
|
||||
|
||||
// Load bundled index file
|
||||
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
||||
require('../../index');
|
||||
|
||||
if (!mainOptions.debug) {
|
||||
if ('_DAEMONIZE_PROCESS' in process.env) {
|
||||
// In the child, clean up the tracking environment variable
|
||||
delete process.env._DAEMONIZE_PROCESS;
|
||||
} else {
|
||||
cliLogger.debug('Daemonizing process. %s %o', process.execPath, process.argv);
|
||||
|
||||
// Spawn child
|
||||
// First arg is path (inside PKG), second arg is restart, stop, etc, rest is args to main argument
|
||||
const [path, , ...rest] = process.argv.slice(1);
|
||||
const replacedCommand = [path, 'start', ...rest];
|
||||
const child = spawn(process.execPath, replacedCommand, {
|
||||
// In the parent set the tracking environment variable
|
||||
env: Object.assign(process.env, { _DAEMONIZE_PROCESS: '1' }),
|
||||
// The process MUST have it's cwd set to the
|
||||
// path where it resides within the Nexe VFS
|
||||
cwd: getters.paths()['unraid-api-base'],
|
||||
stdio: 'ignore',
|
||||
detached: true,
|
||||
});
|
||||
|
||||
// Convert process into daemon
|
||||
child.unref();
|
||||
|
||||
cliLogger.debug('Daemonized successfully!');
|
||||
|
||||
// Exit cleanly
|
||||
process.exit(0);
|
||||
}
|
||||
}
|
||||
execSync(`${PM2_PATH} start ${join(import.meta.dirname, '../../', 'ecosystem.config.json')} --update-env`, {
|
||||
env: process.env,
|
||||
stdio: 'inherit',
|
||||
cwd: process.cwd()
|
||||
});
|
||||
};
|
||||
|
||||
@@ -1,19 +1,7 @@
|
||||
import prettyMs from 'pretty-ms';
|
||||
import pidUsage from 'pidusage';
|
||||
import { cliLogger } from '@app/core/log';
|
||||
import { getUnraidApiPid } from '@app/cli/get-unraid-api-pid';
|
||||
import { setEnv } from '@app/cli/set-env';
|
||||
import { PM2_PATH } from '@app/consts';
|
||||
import { execSync } from 'child_process';
|
||||
|
||||
export const status = async () => {
|
||||
setEnv('LOG_TYPE', 'raw');
|
||||
|
||||
// Find all processes called "unraid-api" which aren't this process
|
||||
const unraidApiPid = await getUnraidApiPid();
|
||||
if (!unraidApiPid) {
|
||||
cliLogger.info('Found no running processes.');
|
||||
return;
|
||||
}
|
||||
|
||||
const stats = await pidUsage(unraidApiPid);
|
||||
cliLogger.info(`API has been running for ${prettyMs(stats.elapsed)} and is in "${process.env.ENVIRONMENT ?? 'ERR: Unknown Environment'}" mode!`);
|
||||
execSync(`${PM2_PATH} status unraid-api`, { stdio: 'inherit' });
|
||||
process.exit(0);
|
||||
};
|
||||
|
||||
@@ -1,47 +1,6 @@
|
||||
import { cliLogger } from '@app/core/log';
|
||||
import { getAllUnraidApiPids } from '@app/cli/get-unraid-api-pid';
|
||||
import { setEnv } from '@app/cli/set-env';
|
||||
import { sleep } from '@app/core/utils/misc/sleep';
|
||||
import pRetry from 'p-retry';
|
||||
|
||||
/**
|
||||
* Stop a running API process.
|
||||
*/
|
||||
import { PM2_PATH } from '@app/consts';
|
||||
import { execSync } from 'child_process';
|
||||
|
||||
export const stop = async () => {
|
||||
setEnv('LOG_TYPE', 'raw');
|
||||
|
||||
try {
|
||||
await pRetry(async (attempts) => {
|
||||
const runningApis = await getAllUnraidApiPids();
|
||||
|
||||
if (runningApis.length > 0) {
|
||||
cliLogger.info('Stopping %s unraid-api process(es)...', runningApis.length);
|
||||
runningApis.forEach(pid => process.kill(pid, 'SIGTERM'));
|
||||
|
||||
const newPids = await getAllUnraidApiPids();
|
||||
|
||||
if (newPids.length > 0) {
|
||||
throw new Error('Not all processes have exited yet');
|
||||
}
|
||||
} else if (attempts < 1) {
|
||||
cliLogger.info('Found no running processes.');
|
||||
}
|
||||
|
||||
return true;
|
||||
}, {
|
||||
retries: 2,
|
||||
minTimeout: 2_000,
|
||||
factor: 1,
|
||||
});
|
||||
} catch (error: unknown) {
|
||||
cliLogger.info('Process did not exit cleanly, forcing shutdown', error);
|
||||
const processes = await getAllUnraidApiPids();
|
||||
for (const pid of processes) {
|
||||
process.kill(pid, 'SIGKILL');
|
||||
await sleep(100);
|
||||
}
|
||||
}
|
||||
|
||||
await sleep(500);
|
||||
execSync(`${PM2_PATH} stop unraid-api`, { stdio: 'inherit' });
|
||||
};
|
||||
|
||||
@@ -1,27 +1,17 @@
|
||||
import { copyFile, readFile, writeFile } from 'fs/promises';
|
||||
import { join } from 'path';
|
||||
import { cliLogger } from '@app/core/log';
|
||||
import { getUnraidApiPid } from '@app/cli/get-unraid-api-pid';
|
||||
import { setEnv } from '@app/cli/set-env';
|
||||
import { getters } from '@app/store';
|
||||
import { start } from '@app/cli/commands/start';
|
||||
import { stop } from '@app/cli/commands/stop';
|
||||
|
||||
export const switchEnv = async () => {
|
||||
setEnv('LOG_TYPE', 'raw');
|
||||
|
||||
const paths = getters.paths();
|
||||
const basePath = paths['unraid-api-base'];
|
||||
const envFlashFilePath = paths['myservers-env'];
|
||||
const envFile = await readFile(envFlashFilePath, 'utf-8').catch(() => '');
|
||||
|
||||
let shouldStartAfterRunning = false;
|
||||
if (await getUnraidApiPid()) {
|
||||
cliLogger.info('unraid-api is running, stopping...');
|
||||
// Stop Running Process
|
||||
await stop();
|
||||
shouldStartAfterRunning = true;
|
||||
}
|
||||
await stop();
|
||||
|
||||
cliLogger.debug(
|
||||
'Checking %s for current ENV, found %s',
|
||||
@@ -70,11 +60,5 @@ export const switchEnv = async () => {
|
||||
await copyFile(source, destination);
|
||||
|
||||
cliLogger.info('Now using %s', newEnv);
|
||||
if (shouldStartAfterRunning) {
|
||||
cliLogger.debug('Restarting unraid-api');
|
||||
// Start Process
|
||||
await start();
|
||||
} else {
|
||||
cliLogger.info('Run "unraid-api start" to start the API.');
|
||||
}
|
||||
await start();
|
||||
};
|
||||
|
||||
102
api/src/cli/index.ts
Normal file → Executable file
102
api/src/cli/index.ts
Normal file → Executable file
@@ -1,72 +1,56 @@
|
||||
import { parse } from 'ts-command-line-args';
|
||||
import { cliLogger } from '@app/core/log';
|
||||
import { type Flags, mainOptions, options, args } from '@app/cli/options';
|
||||
import { execSync } from 'child_process';
|
||||
|
||||
import type { Flags } from '@app/cli/options';
|
||||
import { args, mainOptions, options } from '@app/cli/options';
|
||||
import { setEnv } from '@app/cli/set-env';
|
||||
import { env } from '@app/dotenv';
|
||||
import { getters } from '@app/store';
|
||||
import { PM2_PATH } from '@app/consts';
|
||||
|
||||
const command = mainOptions.command as unknown as string;
|
||||
|
||||
export const main = async (...argv: string[]) => {
|
||||
cliLogger.addContext('envs', env);
|
||||
cliLogger.debug('Loading env file');
|
||||
cliLogger.removeContext('envs');
|
||||
if (mainOptions.debug) {
|
||||
const { cliLogger } = await import('@app/core/log');
|
||||
const { getters } = await import('@app/store');
|
||||
const ENVIRONMENT = await import('@app/environment');
|
||||
cliLogger.debug({ paths: getters.paths(), environment: ENVIRONMENT }, 'Starting CLI');
|
||||
}
|
||||
|
||||
// Set envs
|
||||
setEnv('LOG_TYPE', process.env.LOG_TYPE ?? (command === 'start' || mainOptions.debug ? 'pretty' : 'raw'));
|
||||
cliLogger.addContext('paths', getters.paths());
|
||||
cliLogger.debug('Starting CLI');
|
||||
cliLogger.removeContext('paths');
|
||||
setEnv('PORT', process.env.PORT ?? mainOptions.port ?? '9000');
|
||||
|
||||
setEnv('DEBUG', mainOptions.debug ?? false);
|
||||
setEnv('ENVIRONMENT', process.env.ENVIRONMENT ?? 'production');
|
||||
setEnv('PORT', process.env.PORT ?? mainOptions.port ?? '9000');
|
||||
setEnv('LOG_LEVEL', process.env.LOG_LEVEL ?? mainOptions['log-level'] ?? 'INFO');
|
||||
if (!process.env.LOG_TRANSPORT) {
|
||||
if (process.env.ENVIRONMENT === 'production' && !mainOptions.debug) {
|
||||
setEnv('LOG_TRANSPORT', 'file,errors');
|
||||
setEnv('LOG_LEVEL', 'DEBUG');
|
||||
} else if (!mainOptions.debug) {
|
||||
// Staging Environment, backgrounded plugin
|
||||
setEnv('LOG_TRANSPORT', 'file,errors');
|
||||
setEnv('LOG_LEVEL', 'TRACE');
|
||||
} else {
|
||||
cliLogger.debug('In Debug Mode - Log Level Defaulting to: stdout');
|
||||
}
|
||||
}
|
||||
if (!command) {
|
||||
// Run help command
|
||||
const { parse } = await import('ts-command-line-args');
|
||||
parse<Flags>(args, {
|
||||
...options,
|
||||
partial: true,
|
||||
stopAtFirstUnknown: true,
|
||||
argv: ['-h'],
|
||||
});
|
||||
}
|
||||
|
||||
if (!command) {
|
||||
// Run help command
|
||||
parse<Flags>(args, { ...options, partial: true, stopAtFirstUnknown: true, argv: ['-h'] });
|
||||
}
|
||||
// Only import the command we need when we use it
|
||||
const commands = {
|
||||
start: import('@app/cli/commands/start').then((pkg) => pkg.start),
|
||||
stop: import('@app/cli/commands/stop').then((pkg) => pkg.stop),
|
||||
restart: import('@app/cli/commands/restart').then((pkg) => pkg.restart),
|
||||
logs: async () => execSync(`${PM2_PATH} logs unraid-api --lines 200`, { stdio: 'inherit' }),
|
||||
'switch-env': import('@app/cli/commands/switch-env').then((pkg) => pkg.switchEnv),
|
||||
version: import('@app/cli/commands/version').then((pkg) => pkg.version),
|
||||
status: import('@app/cli/commands/status').then((pkg) => pkg.status),
|
||||
report: import('@app/cli/commands/report').then((pkg) => pkg.report),
|
||||
'validate-token': import('@app/cli/commands/validate-token').then((pkg) => pkg.validateToken),
|
||||
};
|
||||
|
||||
// Only import the command we need when we use it
|
||||
const commands = {
|
||||
start: import('@app/cli/commands/start').then(pkg => pkg.start),
|
||||
stop: import('@app/cli/commands/stop').then(pkg => pkg.stop),
|
||||
restart: import('@app/cli/commands/restart').then(pkg => pkg.restart),
|
||||
'switch-env': import('@app/cli/commands/switch-env').then(pkg => pkg.switchEnv),
|
||||
version: import('@app/cli/commands/version').then(pkg => pkg.version),
|
||||
status: import('@app/cli/commands/status').then(pkg => pkg.status),
|
||||
report: import('@app/cli/commands/report').then(pkg => pkg.report),
|
||||
'validate-token': import('@app/cli/commands/validate-token').then(pkg => pkg.validateToken),
|
||||
};
|
||||
// Unknown command
|
||||
if (!Object.keys(commands).includes(command)) {
|
||||
throw new Error(`Invalid command "${command}"`);
|
||||
}
|
||||
|
||||
// Unknown command
|
||||
if (!Object.keys(commands).includes(command)) {
|
||||
throw new Error(`Invalid command "${command}"`);
|
||||
}
|
||||
// Resolve the command import
|
||||
const commandMethod = await commands[command];
|
||||
|
||||
// Resolve the command import
|
||||
const commandMethod = await commands[command];
|
||||
// Run the command
|
||||
await commandMethod(...argv);
|
||||
|
||||
// Run the command
|
||||
await commandMethod(...argv);
|
||||
|
||||
// Allow the process to exit
|
||||
// Don't exit when we start though
|
||||
if (!['start', 'restart'].includes(command)) {
|
||||
// Ensure process is exited
|
||||
process.exit(0);
|
||||
}
|
||||
process.exit(0);
|
||||
};
|
||||
|
||||
@@ -1,93 +1,112 @@
|
||||
import { getters, type RootState, store } from '@app/store';
|
||||
import { uniq } from 'lodash';
|
||||
import { getServerIps, getUrlForField } from '@app/graphql/resolvers/subscription/network';
|
||||
import uniq from 'lodash/uniq';
|
||||
import {
|
||||
getServerIps,
|
||||
getUrlForField,
|
||||
} from '@app/graphql/resolvers/subscription/network';
|
||||
import { FileLoadStatus } from '@app/store/types';
|
||||
import { logger } from '../core';
|
||||
import { ENVIRONMENT, INTROSPECTION } from '@app/environment';
|
||||
import { GRAPHQL_INTROSPECTION } from '@app/environment';
|
||||
|
||||
const getAllowedSocks = (): string[] => [
|
||||
// Notifier bridge
|
||||
'/var/run/unraid-notifications.sock',
|
||||
// Notifier bridge
|
||||
'/var/run/unraid-notifications.sock',
|
||||
|
||||
// Unraid PHP scripts
|
||||
'/var/run/unraid-php.sock',
|
||||
// Unraid PHP scripts
|
||||
'/var/run/unraid-php.sock',
|
||||
|
||||
// CLI
|
||||
'/var/run/unraid-cli.sock',
|
||||
// CLI
|
||||
'/var/run/unraid-cli.sock',
|
||||
];
|
||||
|
||||
const getLocalAccessUrlsForServer = (state: RootState = store.getState()): string[] => {
|
||||
const { emhttp } = state;
|
||||
if (emhttp.status !== FileLoadStatus.LOADED) {
|
||||
return [];
|
||||
}
|
||||
const getLocalAccessUrlsForServer = (
|
||||
state: RootState = store.getState()
|
||||
): string[] => {
|
||||
const { emhttp } = state;
|
||||
if (emhttp.status !== FileLoadStatus.LOADED) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const { nginx } = emhttp;
|
||||
try {
|
||||
return [
|
||||
getUrlForField({ url: 'localhost', port: nginx.httpPort }).toString(),
|
||||
getUrlForField({ url: 'localhost', portSsl: nginx.httpsPort }).toString(),
|
||||
];
|
||||
} catch (error: unknown) {
|
||||
logger.debug('Caught error in getLocalAccessUrlsForServer: \n%o', error);
|
||||
return [];
|
||||
}
|
||||
const { nginx } = emhttp;
|
||||
try {
|
||||
return [
|
||||
getUrlForField({
|
||||
url: 'localhost',
|
||||
port: nginx.httpPort,
|
||||
}).toString(),
|
||||
getUrlForField({
|
||||
url: 'localhost',
|
||||
portSsl: nginx.httpsPort,
|
||||
}).toString(),
|
||||
];
|
||||
} catch (error: unknown) {
|
||||
logger.debug(
|
||||
'Caught error in getLocalAccessUrlsForServer: \n%o',
|
||||
error
|
||||
);
|
||||
return [];
|
||||
}
|
||||
};
|
||||
|
||||
const getRemoteAccessUrlsForAllowedOrigins = (state: RootState = store.getState()): string[] => {
|
||||
const { urls } = getServerIps(state);
|
||||
const getRemoteAccessUrlsForAllowedOrigins = (
|
||||
state: RootState = store.getState()
|
||||
): string[] => {
|
||||
const { urls } = getServerIps(state);
|
||||
|
||||
if (urls) {
|
||||
return urls.reduce<string[]>((acc, curr) => {
|
||||
if (curr.ipv4 && curr.ipv6) {
|
||||
acc.push(curr.ipv4.toString());
|
||||
} else if (curr.ipv4) {
|
||||
acc.push(curr.ipv4.toString());
|
||||
} else if (curr.ipv6) {
|
||||
acc.push(curr.ipv6.toString());
|
||||
}
|
||||
if (urls) {
|
||||
return urls.reduce<string[]>((acc, curr) => {
|
||||
if (curr.ipv4 && curr.ipv6 || curr.ipv4) {
|
||||
acc.push(curr.ipv4.toString());
|
||||
} else if (curr.ipv6) {
|
||||
acc.push(curr.ipv6.toString());
|
||||
}
|
||||
|
||||
return acc;
|
||||
}, []);
|
||||
}
|
||||
return acc;
|
||||
}, []);
|
||||
}
|
||||
|
||||
return [];
|
||||
return [];
|
||||
};
|
||||
|
||||
const getExtraOrigins = (): string[] => {
|
||||
const { extraOrigins } = getters.config().api;
|
||||
if (extraOrigins) {
|
||||
return extraOrigins.split(', ').filter(origin => origin.startsWith('http://') || origin.startsWith('https://'));
|
||||
}
|
||||
export const getExtraOrigins = (): string[] => {
|
||||
const { extraOrigins } = getters.config().api;
|
||||
if (extraOrigins) {
|
||||
return extraOrigins
|
||||
.replaceAll(' ', '')
|
||||
.split(',')
|
||||
.filter(
|
||||
(origin) =>
|
||||
origin.startsWith('http://') ||
|
||||
origin.startsWith('https://')
|
||||
);
|
||||
}
|
||||
|
||||
return [];
|
||||
return [];
|
||||
};
|
||||
|
||||
const getConnectOrigins = () : string[] => {
|
||||
const connectMain = 'https://connect.myunraid.net';
|
||||
const connectStaging = 'https://staging.connect.myunraid.net';
|
||||
const connectDev = 'https://dev-my.myunraid.net:4000';
|
||||
const getConnectOrigins = (): string[] => {
|
||||
const connectMain = 'https://connect.myunraid.net';
|
||||
const connectStaging = 'https://connect-staging.myunraid.net';
|
||||
const connectDev = 'https://dev-my.myunraid.net:4000';
|
||||
|
||||
return [
|
||||
connectMain,
|
||||
connectStaging,
|
||||
connectDev
|
||||
]
|
||||
}
|
||||
return [connectMain, connectStaging, connectDev];
|
||||
};
|
||||
|
||||
const getApolloSandbox = (): string[] => {
|
||||
if (INTROSPECTION || ENVIRONMENT === 'development') {
|
||||
return ['https://studio.apollographql.com'];
|
||||
}
|
||||
return [];
|
||||
}
|
||||
if (GRAPHQL_INTROSPECTION) {
|
||||
return ['https://studio.apollographql.com'];
|
||||
}
|
||||
return [];
|
||||
};
|
||||
|
||||
export const getAllowedOrigins = (state: RootState = store.getState()): string[] => uniq([
|
||||
...getAllowedSocks(),
|
||||
...getLocalAccessUrlsForServer(),
|
||||
...getRemoteAccessUrlsForAllowedOrigins(state),
|
||||
...getExtraOrigins(),
|
||||
...getConnectOrigins(),
|
||||
...getApolloSandbox()
|
||||
|
||||
]).map(url => url.endsWith('/') ? url.slice(0, -1) : url);
|
||||
export const getAllowedOrigins = (
|
||||
state: RootState = store.getState()
|
||||
): string[] =>
|
||||
uniq([
|
||||
...getAllowedSocks(),
|
||||
...getLocalAccessUrlsForServer(),
|
||||
...getRemoteAccessUrlsForAllowedOrigins(state),
|
||||
...getExtraOrigins(),
|
||||
...getConnectOrigins(),
|
||||
...getApolloSandbox(),
|
||||
]).map((url) => (url.endsWith('/') ? url.slice(0, -1) : url));
|
||||
|
||||
@@ -1,126 +0,0 @@
|
||||
import { ConnectListAllDomainsFlags } from '@vmngr/libvirt';
|
||||
import { getHypervisor } from '@app/core/utils/vms/get-hypervisor';
|
||||
import display from '@app/graphql/resolvers/query/display';
|
||||
import { getUnraidVersion } from '@app/common/dashboard/get-unraid-version';
|
||||
import { getArray } from '@app/common/dashboard/get-array';
|
||||
import { bootTimestamp } from '@app/common/dashboard/boot-timestamp';
|
||||
import { dashboardLogger } from '@app/core/log';
|
||||
import { getters, store } from '@app/store';
|
||||
import { type DashboardServiceInput, type DashboardInput } from '@app/graphql/generated/client/graphql';
|
||||
import { API_VERSION } from '@app/environment';
|
||||
import { DynamicRemoteAccessType } from '@app/remoteAccess/types';
|
||||
import { DashboardInputSchema } from '@app/graphql/generated/client/validators';
|
||||
import { ZodError } from 'zod';
|
||||
|
||||
const getVmSummary = async (): Promise<DashboardInput['vms']> => {
|
||||
try {
|
||||
const hypervisor = await getHypervisor();
|
||||
if (!hypervisor) {
|
||||
return {
|
||||
installed: 0,
|
||||
started: 0,
|
||||
};
|
||||
}
|
||||
|
||||
const activeDomains = await hypervisor.connectListAllDomains(ConnectListAllDomainsFlags.ACTIVE) as unknown[];
|
||||
const inactiveDomains = await hypervisor.connectListAllDomains(ConnectListAllDomainsFlags.INACTIVE) as unknown[];
|
||||
return {
|
||||
installed: activeDomains.length + inactiveDomains.length,
|
||||
started: activeDomains.length,
|
||||
};
|
||||
} catch {
|
||||
return {
|
||||
installed: 0,
|
||||
started: 0,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
const getDynamicRemoteAccessService = (): DashboardServiceInput | null => {
|
||||
const { config, dynamicRemoteAccess } = store.getState();
|
||||
const enabledStatus = config.remote.dynamicRemoteAccessType;
|
||||
|
||||
return {
|
||||
name: 'dynamic-remote-access',
|
||||
online: enabledStatus !== DynamicRemoteAccessType.DISABLED,
|
||||
version: dynamicRemoteAccess.runningType,
|
||||
uptime: {
|
||||
timestamp: bootTimestamp.toISOString(),
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
const services = (): DashboardInput['services'] => {
|
||||
const dynamicRemoteAccess = getDynamicRemoteAccessService();
|
||||
return [
|
||||
{
|
||||
name: 'unraid-api',
|
||||
online: true,
|
||||
uptime: {
|
||||
timestamp: bootTimestamp.toISOString(),
|
||||
},
|
||||
version: API_VERSION,
|
||||
},
|
||||
...(dynamicRemoteAccess ? [dynamicRemoteAccess] : []),
|
||||
];
|
||||
};
|
||||
|
||||
const getData = async (): Promise<DashboardInput> => {
|
||||
const emhttp = getters.emhttp();
|
||||
const docker = getters.docker();
|
||||
|
||||
return {
|
||||
vars: {
|
||||
regState: emhttp.var.regState,
|
||||
regTy: emhttp.var.regTy,
|
||||
flashGuid: emhttp.var.flashGuid,
|
||||
},
|
||||
apps: {
|
||||
installed: docker.installed ?? 0,
|
||||
started: docker.running ?? 0
|
||||
},
|
||||
versions: {
|
||||
unraid: await getUnraidVersion(),
|
||||
},
|
||||
os: {
|
||||
hostname: emhttp.var.name,
|
||||
uptime: bootTimestamp.toISOString()
|
||||
},
|
||||
vms: await getVmSummary(),
|
||||
array: getArray(),
|
||||
services: services(),
|
||||
display: await display(),
|
||||
config: emhttp.var.configValid ? { valid: true } : {
|
||||
valid: false,
|
||||
error: {
|
||||
error: 'UNKNOWN_ERROR',
|
||||
invalid: 'INVALID',
|
||||
nokeyserver: 'NO_KEY_SERVER',
|
||||
withdrawn: 'WITHDRAWN',
|
||||
}[emhttp.var.configState] ?? 'UNKNOWN_ERROR',
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export const generateData = async (): Promise<DashboardInput | null> => {
|
||||
const data = await getData();
|
||||
|
||||
try {
|
||||
// Validate generated data
|
||||
// @TODO: Fix this runtype to use generated types from the Zod validators (as seen in mothership Codegen)
|
||||
const result = DashboardInputSchema().parse(data)
|
||||
|
||||
return result
|
||||
|
||||
} catch (error: unknown) {
|
||||
// Log error for user
|
||||
if (error instanceof ZodError) {
|
||||
dashboardLogger.error('Failed validation with issues: ' , error.issues.map(issue => ({ message: issue.message, path: issue.path.join(',') })))
|
||||
} else {
|
||||
dashboardLogger.error('Failed validating dashboard object: ', error, data);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { PORT } from '@app/environment';
|
||||
import { type JSONWebKeySet } from 'jose';
|
||||
import { join } from 'path';
|
||||
|
||||
export const getInternalApiAddress = (isHttp = true, nginxPort = 80) => {
|
||||
const envPort = PORT;
|
||||
@@ -34,6 +35,7 @@ export const FIVE_MINUTES_MS = 5 * ONE_MINUTE;
|
||||
export const TEN_MINUTES_MS = 10 * ONE_MINUTE;
|
||||
export const THIRTY_MINUTES_MS = 30 * ONE_MINUTE;
|
||||
export const ONE_HOUR_MS = 60 * ONE_MINUTE;
|
||||
export const ONE_DAY_MS = ONE_HOUR_MS * 24;
|
||||
|
||||
// Seconds
|
||||
export const ONE_HOUR_SECS = 60 * 60;
|
||||
@@ -45,11 +47,6 @@ export const KEEP_ALIVE_INTERVAL_MS = THREE_MINUTES_MS; // This is set to 45 sec
|
||||
/**
|
||||
* Graphql link.
|
||||
*/
|
||||
export const MOTHERSHIP_GRAPHQL_LINK =
|
||||
process.env.MOTHERSHIP_GRAPHQL_LINK ??
|
||||
(process.env.ENVIRONMENT === 'staging'
|
||||
? 'https://staging.mothership.unraid.net/ws'
|
||||
: 'https://mothership.unraid.net/ws');
|
||||
|
||||
export const JWKS_LOCAL_PAYLOAD: JSONWebKeySet = {
|
||||
keys: [
|
||||
@@ -83,3 +80,5 @@ export const KEYSERVER_VALIDATION_ENDPOINT =
|
||||
|
||||
/** Set the max retries for the GraphQL Client */
|
||||
export const MAX_RETRIES_FOR_LINEAR_BACKOFF = 100;
|
||||
|
||||
export const PM2_PATH = join(import.meta.dirname, '../../', 'node_modules', '.bin', 'pm2');
|
||||
@@ -1,6 +0,0 @@
|
||||
import NanoBus from 'nanobus';
|
||||
|
||||
/**
|
||||
* Graphql event bus.
|
||||
*/
|
||||
export const bus = new NanoBus();
|
||||
@@ -1,122 +0,0 @@
|
||||
export interface Permission { resource: string, action: string, attributes: string }
|
||||
export interface Role {
|
||||
permissions: Array<Permission>
|
||||
extends?: string;
|
||||
}
|
||||
|
||||
export const admin: Role = {
|
||||
extends: 'user',
|
||||
permissions: [
|
||||
// @NOTE: Uncomment the first line to enable creation of api keys.
|
||||
// See the README.md for more information.
|
||||
// @WARNING: This is currently unsupported, please be careful.
|
||||
// { resource: 'apikey', action: 'create:any', attributes: '*' },
|
||||
{ resource: 'apikey', action: 'read:any', attributes: '*' },
|
||||
{ resource: 'array', action: 'read:any', attributes: '*' },
|
||||
{ resource: 'cpu', action: 'read:any', attributes: '*' },
|
||||
{
|
||||
resource: 'crash-reporting-enabled',
|
||||
action: 'read:any',
|
||||
attributes: '*',
|
||||
},
|
||||
{ resource: 'device', action: 'read:any', attributes: '*' },
|
||||
{ resource: 'device/unassigned', action: 'read:any', attributes: '*' },
|
||||
{ resource: 'disk', action: 'read:any', attributes: '*' },
|
||||
{ resource: 'disk/settings', action: 'read:any', attributes: '*' },
|
||||
{ resource: 'display', action: 'read:any', attributes: '*' },
|
||||
{ resource: 'docker/container', action: 'read:any', attributes: '*' },
|
||||
{ resource: 'docker/network', action: 'read:any', attributes: '*' },
|
||||
{ resource: 'flash', action: 'read:any', attributes: '*' },
|
||||
{ resource: 'info', action: 'read:any', attributes: '*' },
|
||||
{ resource: 'license-key', action: 'read:any', attributes: '*' },
|
||||
{ resource: 'machine-id', action: 'read:any', attributes: '*' },
|
||||
{ resource: 'memory', action: 'read:any', attributes: '*' },
|
||||
{ resource: 'notifications', action: 'read:any', attributes: '*' },
|
||||
{ resource: 'online', action: 'read:any', attributes: '*' },
|
||||
{ resource: 'os', action: 'read:any', attributes: '*' },
|
||||
{ resource: 'owner', action: 'read:any', attributes: '*' },
|
||||
{ resource: 'parity-history', action: 'read:any', attributes: '*' },
|
||||
{ resource: 'permission', action: 'read:any', attributes: '*' },
|
||||
{ resource: 'registration', action: 'read:any', attributes: '*' },
|
||||
{ resource: 'servers', action: 'read:any', attributes: '*' },
|
||||
{ resource: 'service', action: 'read:any', attributes: '*' },
|
||||
{ resource: 'service/emhttpd', action: 'read:any', attributes: '*' },
|
||||
{ resource: 'service/unraid-api', action: 'read:any', attributes: '*' },
|
||||
{ resource: 'services', action: 'read:any', attributes: '*' },
|
||||
{ resource: 'share', action: 'read:any', attributes: '*' },
|
||||
{ resource: 'software-versions', action: 'read:any', attributes: '*' },
|
||||
{ resource: 'unraid-version', action: 'read:any', attributes: '*' },
|
||||
{ resource: 'uptime', action: 'read:any', attributes: '*' },
|
||||
{ resource: 'user', action: 'read:any', attributes: '*' },
|
||||
{ resource: 'vars', action: 'read:any', attributes: '*' },
|
||||
{ resource: 'vms', action: 'read:any', attributes: '*' },
|
||||
{ resource: 'vms/domain', action: 'read:any', attributes: '*' },
|
||||
{ resource: 'vms/network', action: 'read:any', attributes: '*' },
|
||||
],
|
||||
};
|
||||
|
||||
export const user: Role = {
|
||||
extends: 'guest',
|
||||
permissions: [
|
||||
{ resource: 'apikey', action: 'read:own', attributes: '*' },
|
||||
{ resource: 'permission', action: 'read:any', attributes: '*' },
|
||||
],
|
||||
};
|
||||
|
||||
export const upc: Role = {
|
||||
extends: 'guest',
|
||||
permissions: [
|
||||
{ resource: 'apikey', action: 'read:own', attributes: '*' },
|
||||
{ resource: 'cloud', action: 'read:own', attributes: '*' },
|
||||
{ resource: 'config', action: 'read:any', attributes: '*' },
|
||||
{ resource: 'crash-reporting-enabled', action: 'read:any', attributes: '*' },
|
||||
{ resource: 'disk', action: 'read:any', attributes: '*' },
|
||||
{ resource: 'display', action: 'read:any', attributes: '*' },
|
||||
{ resource: 'flash', action: 'read:any', attributes: '*' },
|
||||
{ resource: 'os', action: 'read:any', attributes: '*' },
|
||||
{ resource: 'owner', action: 'read:any', attributes: '*' },
|
||||
{ resource: 'permission', action: 'read:any', attributes: '*' },
|
||||
{ resource: 'registration', action: 'read:any', attributes: '*' },
|
||||
{ resource: 'servers', action: 'read:any', attributes: '*' },
|
||||
{ resource: 'vars', action: 'read:any', attributes: '*' },
|
||||
{ resource: 'connect', action: 'read:own', attributes: '*' },
|
||||
{ resource: 'connect', action: 'update:own', attributes: '*' }
|
||||
],
|
||||
};
|
||||
|
||||
export const my_servers: Role = {
|
||||
extends: 'guest',
|
||||
permissions: [
|
||||
{ resource: 'dashboard', action: 'read:any', attributes: '*' },
|
||||
{ resource: 'two-factor', action: 'read:own', attributes: '*' },
|
||||
{ resource: 'array', action: 'read:any', attributes: '*' },
|
||||
{ resource: 'docker/container', action: 'read:any', attributes: '*' },
|
||||
{ resource: 'docker/network', action: 'read:any', attributes: '*' },
|
||||
{ resource: 'notifications', action: 'read:any', attributes: '*' },
|
||||
{ resource: 'vms/domain', action: 'read:any', attributes: '*' },
|
||||
{ resource: 'unraid-version', action: 'read:any', attributes: '*' },
|
||||
],
|
||||
};
|
||||
|
||||
export const notifier: Role = {
|
||||
extends: 'guest',
|
||||
permissions: [
|
||||
{ resource: 'notifications', action: 'create:own', attributes: '*' },
|
||||
],
|
||||
};
|
||||
|
||||
export const guest: Role = {
|
||||
permissions: [
|
||||
{ resource: 'me', action: 'read:any', attributes: '*' },
|
||||
{ resource: 'welcome', action: 'read:any', attributes: '*' },
|
||||
],
|
||||
};
|
||||
|
||||
export const permissions: Record<string, Role> = {
|
||||
guest,
|
||||
user,
|
||||
admin,
|
||||
upc,
|
||||
my_servers,
|
||||
notifier,
|
||||
};
|
||||
@@ -4,7 +4,7 @@ import { AppError } from '@app/core/errors/app-error';
|
||||
* API key error.
|
||||
*/
|
||||
export class ApiKeyError extends AppError {
|
||||
// eslint-disable-next-line @typescript-eslint/no-useless-constructor
|
||||
|
||||
constructor(message: string) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
export * as modules from '@app/core/modules';
|
||||
export * as notifiers from '@app/core/notifiers';
|
||||
export * as utils from '@app/core/utils';
|
||||
export * from '@app/core/bus';
|
||||
export * from '@app/core/log';
|
||||
export * from '@app/core/permission-manager';
|
||||
export * from '@app/core/permissions';
|
||||
|
||||
@@ -1,137 +1,83 @@
|
||||
import chalk from 'chalk';
|
||||
import { configure, getLogger } from 'log4js';
|
||||
import { serializeError } from 'serialize-error';
|
||||
import { pino } from 'pino';
|
||||
import { LOG_TYPE } from '@app/environment';
|
||||
|
||||
export const levels = ['ALL', 'TRACE', 'DEBUG', 'INFO', 'WARN', 'ERROR', 'FATAL', 'MARK', 'OFF'] as const;
|
||||
import pretty from 'pino-pretty';
|
||||
|
||||
const contextEnabled = Boolean(process.env.LOG_CONTEXT);
|
||||
const stackEnabled = Boolean(process.env.LOG_STACKTRACE);
|
||||
const tracingEnabled = Boolean(process.env.LOG_TRACING);
|
||||
const fullLoggingPattern = chalk`{gray [%d]} %x\{id\} %[[%p]%] %[[%c]%] %m{gray %x\{context\}}${tracingEnabled ? ' %[%f:%l%]' : ''}`;
|
||||
const minimumLoggingPattern = '%m';
|
||||
const appenders = process.env.LOG_TRANSPORT?.split(',').map(transport => transport.trim()) ?? ['out'];
|
||||
const level = levels[levels.indexOf(process.env.LOG_LEVEL?.toUpperCase() as typeof levels[number])] ?? 'INFO';
|
||||
const logLayout = {
|
||||
type: 'pattern',
|
||||
// Depending on what this env is set to we'll either get raw or pretty logs
|
||||
// The reason we do this is to allow the app to change this value
|
||||
// This way pretty logs can be turned off programmatically
|
||||
pattern: process.env.LOG_TYPE === 'pretty' ? fullLoggingPattern : minimumLoggingPattern,
|
||||
tokens: {
|
||||
id() {
|
||||
return chalk`{gray [${process.pid}]}`;
|
||||
},
|
||||
context({ context }: { context?: any }) {
|
||||
if (!contextEnabled || !context) {
|
||||
return '';
|
||||
}
|
||||
export const levels = [
|
||||
'trace',
|
||||
'debug',
|
||||
'info',
|
||||
'warn',
|
||||
'error',
|
||||
'fatal',
|
||||
] as const;
|
||||
|
||||
try {
|
||||
const contextEntries = Object.entries(context)
|
||||
.map(([key, value]) => [key, value instanceof Error ? (stackEnabled ? serializeError(value) : value) : value])
|
||||
.filter(([key]) => key !== 'pid');
|
||||
const cleanContext = Object.fromEntries(contextEntries);
|
||||
return `\n${Object.entries(cleanContext).map(([key, value]) => `${key}=${JSON.stringify(value, null, 2)}`).join(' ')}`;
|
||||
} catch (error: unknown) {
|
||||
const errorInfo = error instanceof Error ? `${error.message}: ${error.stack ?? 'no stack'}` : 'Error not instance of error';
|
||||
return `Error generating context: ${errorInfo}`;
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
const level =
|
||||
levels[
|
||||
levels.indexOf(
|
||||
process.env.LOG_LEVEL?.toLowerCase() as (typeof levels)[number]
|
||||
)
|
||||
] ?? 'info';
|
||||
|
||||
if (process.env.NODE_ENV !== 'test') {
|
||||
// We log to both the stdout and log file
|
||||
// The log file should be changed to errors only unless in debug mode
|
||||
configure({
|
||||
appenders: {
|
||||
file: {
|
||||
type: 'file',
|
||||
filename: '/var/log/unraid-api/stdout.log',
|
||||
maxLogSize: 10_000_000,
|
||||
backups: 0,
|
||||
layout: {
|
||||
...logLayout,
|
||||
// File logs should always be pretty
|
||||
pattern: fullLoggingPattern,
|
||||
},
|
||||
},
|
||||
errorFile: {
|
||||
type: 'file',
|
||||
filename: '/var/log/unraid-api/stderr.log',
|
||||
maxLogSize: 2_500_000,
|
||||
backups: 0,
|
||||
layout: {
|
||||
...logLayout,
|
||||
// File logs should always be pretty
|
||||
pattern: fullLoggingPattern,
|
||||
},
|
||||
},
|
||||
out: {
|
||||
type: 'stdout',
|
||||
layout: logLayout,
|
||||
},
|
||||
errors: { type: 'logLevelFilter', appender: 'errorFile', level: 'error' },
|
||||
},
|
||||
categories: {
|
||||
default: {
|
||||
appenders,
|
||||
level,
|
||||
enableCallStack: tracingEnabled,
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
export const logDestination = pino.destination({
|
||||
minLength: 1_024,
|
||||
sync: true,
|
||||
});
|
||||
|
||||
export const internalLogger = getLogger('internal');
|
||||
export const logger = getLogger('app');
|
||||
export const mothershipLogger = getLogger('mothership');
|
||||
export const dashboardLogger = getLogger('dashboard');
|
||||
export const emhttpLogger = getLogger('emhttp');
|
||||
export const libvirtLogger = getLogger('libvirt');
|
||||
export const graphqlLogger = getLogger('graphql');
|
||||
export const dockerLogger = getLogger('docker');
|
||||
export const cliLogger = getLogger('cli');
|
||||
export const minigraphLogger = getLogger('minigraph');
|
||||
export const cloudConnectorLogger = getLogger('cloud-connector');
|
||||
export const upnpLogger = getLogger('upnp');
|
||||
export const keyServerLogger = getLogger('key-server');
|
||||
export const remoteAccessLogger = getLogger('remote-access');
|
||||
export const remoteQueryLogger = getLogger('remote-query');
|
||||
const stream =
|
||||
LOG_TYPE === 'pretty'
|
||||
? pretty({
|
||||
singleLine: true,
|
||||
hideObject: false,
|
||||
colorize: true,
|
||||
ignore: 'time,hostname,pid',
|
||||
destination: logDestination,
|
||||
})
|
||||
: logDestination;
|
||||
|
||||
export const logger = pino(
|
||||
{
|
||||
level,
|
||||
timestamp: () => `,"time":"${new Date().toISOString()}"`,
|
||||
formatters: {
|
||||
level: (label: string) => ({ level: label }),
|
||||
},
|
||||
},
|
||||
stream
|
||||
);
|
||||
|
||||
export const internalLogger = logger.child({ logger: 'internal' });
|
||||
export const appLogger = logger.child({ logger: 'app' });
|
||||
export const mothershipLogger = logger.child({ logger: 'mothership' });
|
||||
export const dashboardLogger = logger.child({ logger: 'dashboard' });
|
||||
export const emhttpLogger = logger.child({ logger: 'emhttp' });
|
||||
export const libvirtLogger = logger.child({ logger: 'libvirt' });
|
||||
export const graphqlLogger = logger.child({ logger: 'graphql' });
|
||||
export const dockerLogger = logger.child({ logger: 'docker' });
|
||||
export const cliLogger = logger.child({ logger: 'cli' });
|
||||
export const minigraphLogger = logger.child({ logger: 'minigraph' });
|
||||
export const cloudConnectorLogger = logger.child({ logger: 'cloud-connector' });
|
||||
export const upnpLogger = logger.child({ logger: 'upnp' });
|
||||
export const keyServerLogger = logger.child({ logger: 'key-server' });
|
||||
export const remoteAccessLogger = logger.child({ logger: 'remote-access' });
|
||||
export const remoteQueryLogger = logger.child({ logger: 'remote-query' });
|
||||
export const apiLogger = logger.child({ logger: 'api' });
|
||||
|
||||
export const loggers = [
|
||||
logger,
|
||||
mothershipLogger,
|
||||
dashboardLogger,
|
||||
emhttpLogger,
|
||||
libvirtLogger,
|
||||
graphqlLogger,
|
||||
dockerLogger,
|
||||
cliLogger,
|
||||
minigraphLogger,
|
||||
cloudConnectorLogger,
|
||||
upnpLogger,
|
||||
keyServerLogger,
|
||||
remoteAccessLogger,
|
||||
remoteQueryLogger,
|
||||
internalLogger,
|
||||
appLogger,
|
||||
mothershipLogger,
|
||||
dashboardLogger,
|
||||
emhttpLogger,
|
||||
libvirtLogger,
|
||||
graphqlLogger,
|
||||
dockerLogger,
|
||||
cliLogger,
|
||||
minigraphLogger,
|
||||
cloudConnectorLogger,
|
||||
upnpLogger,
|
||||
keyServerLogger,
|
||||
remoteAccessLogger,
|
||||
remoteQueryLogger,
|
||||
apiLogger,
|
||||
];
|
||||
|
||||
// Send SIGUSR1 to increase log level
|
||||
process.on('SIGUSR1', () => {
|
||||
const level = typeof logger.level === 'string' ? logger.level : logger.level.levelStr;
|
||||
const nextLevel = levels[levels.findIndex(_level => _level === level) + 1] ?? levels[0];
|
||||
loggers.forEach(logger => {
|
||||
logger.level = nextLevel;
|
||||
});
|
||||
internalLogger.mark('Log level changed from %s to %s', level, nextLevel);
|
||||
});
|
||||
|
||||
// Send SIGUSR1 to decrease log level
|
||||
process.on('SIGUSR2', () => {
|
||||
const level = typeof logger.level === 'string' ? logger.level : logger.level.levelStr;
|
||||
const nextLevel = levels[levels.findIndex(_level => _level === level) - 1] ?? levels[levels.length - 1];
|
||||
loggers.forEach(logger => {
|
||||
logger.level = nextLevel;
|
||||
});
|
||||
internalLogger.mark('Log level changed from %s to %s', level, nextLevel);
|
||||
});
|
||||
|
||||
22
api/src/core/logrotate/setup-logrotate.ts
Normal file
22
api/src/core/logrotate/setup-logrotate.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import { writeFile } from 'fs/promises';
|
||||
|
||||
import { fileExists } from '@app/core/utils/files/file-exists';
|
||||
|
||||
export const setupLogRotation = async () => {
|
||||
if (await fileExists('/etc/logrotate.d/unraid-api')) {
|
||||
return;
|
||||
} else {
|
||||
await writeFile(
|
||||
'/etc/logrotate.d/unraid-api',
|
||||
`
|
||||
/var/log/unraid-api/*.log {
|
||||
rotate 1
|
||||
missingok
|
||||
size 5M
|
||||
su root root
|
||||
}
|
||||
`,
|
||||
{ mode: '644' }
|
||||
);
|
||||
}
|
||||
};
|
||||
@@ -1,126 +0,0 @@
|
||||
// import fs from 'fs';
|
||||
// import { log } from '../log';
|
||||
import type { CoreContext, CoreResult } from '@app/core/types';
|
||||
import { ensurePermission } from '@app/core/utils/permissions/ensure-permission';
|
||||
import { NotImplementedError } from '@app/core/errors/not-implemented-error';
|
||||
import { AppError } from '@app/core/errors/app-error';
|
||||
import { getters } from '@app/store';
|
||||
|
||||
interface Context extends CoreContext {
|
||||
data: {
|
||||
keyUri?: string;
|
||||
trial?: boolean;
|
||||
replacement?: boolean;
|
||||
email?: string;
|
||||
keyFile?: string;
|
||||
};
|
||||
}
|
||||
|
||||
interface Result extends CoreResult {
|
||||
json: {
|
||||
key?: string;
|
||||
type?: string;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a license key.
|
||||
*/
|
||||
export const addLicenseKey = async (context: Context): Promise<Result | void> => {
|
||||
ensurePermission(context.user, {
|
||||
resource: 'license-key',
|
||||
action: 'create',
|
||||
possession: 'any',
|
||||
});
|
||||
|
||||
// Const { data } = context;
|
||||
const emhttp = getters.emhttp();
|
||||
const guid = emhttp.var.regGuid;
|
||||
// Const timestamp = new Date();
|
||||
|
||||
if (!guid) {
|
||||
throw new AppError('guid missing');
|
||||
}
|
||||
|
||||
throw new NotImplementedError();
|
||||
|
||||
// // Connect to unraid.net to request a trial key
|
||||
// if (data?.trial) {
|
||||
// const body = new FormData();
|
||||
// body.append('guid', guid);
|
||||
// body.append('timestamp', timestamp.getTime().toString());
|
||||
|
||||
// const key = await got('https://keys.lime-technology.com/account/trial', { method: 'POST', body })
|
||||
// .then(response => JSON.parse(response.body))
|
||||
// .catch(error => {
|
||||
// log.error(error);
|
||||
// throw new AppError(`Sorry, a HTTP ${error.status} error occurred while registering USB Flash GUID ${guid}`);
|
||||
// });
|
||||
|
||||
// // Update the trial key file
|
||||
// await fs.promises.writeFile('/boot/config/Trial.key', Buffer.from(key, 'base64'));
|
||||
|
||||
// return {
|
||||
// text: 'Thank you for registering, your trial key has been accepted.',
|
||||
// json: {
|
||||
// key
|
||||
// }
|
||||
// };
|
||||
// }
|
||||
|
||||
// // Connect to unraid.net to request a new replacement key
|
||||
// if (data?.replacement) {
|
||||
// const { email, keyFile } = data;
|
||||
|
||||
// if (!email || !keyFile) {
|
||||
// throw new AppError('email or keyFile is missing');
|
||||
// }
|
||||
|
||||
// const body = new FormData();
|
||||
// body.append('guid', guid);
|
||||
// body.append('timestamp', timestamp.getTime().toString());
|
||||
// body.append('email', email);
|
||||
// body.append('keyfile', keyFile);
|
||||
|
||||
// const { body: key } = await got('https://keys.lime-technology.com/account/license/transfer', { method: 'POST', body })
|
||||
// .then(response => JSON.parse(response.body))
|
||||
// .catch(error => {
|
||||
// log.error(error);
|
||||
// throw new AppError(`Sorry, a HTTP ${error.status} error occurred while issuing a replacement for USB Flash GUID ${guid}`);
|
||||
// });
|
||||
|
||||
// // Update the trial key file
|
||||
// await fs.promises.writeFile('/boot/config/Trial.key', Buffer.from(key, 'base64'));
|
||||
|
||||
// return {
|
||||
// text: 'Thank you for registering, your trial key has been registered.',
|
||||
// json: {
|
||||
// key
|
||||
// }
|
||||
// };
|
||||
// }
|
||||
|
||||
// // Register a new server
|
||||
// if (data?.keyUri) {
|
||||
// const parts = data.keyUri.split('.key')[0].split('/');
|
||||
// const { [parts.length - 1]: keyType } = parts;
|
||||
|
||||
// // Download key blob
|
||||
// const { body: key } = await got(data.keyUri)
|
||||
// .then(response => JSON.parse(response.body))
|
||||
// .catch(error => {
|
||||
// log.error(error);
|
||||
// throw new AppError(`Sorry, a HTTP ${error.status} error occurred while registering your key for USB Flash GUID ${guid}`);
|
||||
// });
|
||||
|
||||
// // Save key file
|
||||
// await fs.promises.writeFile(`/boot/config/${keyType}.key`, Buffer.from(key, 'base64'));
|
||||
|
||||
// return {
|
||||
// text: `Thank you for registering, your ${keyType} key has been accepted.`,
|
||||
// json: {
|
||||
// type: keyType
|
||||
// }
|
||||
// };
|
||||
// }
|
||||
};
|
||||
@@ -1,11 +1,11 @@
|
||||
import type { CoreContext, CoreResult } from '@app/core/types';
|
||||
import { bus } from '@app/core/bus';
|
||||
import { AppError } from '@app/core/errors/app-error';
|
||||
import { ensurePermission } from '@app/core/utils/permissions/ensure-permission';
|
||||
import { hasFields } from '@app/core/utils/validation/has-fields';
|
||||
import { FieldMissingError } from '@app/core/errors/field-missing-error';
|
||||
import { emcmd } from '@app/core/utils/clients/emcmd';
|
||||
import { getters } from '@app/store';
|
||||
import { pubsub } from '@app/core/pubsub';
|
||||
|
||||
interface Context extends CoreContext {
|
||||
readonly data: {
|
||||
@@ -23,7 +23,6 @@ interface Context extends CoreContext {
|
||||
*/
|
||||
export const addUser = async (context: Context): Promise<CoreResult> => {
|
||||
const { data } = context;
|
||||
|
||||
// Check permissions
|
||||
ensurePermission(context.user, {
|
||||
resource: 'user',
|
||||
@@ -62,7 +61,7 @@ export const addUser = async (context: Context): Promise<CoreResult> => {
|
||||
}
|
||||
|
||||
// Update users channel with new user
|
||||
bus.emit('users', {
|
||||
pubsub.publish('users', {
|
||||
users: {
|
||||
mutation: 'CREATED',
|
||||
node: [user],
|
||||
@@ -70,7 +69,7 @@ export const addUser = async (context: Context): Promise<CoreResult> => {
|
||||
});
|
||||
|
||||
// Update user channel with new user
|
||||
bus.emit('user', {
|
||||
pubsub.publish('user', {
|
||||
user: {
|
||||
mutation: 'CREATED',
|
||||
node: user,
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { getServerIdentifier } from '@app/core/utils/server-identifier';
|
||||
import {
|
||||
ArrayDiskType,
|
||||
type ArrayCapacity,
|
||||
@@ -56,6 +57,7 @@ export const getArrayData = (getState = store.getState): ArrayType => {
|
||||
};
|
||||
|
||||
return {
|
||||
id: getServerIdentifier('array'),
|
||||
state: emhttp.var.mdState,
|
||||
capacity,
|
||||
boot,
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import camelCaseKeys from 'camelcase-keys';
|
||||
import { docker, ensurePermission } from '@app/core/utils';
|
||||
import { docker } from '@app/core/utils';
|
||||
import { type CoreContext, type CoreResult } from '@app/core/types';
|
||||
import { catchHandlers } from '@app/core/utils/misc/catch-handlers';
|
||||
import { ensurePermission } from '@app/core/utils/permissions/ensure-permission';
|
||||
|
||||
export const getDockerNetworks = async (context: CoreContext): Promise<CoreResult> => {
|
||||
const { user } = context;
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
import type { CoreResult, CoreContext } from '@app/core/types';
|
||||
import { ensurePermission } from '@app/core/utils/permissions/ensure-permission';
|
||||
import { getShares } from '@app/core/utils/shares/get-shares';
|
||||
|
||||
/**
|
||||
* Get all shares.
|
||||
*/
|
||||
export const getAllShares = async (context: CoreContext): Promise<CoreResult> => {
|
||||
const { user } = context;
|
||||
|
||||
// Check permissions
|
||||
ensurePermission(user, {
|
||||
resource: 'share',
|
||||
action: 'read',
|
||||
possession: 'any',
|
||||
});
|
||||
|
||||
const userShares = getShares('users');
|
||||
const diskShares = getShares('disks');
|
||||
|
||||
const shares = [
|
||||
...userShares,
|
||||
...diskShares,
|
||||
];
|
||||
|
||||
return {
|
||||
text: `Shares: ${JSON.stringify(shares, null, 2)}`,
|
||||
json: shares,
|
||||
};
|
||||
};
|
||||
@@ -1,25 +0,0 @@
|
||||
import { ensurePermission } from '@app/core/utils/permissions/ensure-permission';
|
||||
import { store } from '@app/store';
|
||||
import { type QueryResolvers } from '@app/graphql/generated/api/types';
|
||||
import { getArrayData } from '@app/core/modules/array/get-array-data';
|
||||
|
||||
/**
|
||||
* Get array info.
|
||||
* @returns Array state and array/disk capacity.
|
||||
*/
|
||||
export const getArray: QueryResolvers['array'] = (
|
||||
_,
|
||||
__,
|
||||
context
|
||||
) => {
|
||||
const { user } = context;
|
||||
|
||||
// Check permissions
|
||||
ensurePermission(user, {
|
||||
resource: 'array',
|
||||
action: 'read',
|
||||
possession: 'any',
|
||||
});
|
||||
|
||||
return getArrayData(store.getState);
|
||||
};
|
||||
@@ -4,15 +4,12 @@ import {
|
||||
blockDevices,
|
||||
diskLayout,
|
||||
} from 'systeminformation';
|
||||
import { map as asyncMap } from 'p-iteration';
|
||||
import { ensurePermission } from '@app/core/utils/permissions/ensure-permission';
|
||||
import { type Context } from '@app/graphql/schema/utils';
|
||||
import {
|
||||
type Disk,
|
||||
DiskInterfaceType,
|
||||
DiskSmartStatus,
|
||||
DiskFsType,
|
||||
} from '@app/graphql/generated/api/types';
|
||||
import { DiskFsType } from '@app/graphql/generated/api/types';
|
||||
import { graphqlLogger } from '@app/core/log';
|
||||
|
||||
const getTemperature = async (
|
||||
@@ -86,25 +83,16 @@ const parseDisk = async (
|
||||
* Get all disks.
|
||||
*/
|
||||
export const getDisks = async (
|
||||
context: Context,
|
||||
options?: { temperature: boolean }
|
||||
): Promise<Disk[]> => {
|
||||
const { user } = context;
|
||||
|
||||
// Check permissions
|
||||
ensurePermission(user, {
|
||||
resource: 'disk',
|
||||
action: 'read',
|
||||
possession: 'any',
|
||||
});
|
||||
|
||||
// Return all fields but temperature
|
||||
if (options?.temperature === false) {
|
||||
const partitions = await blockDevices().then((devices) =>
|
||||
devices.filter((device) => device.type === 'part')
|
||||
);
|
||||
const disks = await asyncMap(await diskLayout(), async (disk) =>
|
||||
parseDisk(disk, partitions)
|
||||
const diskLayoutData = await diskLayout();
|
||||
const disks = await Promise.all(
|
||||
diskLayoutData.map((disk) => parseDisk(disk, partitions))
|
||||
);
|
||||
|
||||
return disks;
|
||||
|
||||
@@ -30,7 +30,7 @@ export const getPermissions = async function (context: CoreContext): Promise<Cor
|
||||
const grants = Object.entries(ac.getGrants())
|
||||
.map(([name, grant]) => {
|
||||
// @ts-expect-error - $extend and grants are any
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
|
||||
const { $extend: _, ...grants } = grant;
|
||||
return [name, grants];
|
||||
})
|
||||
|
||||
@@ -4,27 +4,22 @@ import type { CoreResult, CoreContext } from '@app/core/types';
|
||||
import { getUnraidApiService } from '@app/core/modules/services/get-unraid-api';
|
||||
import { NODE_ENV } from '@app/environment';
|
||||
|
||||
const devNames = [
|
||||
'emhttpd',
|
||||
'rest-api',
|
||||
];
|
||||
const devNames = ['emhttpd', 'rest-api'];
|
||||
|
||||
const coreNames = [
|
||||
'unraid-api',
|
||||
];
|
||||
const coreNames = ['unraid-api'];
|
||||
|
||||
interface Service {
|
||||
online: boolean;
|
||||
uptime: string;
|
||||
version: string;
|
||||
online: boolean;
|
||||
uptime: string;
|
||||
version: string;
|
||||
}
|
||||
|
||||
interface ServiceResult extends CoreResult {
|
||||
json: Service;
|
||||
json: Service;
|
||||
}
|
||||
|
||||
interface ServiceWithName extends Service {
|
||||
name: string;
|
||||
name: string;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -33,39 +28,40 @@ interface ServiceWithName extends Service {
|
||||
* @param services
|
||||
* @param names
|
||||
*/
|
||||
const addNameToService = (services: ServiceResult[], names: string[]): ServiceWithName[] => services.map((service, index) => ({
|
||||
name: names[index],
|
||||
...service.json,
|
||||
}));
|
||||
const addNameToService = (services: ServiceResult[], names: string[]): ServiceWithName[] =>
|
||||
services.map((service, index) => ({
|
||||
name: names[index],
|
||||
...service.json,
|
||||
}));
|
||||
|
||||
interface Result extends CoreResult {
|
||||
json: ServiceWithName[];
|
||||
json: ServiceWithName[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all services.
|
||||
*/
|
||||
export const getServices = async (context: CoreContext): Promise<Result> => {
|
||||
const logErrorAndReturnEmptyArray = (error: Error) => {
|
||||
logger.error(error);
|
||||
return [];
|
||||
};
|
||||
const logErrorAndReturnEmptyArray = (error: Error) => {
|
||||
logger.error(error);
|
||||
return [];
|
||||
};
|
||||
|
||||
const devServices: ServiceResult[] = NODE_ENV === 'development' ? await Promise.all([
|
||||
getEmhttpdService(context),
|
||||
]).catch(logErrorAndReturnEmptyArray) as ServiceResult[] : [];
|
||||
const devServices: ServiceResult[] = (await Promise.all([getEmhttpdService(context)]).catch(
|
||||
logErrorAndReturnEmptyArray
|
||||
)) as ServiceResult[];
|
||||
|
||||
const coreServices: ServiceResult[] = await Promise.all([
|
||||
getUnraidApiService(context),
|
||||
]).catch(logErrorAndReturnEmptyArray) as ServiceResult[];
|
||||
const coreServices: ServiceResult[] = (await Promise.all([getUnraidApiService(context)]).catch(
|
||||
logErrorAndReturnEmptyArray
|
||||
)) as ServiceResult[];
|
||||
|
||||
const result = [
|
||||
...addNameToService(devServices, devNames),
|
||||
...addNameToService(coreServices, coreNames),
|
||||
];
|
||||
const result = [
|
||||
...addNameToService(devServices, devNames),
|
||||
...addNameToService(coreServices, coreNames),
|
||||
];
|
||||
|
||||
return {
|
||||
text: `Services: ${JSON.stringify(result, null, 2)}`,
|
||||
json: result,
|
||||
};
|
||||
return {
|
||||
text: `Services: ${JSON.stringify(result, null, 2)}`,
|
||||
json: result,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
import { AppError } from '@app/core/errors/app-error';
|
||||
import type { CoreResult, CoreContext } from '@app/core/types';
|
||||
import { ensurePermission } from '@app/core/utils/permissions/ensure-permission';
|
||||
|
||||
/**
|
||||
* Get all unassigned devices.
|
||||
*/
|
||||
export const getUnassignedDevices = async (context: CoreContext): Promise<CoreResult> => {
|
||||
const { user } = context;
|
||||
|
||||
// Bail if the user doesn't have permission
|
||||
ensurePermission(user, {
|
||||
resource: 'devices/unassigned',
|
||||
action: 'read',
|
||||
possession: 'any',
|
||||
});
|
||||
|
||||
const devices = [];
|
||||
|
||||
if (devices.length === 0) {
|
||||
throw new AppError('No devices found.', 404);
|
||||
}
|
||||
|
||||
return {
|
||||
text: `Unassigned devices: ${JSON.stringify(devices, null, 2)}`,
|
||||
json: devices,
|
||||
};
|
||||
};
|
||||
@@ -1,26 +0,0 @@
|
||||
import type { CoreContext, CoreResult } from '@app/core/types';
|
||||
import { ensurePermission } from '@app/core/utils/permissions/ensure-permission';
|
||||
import { getters } from '@app/store';
|
||||
|
||||
/**
|
||||
* Get all system vars.
|
||||
*/
|
||||
export const getVars = async (context: CoreContext): Promise<CoreResult> => {
|
||||
const { user } = context;
|
||||
|
||||
// Bail if the user doesn't have permission
|
||||
ensurePermission(user, {
|
||||
resource: 'vars',
|
||||
action: 'read',
|
||||
possession: 'any',
|
||||
});
|
||||
|
||||
const emhttp = getters.emhttp();
|
||||
|
||||
return {
|
||||
text: `Vars: ${JSON.stringify(emhttp.var, null, 2)}`,
|
||||
json: {
|
||||
...emhttp.var,
|
||||
},
|
||||
};
|
||||
};
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user